From annai
NEVER INVOKE THIS SKILL DIRECTLY; USE `/annai:review-pr` to review a GitHub pull request, or `/annai:review-local` to review code an agent just produced locally. This file is the shared base that the two leaf skills load — it owns the surface-authoring rules, launch + watch + react loop, schema-validation contract, and cleanup. It is not a standalone entry point.
How this skill is triggered — by the user, by Claude, or both
Slash command
/annai:reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This base skill is loaded by `review-pr` and `review-local`. It owns every step that is identical regardless of whether the change being reviewed is a GitHub PR or a local agent draft. The mode-specific steps (determine subject, scaffold, finish-line message, terminus) live in the leaf skill that invoked you — read it alongside this base and interleave: the leaf covers Step 1 (subject), Step 3a...
references/surface-example-local.jsonreferences/surface-example.jsonreferences/surface.schema.jsonscripts/annai.shscripts/app/package-lock.jsonscripts/app/package.jsonscripts/app/scripts/gen-schema.tsscripts/app/src/cli.tsscripts/app/src/cli/ipc-client.tsscripts/app/src/cli/output.tsscripts/app/src/cli/reply.tsscripts/app/src/cli/result.tsscripts/app/src/cli/sessions.tsscripts/app/src/cli/start.tsscripts/app/src/cli/status.tsscripts/app/src/cli/stop.tsscripts/app/src/cli/submit.tsscripts/app/src/cli/surface.tsscripts/app/src/cli/surface/annotation-add.tsscripts/app/src/cli/surface/annotation-drop.tsThis base skill is loaded by review-pr and review-local. It owns every step that is identical regardless of whether the change being reviewed is a GitHub PR or a local agent draft. The mode-specific steps (determine subject, scaffold, finish-line message, terminus) live in the leaf skill that invoked you — read it alongside this base and interleave: the leaf covers Step 1 (subject), Step 3a (scaffold), the finish-line message inside Step 5, and the terminus inside Step 7. Everything else below is shared.
This iteration adds draft comments + single-shot review finalization on top of v0.1's read-only surface:
Suggestion items.result.json, or Dismiss session to throw it away.review-submitted (or session-aborted on dismiss / browser close), then run the terminus the mode-specific leaf describes.Still stubbed: ask-agent threads (agent-asked event, annai.sh reply, inline agent replies in the browser). Don't promise the user they can ask the agent questions inline yet.
Before you start Step 1, call TaskCreate once per step in the procedure below (Step 1 through Step 8) using the step's title as the task subject — including the mode-specific ones the leaf owns (Step 1, 3a, 7). Mark each task in_progress when you start it and completed when it's done; don't batch. This is how the user sees your progress on the review — the spinner reflects which step you're on, and stale "pending" entries surface incomplete work after a crash or interruption.
It's fine to add tasks as you go for sub-work that emerges (e.g. "ask the user about ambiguous diff source", "re-validate after group reshuffle"), but the eight procedure steps should always exist as their own tasks from the start.
This step is mode-specific — see the leaf skill that invoked you (review-pr or review-local).
If the user supplied context (paths, URLs, free text) in their invocation, use it. Otherwise ask once, short:
What context should I use for this review? Paste links, paths, or describe where the relevant info lives. Reply 'none' to proceed with just the diff.
'none' is valid — Annai works with no context, just better with it. Common context sources: Katachi specs, Notion pages, design docs, transcripts, GitHub issue threads, the PR body itself (PR mode), or the agent's own working notes (local mode).
surface.jsonYou don't compose the surface by hand. annai.sh surface ... is a family of subcommands that build and edit it atomically — each one reads, validates, mutates, re-validates, and atomic-writes the file. That keeps hunks faithful to the change (parser-generated, not LLM-written) and lets you edit a 100-file scaffold without touching the JSON directly.
Reference: references/surface-example.json (PR shape) and references/surface-example-local.json (local-agent shape) show the final shape. You read one once to understand structure; you don't mimic it line by line.
This step is mode-specific — review-pr uses surface scaffold --pr ...; review-local uses surface scaffold-local --diff .... See the leaf skill that invoked you.
Pick a short session id (e.g. review-<n> or a 6-char nonce). The surface lives at ${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}/annai-$UID}/annai/sessions/<id>/surface.json.
--line-range on annotation-add / suggestion-add always refers to new-file line numbers — the newLine values from the scaffold's parsed hunks. To find a usable range, run annai.sh surface show --surface <p> --diff <id> --text and read the new-file column. Don't guess line numbers from the diff body; the parsed structure is the source of truth.
Don't edit hunks by hand — the scaffold has them right. Use the surface subcommand family for everything else. Free-form text (intro, annotation body, suggestion body, mermaid source) is passed via --*-file <path> so you can write multi-line markdown to a temp file with Write and reference it.
Every surface op:
annai: annotation "ann-1" added to "diff-foo" at L13–22 (note)).--json for a single-line JSON output, --quiet for none.--help for per-op usage.# Create the groups you've decided on. --before / --after control order.
annai.sh surface group-add --surface <p> --id models --kind base-context \
--title "Data model" --intro-file /tmp/intro-models.md --before unsorted
# Move diffs from `unsorted` into your groups.
annai.sh surface diff-move --surface <p> --diff diff-src-models-user-ts \
--to-group models
# Drop noise (lockfile churn, etc).
annai.sh surface diff-drop --surface <p> --diff diff-package-lock-json
# Annotate (highest volume). --kind ∈ pattern | note | question | surface-check | discrepancy.
annai.sh surface annotation-add --surface <p> \
--diff diff-src-handlers-create-ts \
--id ann-deferred-black-hole --kind question \
--title "Deferred branch is a black hole" \
--body-file /tmp/ann.md --line-range 142,158
# Inline suggestions (Accept-as-draft in the browser).
annai.sh surface suggestion-add --surface <p> --diff <id> --id sug-1 \
--body-file /tmp/sug.md --line-range 12,18 --code-file /tmp/sug-code.txt
# Mermaid diagrams. --group <id> for group-scoped; omit for surface-level.
# The mermaid source is parsed with the bundled renderer; pass --skip-validate
# to bypass when needed (rare).
annai.sh surface diagram-add --surface <p> --id flow \
--title "Submit flow" --source-file /tmp/flow.mmd --group entry
Drop-variants exist for every add: group-drop, diff-drop, annotation-drop, suggestion-drop, diagram-drop.
Update-variants exist too — use them to revise an item without dropping and re-adding (which loses position / ids): group-update, annotation-update, suggestion-update, diagram-update. Every field is optional; only what you pass is changed. Examples:
annai.sh surface group-update --surface <p> --id entry --title "Entry: webhook handlers"
annai.sh surface annotation-update --surface <p> \
--diff diff-src-handlers-create-ts --id ann-deferred-black-hole \
--body-file /tmp/ann-v2.md
annai.sh surface diagram-update --surface <p> --id flow \
--source-file /tmp/flow-v2.mmd
Run annai.sh surface with no args to print the full list.
tldr and reviewPrompts# tldr: short prose, file-backed for multi-line content.
annai.sh surface set-tldr --surface <p> --body-file /tmp/tldr.md
# reviewPrompts: one per line, blank lines ignored.
annai.sh surface set-review-prompts --surface <p> --file /tmp/prompts.txt
Leave both for last so you've already seen the whole diff. If you need to revise them later, just re-run the same setters.
surface show and surface validatesurface show is read-only and answers "what's in this surface right now?" Three modes:
# Overview: subject + tldr + per-group counts + surface-level diagrams + prompts.
annai.sh surface show --surface <p>
# Group details + per-diff counts.
annai.sh surface show --surface <p> --group <id>
# A diff's hunks rendered with new-file line numbers + its annotations
# + suggestions. This is how you find usable --line-range values.
annai.sh surface show --surface <p> --diff <id>
# Add --text for human-readable formatting; default output is JSON.
surface validate re-runs the zod schema against the file and prints a one-line summary on success:
annai.sh surface validate --surface <p> # base check
annai.sh surface validate --surface <p> --strict # also fail on non-empty unsorted,
# empty group intros, empty tldr
Every surface subcommand validates with zod before writing. So does annai.sh start. On failure, stderr is:
annai: surface validation failed
<json-path>: <message>
<json-path>: <message>
— one issue per line, paths like groups[0].diffs[1].annotations[0].kind. Read each line, fix the cited field with the appropriate mutator (e.g. an unknown enum value on groups[0].kind → drop and re-add the group, or use group-update --kind …), and re-run.
Hard constraints:
Edit.discrepancy annotation — surface it explicitly.kind is one of base-context, entry-point, supporting. Put base-context first only when load-bearing (new abstractions, data models). Then entry points (HTTP handlers, webhooks, background triggers, CLI commands — the points a user or system triggers, not where code is wired up). Then supporting code.pattern — name a recurring shape so the reviewer can match it once and stop re-reading.
'immediate' preserves existing behaviour — every existing row reads this value after the migration, which is what every call site expects when the column is absent."note — the why: purpose, constraint, hidden invariant, or non-obvious consequence. Never a description of what the code does.
question — something the reviewer should ask the author (or themselves) before approving. Not a rhetorical aside.
deferred branch returns 202 without enqueueing — who picks these up later? If nothing does, it's a black hole."surface-check — a verification target: "look here for X."
runTransition and enqueueTransition are newly exported — check whether anything actually consumes the export or it can drop."discrepancy — a doc-vs-code mismatch you can name. The doc source must be specified and the mismatch concrete.
docs/transition-scheduling.md) describes the body as { advance_at: ISO8601 }, but the handler reads req.body?.scheduled_for. One of them is wrong."suggestionCode when proposing a code change. Suggestions render inline with "Accept as draft" and "Dismiss" buttons; accepting promotes a suggestion into a real draft comment that goes out with the review.surface.diagrams; group-scoped diagrams go on the relevant group."${CLAUDE_PLUGIN_ROOT}/skills/review/scripts/annai.sh" start \
--surface "<path-to-surface.json>" \
--session "<session-id>" \
--repo "<local-repo-path>"
Bootstrap (npm install) runs on first invocation inside the script — don't pre-install. Capture {sessionId, url} from stdout.
Tell them the URL and how the flow ends. The finish-line message is mode-specific — see the leaf skill for the exact wording to use.
Then run annai.sh watch under Monitor so the agent reacts to events without polling:
"${CLAUDE_PLUGIN_ROOT}/skills/review/scripts/annai.sh" watch \
--session "<session-id>"
watch is quiet until the reviewer acts. The line-delimited events it emits to stdout are the ones the agent must handle (everything else — comment-drafted, comment-edited, etc. — stays browser-side and never wakes the agent).
review-submitted ({ decision, commentCount }) → go to the mode-specific terminus (Step 7).session-aborted ({ reason }) → report a clean cancellation to the user. reason: "dismissed-by-reviewer" means the reviewer hit Dismiss session; other reasons are browser-close / stop / signals. Don't run the terminus — there's nothing to finalize. The daemon has already exited; no stop needed.daemon-error ({ message, source }) → report the failure to the user. source: "client" means the browser surfaced a runtime error (window.onerror, unhandled rejection, or the React error boundary fired); the page may have rendered a fallback. source: "daemon" (or omitted) means a server-side fault. Run annai.sh status --session <id> for full context — the clientErrors[] array on the status JSON has the captured stack / componentStack for every client-side report.This step is mode-specific — see the leaf skill that invoked you. PR mode submits to GitHub via annai.sh submit; local-agent mode reads result.json via annai.sh result and translates the feedback into agent-actionable notes.
"${CLAUDE_PLUGIN_ROOT}/skills/review/scripts/annai.sh" stop \
--session "<session-id>"
Skip this if the watch event was session-aborted — the daemon already shut down.
references/surface.schema.json — the JSON schema for surface.json.references/surface-example.json — a minimal, concrete PR example you can mimic the shape of.references/surface-example-local.json — same shape for a local-agent subject.docs/code-review-surface.md (idea + shape) and docs/annai-architecture.md (full runtime design, including the interactive flows deferred past v0.1).Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub asermax/claude-plugins --plugin annai