Your first agent
By the end you’ll have a codename agent Echo that picks the oldest open issue with a specific label, asks Claude for a one-line summary, posts it as an issue comment, and reports to Slack. Fires every 30 minutes via launchd, isolated in a per-firing git worktree, claiming the issue via the state machine before posting.
Condensed companion to docs/TUTORIAL.md. Full agent source at examples/bin/echo_summarise.py; copy-paste-ready.
Prerequisites
Section titled “Prerequisites”You’ve completed Install. bash bin/doctor.sh shows 0 passed, 0 failed. gh auth login and claude are authenticated.
1. Pick a target repo
Section titled “1. Pick a target repo”echo 'ECHO_REPO_SLUG=myorg/sandbox-repo' >> ~/.alfredrcexec $SHELL2. Create a test issue
Section titled “2. Create a test issue”gh label create agent:summarise --color "00ccff" \ --description "Echo will summarise this issue" \ -R "$ECHO_REPO_SLUG"
gh issue create -R "$ECHO_REPO_SLUG" \ --title "test issue for the Echo tutorial" \ --body "Echo should pick this up and post a one-line summary." \ --label "agent:summarise"3. Drop in the example agent
Section titled “3. Drop in the example agent”cp examples/bin/echo_summarise.py bin/echo.pychmod +x bin/echo.py4. Register in launchd/agents.conf
Section titled “4. Register in launchd/agents.conf”Append:
my.fleet.echo echo.py interval:1800 no5. Deploy + verify
Section titled “5. Deploy + verify”bash deploy.shbash bin/doctor.shDoctor should now report 1 passed, 0 failed (or N+1).
6. Force a firing
Section titled “6. Force a firing”Don’t wait 30 minutes:
launchctl kickstart -k "gui/$(id -u)/my.fleet.echo"tail -f /tmp/my.fleet.echo.std{out,err}Within ~10 seconds:
Echo summarised myorg/sandbox-repo#42: <one-line summary>Look at the issue on GitHub:
- A new comment from your gh user.
- The
agent:in-flightlabel briefly appeared, then was replaced withagent:done. - Three structured comments: claim, release, and the actual summary.
Check your configured fleet channel in Slack: the success message is there.
7. Confirm dedup actually works
Section titled “7. Confirm dedup actually works”Force a second firing immediately:
launchctl kickstart -k "gui/$(id -u)/my.fleet.echo"Output: [ECHO-IDLE] no agent:summarise issues. The first firing transitioned the issue to agent:done, which blocks future claims.
What you just learned
Section titled “What you just learned”Every framework primitive Echo uses scales up to a richer agent without changing shape:
with_lock(AGENT): host-level mutex prevents concurrent firings of the same codename.preflight(PREFLIGHT): fail loud and early on missing env / CLIs / auth.doctor_mode():bash bin/doctor.shdoesn’t burn turns or commit side effects.is_globally_blocked(): fleet-wide rate-limit poison pill.SpendState(AGENT): per-agent per-day spend tracking.claim_issue()/release_issue(): issue claim state machine.claude_invoke(): structuredclaude -pinvocation, parses turns/cost/session_id/result.gh_issue_comment(): gh CLI wrapper.slack_post(text, severity=): webhook post with severity routing.EventLog: per-firing JSONL audit log.
For richer agents (write code, open PRs, multi-step prompts, max-turns resume), see the shipped runners under bin/ and the examples in examples/bin/.
- Issue claim state machine: what
claim_issueactually does - Slack setup: wire your channel
- agent_runner API reference: every primitive available