skills/done/SKILL.md
Complete work and push changes. Use when user types /done or says they are finished with the current task and ready to commit/push/create PR.
npx skillsauth add jamesc/skills doneInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When activated, execute this workflow to complete work and push:
Determine Issue ID per pick-issue step 1 (branch name → worktree name → ask user).
If branch starts with chore/, docs/, or refactor/ with no BT- prefix, treat as standalone — skip steps 10-11 (Linear updates) and omit issue ID from commit/PR.
Check branch: Verify we're NOT on main branch. If on main, stop and tell the user to create a feature branch first.
Stage changes:
git add -A
Check for changes: Run git status. If there's nothing to commit, inform the user and stop.
Run static checks (skip for doc/config-only changes):
If all changed files (staged + committed vs main) are docs/config (.md, .json, .yaml, .toml, etc.), skip CI.
Otherwise run: just build && just clippy && just fmt-check. Stop on failure.
Generate commit message: Based on the staged diff (git diff --cached), create a conventional commit message:
type: short description BT-{number} (include issue ID when available)feat, fix, docs, style, refactor, test, choreBT-{number} (e.g., chore: clean up dead code)Commit:
git commit -m "<generated message>"
Push:
git push -u origin HEAD
Create or update Pull Request: Check if a PR already exists for this branch:
gh pr list --head $(git branch --show-current) --json number,url --jq '.[0]'
If PR exists: Skip creation — the push in step 8 already updated it. Note the existing PR URL for reporting.
If no PR exists: Use the issue ID from step 1 (if available). Fetch the Linear issue title using the CLI:
streamlinear-cli get BT-{number}
Create the PR:
gh pr create --title "<Issue Title> (BT-{number})" --body "<Issue description with link to Linear issue>"
The PR body should include:
https://linear.app/beamtalk/issue/BT-{number} (if issue exists)For chore/docs/refactor branches without a Linear issue, use a descriptive title based on the commit message and omit the Linear link.
Update Linear acceptance criteria: Use the CLI to add a comment on the issue summarising which acceptance criteria were completed with checkmarks (✅):
streamlinear-cli comment BT-{number} "✅ Implemented: ..."
Update Linear state: Mark the Linear issue as "In Review":
streamlinear-cli update BT-{number} --state "In Review"
Bot review gate — never proceed past this step with unresolved Copilot / CodeRabbit findings.
Resolve PR, OWNER, REPO once up front so both subsections can use them:
PR=$(gh pr view --json number --jq .number)
OWNER=$(gh repo view --json owner --jq .owner.login)
REPO=$(gh repo view --json name --jq .name)
a. Wait for reviews to arrive (new PRs only):
For PRs created in step 9, poll every 60s for up to 10 minutes for copilot-pull-request-reviewer[bot] and coderabbitai[bot] reviews:
gh api "repos/${OWNER}/${REPO}/pulls/${PR}/reviews" \
--jq '[.[] | select(.user.login | test("copilot|coderabbit"; "i")) | {user: .user.login, state}]'
If both still missing after 10 min, note it in the report and continue to (b). Pre-existing PRs skip the wait.
b. Enumerate unresolved findings — both inline threads AND top-level review bodies:
AUTHOR=$(gh api "repos/${OWNER}/${REPO}/pulls/${PR}" --jq .user.login)
gh api graphql -f query="
{
repository(owner: \"${OWNER}\", name: \"${REPO}\") {
pullRequest(number: ${PR}) {
reviewThreads(first: 100) {
pageInfo { hasNextPage endCursor }
nodes {
id
isResolved
comments(first: 100) {
pageInfo { hasNextPage endCursor }
nodes { author { login } url body }
}
}
}
reviews(first: 100) {
pageInfo { hasNextPage endCursor }
nodes { author { login } state body url submittedAt }
}
}
}
}" --jq "
{
inline: [.data.repository.pullRequest.reviewThreads.nodes[]
| select(.comments.nodes | length > 0)
| select(.comments.nodes[0].author.login | test(\"copilot|coderabbit\"; \"i\"))
| select(.isResolved | not)
| select([.comments.nodes[].author.login] | index(\"${AUTHOR}\") | not)
| {url: .comments.nodes[0].url, body: (.comments.nodes[0].body[:200])}],
top_level: [.data.repository.pullRequest.reviews.nodes[]
| select(.author.login | test(\"copilot|coderabbit\"; \"i\"))
| select(.body != null and .body != \"\")
| select(.state != \"DISMISSED\")
| select((.state == \"CHANGES_REQUESTED\")
or (.body | test(\"Actionable comments posted: [1-9][0-9]*\")))
| {url, state, body: (.body[:200])}],
pagination: {
threads_has_next: .data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage,
threads_end_cursor: .data.repository.pullRequest.reviewThreads.pageInfo.endCursor,
reviews_has_next: .data.repository.pullRequest.reviews.pageInfo.hasNextPage,
reviews_end_cursor: .data.repository.pullRequest.reviews.pageInfo.endCursor,
thread_comments_has_next: [.data.repository.pullRequest.reviewThreads.nodes[]
| .comments.pageInfo.hasNextPage] | any,
thread_comment_cursors: [.data.repository.pullRequest.reviewThreads.nodes[]
| select(.comments.pageInfo.hasNextPage)
| select(.comments.nodes | length > 0)
| {thread_id: .id, thread_url: .comments.nodes[0].url, end_cursor: .comments.pageInfo.endCursor}]
}
}"
Pagination: If any pagination.*_has_next is true, re-issue the GraphQL query with after: "<endCursor>" on the relevant connection — use threads_end_cursor for reviewThreads, reviews_end_cursor for reviews, and the per-thread cursors in thread_comment_cursors for the inner comments connection — then merge the additional pages into the inline/top-level lists before deciding the gate. Skipping pagination would silently ignore findings on large PRs.
Concrete syntax — the cursor is an after: argument on the same connection field:
reviewThreads(first: 100, after: "<threads_end_cursor>") { ... }
reviews(first: 100, after: "<reviews_end_cursor>") { ... }
Per-thread comments pagination requires re-querying the specific thread by thread_id (now surfaced in thread_comment_cursors), then paging its comments(first: 100, after: "<end_cursor>") — for example via node(id: "<thread_id>") { ... on PullRequestReviewThread { comments(first: 100, after: "<end_cursor>") { ... } } }.
Dismissal heuristic:
isResolved: true (marked resolved in UI) OR the PR author (${AUTHOR}) has replied anywhere in the thread. Any reply counts — even "wontfix" or "out of scope".state == CHANGES_REQUESTED OR the body matches Actionable comments posted: [1-9][0-9]* (CodeRabbit's marker). Reviews with state == DISMISSED are always excluded — dismissing a CodeRabbit review keeps the "Actionable comments posted: N" text in its body, so without this filter dismissed reviews would re-trigger the gate forever. This also filters out Copilot's "Pull request overview" summaries and CodeRabbit's "Actionable comments posted: 0" runs.c. If any unresolved findings remain, HALT and prompt the user explicitly:
inline vs top_level./resolve-pr (handles enumerate → fix → reply → resolve threads → push). This is the recommended path.merge anyway (exact phrase) to proceed despite findings. Log the override in the final report.d. If zero unresolved findings, report ✅ and continue.
Report success: Confirm the commit was pushed, PR was created/updated (include PR URL), Linear was updated, and the bot review gate result (passed clean / passed after resolve / overridden by user with phrase). If overridden, include the count of skipped findings so the risk is visible in the report.
Note: These steps are manual — there is no automation to detect merge events yet.
done agent-state label to indicate completiontools
Find the next logical piece of work. Use when user types /whats-next or asks what they should work on next, or wants recommendations for the next task.
development
Use when navigating code, finding references, looking up definitions, understanding types, or tracing call hierarchies in TypeScript, Rust, or Beamtalk (.bt) files. Prefer LSP over Grep/Glob for any navigation task where symbol semantics matter.
data-ai
Find and update Linear issues that need labels, blocking relationships, or metadata. Use when user says '/update-issues' with criteria like 'no labels', 'missing agent-ready', 'needs size', etc.
data-ai
Sync modified skills and agents back to the repo and create a PR. Use when user types /sync-skills or wants to save in-session skill improvements.