Skip to content

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.

You’ve completed Install. bash bin/doctor.sh shows 0 passed, 0 failed. gh auth login and claude are authenticated.

Terminal window
echo 'ECHO_REPO_SLUG=myorg/sandbox-repo' >> ~/.alfredrc
exec $SHELL
Terminal window
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"
Terminal window
cp examples/bin/echo_summarise.py bin/echo.py
chmod +x bin/echo.py

Append:

my.fleet.echo echo.py interval:1800 no
Terminal window
bash deploy.sh
bash bin/doctor.sh

Doctor should now report 1 passed, 0 failed (or N+1).

Don’t wait 30 minutes:

Terminal window
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-flight label briefly appeared, then was replaced with agent:done.
  • Three structured comments: claim, release, and the actual summary.

Check your configured fleet channel in Slack: the success message is there.

Force a second firing immediately:

Terminal window
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.

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.sh doesn’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(): structured claude -p invocation, 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/.