Dispatch Watcher Skill — Gemini CLI Adapter
This is the Gemini CLI-specific adapter for the shared agent-presence
protocol. All shared logic (pre-flight, claim, lease, roster, takeover,
presence event format) lives in .agent/skills/agent-presence/SKILL.md and
scripts/agent-presence-helpers.sh — this file only contains Gemini-specific
glue.
When this Gemini CLI session starts (or when explicitly triggered by
@gemini #<num> in <DISPATCH_CHANNEL>), follow the workflow below.
Contract
- Input: environment variables
AGENT_ID(starts withgemini-),GH_TOKEN_GEMINI_BOT,SLACK_BOT_TOKEN,ROSTER_MESSAGE_TS,ROSTER_CHANNEL_ID,REPO. - Output: if a
@geminidispatch is pending, claim and execute it; otherwise return idle.startis emitted only AFTER pre-flight passes.endis emitted on session close via trap, with reason inEXIT_REASON. - Slack Connectivity: Gemini CLI uses
curl-based helpers inscripts/agent-presence-helpers.sh(which useSLACK_BOT_TOKEN) for both reading and writing to Slack. It does NOT use MCP tools.
Preconditions
Workspace name check (optional but recommended): When running multiple Gemini sessions in parallel, the
seqforAGENT_IDis conventionally derived from a worktree directory named*-Gemini-<N>. Skip this check if running a single-session setup.# Portable extraction (no GNU grep -P / -oP): sed prints nothing on no match, # so WS_SEQ is genuinely empty (the `-z` test below works). WS_SEQ=$(basename "$PWD" | sed -n 's/.*Gemini-\([0-9][0-9]*\).*/\1/p') if [ -z "$WS_SEQ" ]; then # Single-session setup — fall back to a default seq WS_SEQ=1 fi export AGENT_ID="gemini-${HOSTNAME:-host}-wsl-${WS_SEQ}"Dependency check: Ensure shared protocol files are present before proceeding:
if [ ! -f .agent/skills/agent-presence/SKILL.md ] || [ ! -f scripts/agent-presence-helpers.sh ]; then echo "Missing shared agent-presence dependencies (.agent/skills/agent-presence/SKILL.md or scripts/agent-presence-helpers.sh)" >&2 exit 1 fiTokens and repo:
GH_TOKEN_GEMINI_BOT,SLACK_BOT_TOKEN, andREPOmust be set.GH_TOKEN_BOTis resolved fromGH_TOKEN_GEMINI_BOT:export GH_TOKEN_BOT="$GH_TOKEN_GEMINI_BOT" export REPO="<OWNER>/<REPO>" source scripts/agent-presence-helpers.sh
Workflow
0. Wire shutdown wrapper
Use the trap-based wrapper to ensure end event emission:
EXIT_REASON="boot-incomplete"
emit_presence_end_once() {
local reason="${EXIT_REASON}"
trap - EXIT INT TERM HUP
emit_presence end "${reason}" || true
}
trap 'emit_presence_end_once' EXIT
trap 'EXIT_REASON="interrupted (${EXIT_REASON})"; emit_presence_end_once; exit 130' INT
trap 'EXIT_REASON="terminated (${EXIT_REASON})"; emit_presence_end_once; exit 143' TERM
trap 'EXIT_REASON="hangup (${EXIT_REASON})"; emit_presence_end_once; exit 129' HUP
1. Pre-flight check
Run pre_flight_check. If it fails, set
EXIT_REASON="declined (pre-flight conflict)" and exit 0.
2. Emit start event
emit_presence start
EXIT_REASON="post-start"
3. Read dispatch channel for @gemini mentions
scripts/agent-presence-helpers.sh has no public helper for reading
arbitrary channel history (it only exposes read_pinned_roster, which is
hard-wired to the single pinned roster message). To scan the dispatch
channel, call Slack's conversations.history directly with curl:
curl -sS "https://slack.com/api/conversations.history?channel=${DISPATCH_CHANNEL_ID}&limit=50" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
| jq -r '.messages[] | "\(.ts)\t\(.text)"'
Match each message text against:
(^|[[:space:]])@gemini\b\s*#?([0-9]+)\b
Filter out messages older than 1h (compare each .ts epoch to now - 3600).
4. Claim and execute
For a matched <issue_num>, call claim_issue "$issue_num" quick.
If successful, follow the implementation workflow:
git checkout -b feature/issue-<num>-<slug>- Implement and verify per the project's
Verification After Changeschecklist - Open PR with
Closes #<num>
5. Report completion
Emit the ready event, then record the worktree path on the roster. Note that
emit_presence takes only <event> [<context>] (Slack messaging); the
workspace_path lives on the roster, so set it via update_roster_chat_update:
emit_presence ready "PR #${PR_NUM} done"
update_roster_chat_update --event ready --workspace-path "$PWD"
Update EXIT_REASON="task-done (PR #${PR_NUM})".
Required env summary
export AGENT_ID="gemini-${HOSTNAME}-wsl-1"
export GH_TOKEN_GEMINI_BOT="ghp_..."
export SLACK_BOT_TOKEN="xoxb-..."
export REPO="<OWNER>/<REPO>"
export ROSTER_MESSAGE_TS="<ROSTER_MESSAGE_TS>"
export ROSTER_CHANNEL_ID="<DISPATCH_CHANNEL_ID>"
export GH_TOKEN_BOT="$GH_TOKEN_GEMINI_BOT"
source scripts/agent-presence-helpers.sh
Shared protocol references
.agent/skills/agent-presence/SKILL.md— shared protocol contractscripts/agent-presence-helpers.sh— shared shell library.agent/skills/dispatch-watcher-claude/SKILL.md— Claude adapter.agent/skills/dispatch-watcher/SKILL.md— Codex adapter