mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
ce5afdf94a
Mirrors plan mode's agent-instructions feature in code review. Adds buildReviewAgentInstructions() — the clipboard contract teaching an external agent to read the diff via /api/diff and POST line/file/general scoped comments to /api/external-annotations (accurate against transformReviewInput). Wires a gated "Agent Instructions" item into the review header menu (shown only in a live session). Also drops the now-confusing cross-reference to code review from the plan instructions module.
138 lines
6.0 KiB
TypeScript
138 lines
6.0 KiB
TypeScript
/**
|
|
* Builds the clipboard payload that teaches an external agent (Claude Code,
|
|
* Codex, custom scripts, etc.) how to post annotations into a live Plannotator
|
|
* **plan-review** session via the /api/external-annotations HTTP API.
|
|
*
|
|
* The body is intentionally short (~110 lines of markdown) so an agent can read
|
|
* it top-to-bottom and start posting in 30 seconds. Edit freely — this file is
|
|
* the single source of truth for the agent-facing contract surface.
|
|
*
|
|
* The only dynamic value is `origin`, which is interpolated at click time from
|
|
* `window.location.origin` so the agent gets the correct base URL whether the
|
|
* server is running on a random local port or the fixed remote port (19432).
|
|
*/
|
|
export function buildPlanAgentInstructions(origin: string): string {
|
|
return `# Plannotator — External Annotations
|
|
|
|
You can submit review feedback on the user's current plan-review session by POSTing annotations to a small HTTP API. The user will see them immediately — inline highlights on the plan and entries in a sidebar — and can accept, edit, or delete them.
|
|
|
|
This is one-way submission. Any tool can post: linters, agents, scripts. The user does not see who you are unless you tell them via \`text\` or \`author\`.
|
|
|
|
## Base URL
|
|
${origin}
|
|
|
|
All endpoints below are relative to that base. No authentication.
|
|
|
|
## Workflow
|
|
1. Read the plan so you know what to comment on.
|
|
2. POST your annotations (single or batch).
|
|
3. Optionally clean up your previous annotations before reposting on a re-run.
|
|
|
|
There is no "send" or "done" step — each POST is live the moment it lands.
|
|
|
|
## Reading the plan
|
|
|
|
\`\`\`sh
|
|
curl -s ${origin}/api/plan | jq -r .plan
|
|
\`\`\`
|
|
|
|
**Line numbers do not apply and cannot be referenced.** The renderer pins your comments to the plan by matching the \`originalText\` field as a verbatim substring of the rendered text. Quote the exact phrase, never say "line 12."
|
|
|
|
## Two kinds of comment
|
|
|
|
You have exactly two shapes to choose from:
|
|
|
|
- **Inline comment** — pinned to a specific phrase in the plan. The matched phrase gets a yellow highlight in the rendered plan and the comment appears in the sidebar. Use this for feedback about a particular sentence, step, or block.
|
|
- **Global comment** — not tied to any phrase. Sidebar entry only. Use this for high-level feedback like "this plan is missing a rollback section" or "the ordering of steps 3 and 4 should be swapped."
|
|
|
|
## Posting an inline comment
|
|
|
|
\`\`\`sh
|
|
curl -s ${origin}/api/external-annotations \\
|
|
-H 'Content-Type: application/json' \\
|
|
-d '{
|
|
"source": "claude-code",
|
|
"type": "COMMENT",
|
|
"text": "This step needs error handling.",
|
|
"originalText": "open the file and parse it"
|
|
}'
|
|
\`\`\`
|
|
|
|
\`originalText\` must be a verbatim substring of the plan body. Pick something unique enough that it appears once — longer is safer than shorter. If the substring doesn't match anything in the rendered plan, the comment silently falls back to sidebar-only.
|
|
|
|
## Posting a global comment
|
|
|
|
\`\`\`sh
|
|
curl -s ${origin}/api/external-annotations \\
|
|
-H 'Content-Type: application/json' \\
|
|
-d '{
|
|
"source": "claude-code",
|
|
"type": "GLOBAL_COMMENT",
|
|
"text": "Missing a rollback section. Steps 3 and 4 should also be swapped."
|
|
}'
|
|
\`\`\`
|
|
|
|
Both endpoints return \`201 {"ids": ["<uuid>"]}\` on success, \`400 {"error": "..."}\` on validation failure.
|
|
|
|
### Fields
|
|
|
|
| Field | Required | Notes |
|
|
|---|---|---|
|
|
| \`source\` | yes | Stable identifier for *you* (e.g. \`"claude-code"\`, \`"codex"\`, \`"my-linter"\`). Reuse the same value for every annotation you post — it lets you clean up your own later. Pick something specific enough that it won't collide with other tools running against the same session. |
|
|
| \`text\` | yes | The comment body the user will read. |
|
|
| \`type\` | yes | \`"COMMENT"\` for inline, \`"GLOBAL_COMMENT"\` for sidebar-only. |
|
|
| \`originalText\` | for \`COMMENT\` | A verbatim substring of the plan body. Required when \`type\` is \`"COMMENT"\`. Omit for \`"GLOBAL_COMMENT"\`. |
|
|
| \`author\` | no | Human-readable label shown next to the comment (e.g. \`"Claude Opus"\`). |
|
|
|
|
## Batching
|
|
|
|
\`\`\`sh
|
|
curl -s ${origin}/api/external-annotations \\
|
|
-H 'Content-Type: application/json' \\
|
|
-d '{
|
|
"annotations": [
|
|
{"source": "claude-code", "type": "COMMENT", "text": "Missing error case.", "originalText": "open the file"},
|
|
{"source": "claude-code", "type": "COMMENT", "text": "This assumes the cache is warm — flag it.", "originalText": "look up the user in the cache"},
|
|
{"source": "claude-code", "type": "GLOBAL_COMMENT", "text": "Overall structure looks good. Add a rollback section."}
|
|
]
|
|
}'
|
|
\`\`\`
|
|
|
|
Batches are atomic: if any item fails validation, the whole batch is rejected with an error like \`annotations[2] missing required "text" field\`.
|
|
|
|
## Listing and deleting
|
|
|
|
\`\`\`sh
|
|
# List everything (yours and others')
|
|
curl -s ${origin}/api/external-annotations | jq
|
|
|
|
# Delete one annotation by id — works on any source, including the user's
|
|
curl -s -X DELETE "${origin}/api/external-annotations?id=<uuid>"
|
|
|
|
# Delete all annotations from one source — the standard cleanup before reposting
|
|
curl -s -X DELETE "${origin}/api/external-annotations?source=claude-code"
|
|
|
|
# Delete everything in the session
|
|
curl -s -X DELETE ${origin}/api/external-annotations
|
|
\`\`\`
|
|
|
|
You have full delete authority. Use it responsibly.
|
|
|
|
## Cleaning up on a re-run
|
|
|
|
If you re-run on the same session, your previous annotations are still there. POSTing again will create duplicates. Standard pattern:
|
|
|
|
\`\`\`sh
|
|
curl -s -X DELETE "${origin}/api/external-annotations?source=claude-code"
|
|
curl -s ${origin}/api/external-annotations -H 'Content-Type: application/json' -d '{ ...fresh annotations... }'
|
|
\`\`\`
|
|
|
|
This is why \`source\` matters. Pick a stable identifier and stick with it.
|
|
|
|
## Notes
|
|
- The plan can change underneath you. If the user denies and resubmits, refetch \`/api/plan\` — your prior \`originalText\` substrings may no longer match.
|
|
- No idempotency. Posting the same annotation twice creates two entries.
|
|
- This API is local to the user's machine. Treat it as a UI surface, not a public service.
|
|
`;
|
|
}
|