| name | autoship-poll |
| description | GitHub issue sync for OpenCode — diffs live issue state against .autoship/state.json, integrates new issues, cancels agents for closed issues |
| platform | opencode |
| tools | ["Bash","Read","Write"] |
AutoShip Poll Protocol — OpenCode Port
You are the AutoShip polling safety net. You run every 10 minutes. Your job is to diff live GitHub issue state against .autoship/state.json and reconcile any drift.
Step 1: Load Known State
cat .autoship/state.json
Extract the issues map and repo field.
Step 2: Fetch Live Issues from GitHub
gh issue list --state open --json number,title,body,labels,updatedAt --limit 200
Also fetch recently closed issues:
gh issue list --state closed --json number,title,labels,updatedAt,closedAt --limit 50 \
| jq '[.[] | select(.closedAt > (now - 1800 | todate))]'
Step 3: Diff Against Known State
New Issues
Issues in live list not in state.json:
jq --argjson known "$(jq '[.issues | keys[] | ltrimstr("issue-") | tonumber]' .autoship/state.json)" \
'[.[] | .number | select(. as $n | $known | index($n) == null)]'
Closed Issues
Issues in state with state != merged/blocked but absent from live list.
Changed Issues
Issues where updatedAt is newer than stored timestamp.
Step 4: Handle New Issues
For each new issue:
4a. Classify Complexity
| Signal | Simple | Medium | Complex |
|---|
| Scope | Single file | 2-5 files | 5+ files |
| Test coverage | Existing tests | New test cases | New infrastructure |
| Dependencies | None | Touches shared | Cross-cutting |
Classify up when ambiguous.
4b. Check Dependencies
Scan for: blocks: #N, depends-on: #N, blocked by #N
4c. Add to Plan
jq --arg num "<number>" \
--arg title "<title>" \
--arg complexity "<simple|medium|complex>" \
'.issues[$num] = {
state: "unclaimed",
complexity: $complexity,
title: $title,
agent: null,
attempt: 0,
worktree: null,
discovered_by: "poll",
started_at: null,
attempts_history: []
}' .autoship/state.json > .autoship/state.tmp && mv .autoship/state.tmp .autoship/state.json
Step 5: Handle Closed Issues
For each externally closed issue:
5a. Check for Running Agent
agent=$(jq -r --arg num "<number>" '.issues[$num].agent // empty' .autoship/state.json)
5b. Update State
jq --arg num "<number>" \
'.issues[$num].state = "cancelled" |
.issues[$num].cancelled_reason = "closed-externally"' \
.autoship/state.json > .autoship/state.tmp && mv .autoship/state.tmp .autoship/state.json
5c. Clean Up Worktree
worktree=$(jq -r --arg num "<number>" '.issues[$num].worktree // empty' .autoship/state.json)
if [[ -n "$worktree" && -d "$worktree" ]]; then
git worktree remove "$worktree" --force
fi
Step 6: Handle Changed Issues
Label Reconciliation
If autoship:* labels added/removed externally, update state.
Metadata Update
jq --arg num "<number>" \
--arg title "<new_title>" \
'.issues[$num].title = $title |
.issues[$num].last_synced = now | todate' \
.autoship/state.json > .autoship/state.tmp && mv .autoship/state.tmp .autoship/state.json
Step 7: Refresh Quota
bash hooks/quota-update.sh refresh
Step 8: Update Timestamp and Log
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.updated_at = $ts | .last_poll = $ts' \
.autoship/state.json > .autoship/state.tmp && mv .autoship/state.tmp .autoship/state.json
Output
AutoShip Poll [<timestamp>]
New: N issues added to plan
Closed: N agents cancelled
Changed: N state updates
Or: AutoShip Poll [<timestamp>] — no changes detected