You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(annotate): add --silent-approve flag for naive hook compatibility
Hooks that treat any non-empty stdout as a block signal (spec-kit
PostToolUse pattern) need Approve to emit empty stdout. The default
--gate behavior emits "The user approved." so slash command templates
can distinguish approve from close in plaintext. --silent-approve
suppresses that marker, restoring silence-is-permission for hook
authors. No effect in --json mode.
Includes: shared parser update (FLAG_MAP refactor), 4 new tests,
docs for annotate, annotate-last, hook-integration, and gates guide.
For provenance purposes, this commit was AI assisted.
Copy file name to clipboardExpand all lines: apps/marketing/src/content/docs/commands/annotate-last.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,7 +75,7 @@ The annotation UI in `annotate-last` mode works the same as `/plannotator-annota
75
75
76
76
## Flags
77
77
78
-
`plannotator annotate-last` accepts the same `--gate`and `--json` flags as `plannotator annotate`. See [Annotate → Flags](/docs/commands/annotate/#flags) for the full matrix.
78
+
`plannotator annotate-last` accepts the same `--gate`, `--json`, and `--silent-approve` flags as `plannotator annotate`. See [Annotate → Flags](/docs/commands/annotate/#flags) for the full matrix.
79
79
80
80
The common use case for `--gate` on annotate-last is a turn-by-turn review gate wired to a Stop hook:
Copy file name to clipboardExpand all lines: apps/marketing/src/content/docs/commands/annotate.md
+10-3Lines changed: 10 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -103,7 +103,7 @@ All annotation types work identically: deletions, replacements, comments, insert
103
103
104
104
## Flags
105
105
106
-
Two opt-in flags turn annotate into a review gate for hook integrations (spec-driven frameworks, turn-by-turn review, and so on). They are orthogonal: you can use either alone or combine them.
106
+
Three opt-in flags turn annotate into a review gate for hook integrations (spec-driven frameworks, turn-by-turn review, and so on). They compose: use any alone or combine them.
107
107
108
108
### `--gate`
109
109
@@ -123,18 +123,25 @@ Switches stdout to a structured decision object so hooks can route programmatica
123
123
124
124
`feedback` is only present when `decision === "annotated"`.
125
125
126
+
### `--silent-approve`
127
+
128
+
Suppresses the plaintext approve marker so Approve emits empty stdout instead of `The user approved.`. Use this with naive hooks that treat any non-empty stdout as a block signal. Approve and Close both become silent, and only Send Annotations blocks with feedback (silence-is-permission).
129
+
130
+
`--silent-approve` only affects plaintext mode. In `--json` mode, Approve continues to emit `{"decision":"approved"}` — JSON callers route on the `decision` field, so there's no ambiguity to silence.
**Key property:**`--gate` plaintext output is unambiguous across all three decisions. Close is empty, Send Annotations is feedback markdown, Approve is the exact line `The user approved.` — each case distinguishable without JSON parsing. Use `--json` when you want machine-readable decision objects instead of string matching.
142
+
**Key property:**`--gate` plaintext output is unambiguous across three decisions — Close is empty, Send Annotations is feedback markdown, Approve is the line `The user approved.`. Drop the marker with `--silent-approve` when your hook treats any stdout as a block. Use `--json` when you want machine-readable decision objects instead of string matching.
136
143
137
-
On OpenCode and Pi, `--json`is silently accepted because those harnesses write back into the session directly rather than via stdout. The `--gate` flag behaves identically across all three harnesses.
144
+
On OpenCode and Pi, `--json`and `--silent-approve` are silently accepted because those harnesses write back into the session directly rather than via stdout. The `--gate` flag behaves identically across all three harnesses.
138
145
139
146
See [Hook integration recipes](/docs/guides/hook-integration/) for ready-to-use PostToolUse and Stop hook examples.
Copy file name to clipboardExpand all lines: apps/marketing/src/content/docs/guides/annotate-gates-and-json-responses.md
+24-10Lines changed: 24 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,29 +1,31 @@
1
1
---
2
2
title: "Annotate Gates and JSON Responses"
3
-
description: "The --gateand --json flags extend plannotator annotate from a feedback tool into a structured review gate with machine-readable decisions. Use them to wire Plannotator into spec-driven workflows, Stop hooks, and agent pipelines."
3
+
description: "The --gate, --json, and --silent-approve flags extend plannotator annotate from a feedback tool into a structured review gate with machine-readable decisions. Use them to wire Plannotator into spec-driven workflows, Stop hooks, and agent pipelines."
4
4
sidebar:
5
5
order: 28
6
6
section: "Guides"
7
7
---
8
8
9
-
`plannotator annotate` and `plannotator annotate-last` accept two flags that turn markdown annotation into a full review gate with structured output.
9
+
`plannotator annotate` and `plannotator annotate-last` accept three flags that turn markdown annotation into a full review gate with structured output.
10
10
11
11
## Capabilities
12
12
13
13
-**`--gate`** adds an Approve button to the annotation UI. The reviewer picks one of three decisions: approve, send annotations, or close.
14
14
-**`--json`** emits every decision as a structured JSON object on stdout so hooks and plugins can route on the outcome without parsing free text.
15
-
- The flags compose. Use them together, separately, or not at all.
15
+
-**`--silent-approve`** suppresses the plaintext approve marker so Approve emits empty stdout. Naive hooks that treat any stdout as a block signal opt in here to keep silence-is-permission intact.
16
+
- The flags compose. Use any alone or together.
16
17
- Identical semantics across every supported harness: Claude Code, Copilot CLI, Gemini CLI, OpenCode, Pi, and Codex.
@@ -68,7 +70,7 @@ A three-way review decision. The annotation UI adds an Approve button alongside
68
70
-**Send Annotations.** The reviewer has specific changes. The feedback is returned verbatim.
69
71
-**Close.** The session ends without a decision. Neither a signal to the agent nor an instruction set.
70
72
71
-
In plaintext mode, Approve emits the single line `The user approved.` on stdout so templates and agents can distinguish approval from close without needing `--json`. Close emits nothing. Send Annotations emits the feedback markdown. Hook authors who treat any non-empty stdout as a block signal need to filter the approve marker (or use `--json` for cleaner routing).
73
+
In plaintext mode, Approve emits the single line `The user approved.` on stdout so templates and agents can distinguish approval from close without needing `--json`. Close emits nothing. Send Annotations emits the feedback markdown. Hook authors who treat any non-empty stdout as a block signal can add `--silent-approve`to suppress the marker, or use `--json` for structured routing.
72
74
73
75
## `--json`
74
76
@@ -80,6 +82,18 @@ Structured stdout. Every decision is emitted as a JSON object with a `decision`
80
82
-`--gate --json` unlocks all three decisions in structured form.
81
83
- On OpenCode and Pi, `--json` is accepted silently. Those harnesses write back to the session directly rather than via stdout, so the flag has no effect there. Recipes remain portable.
82
84
85
+
## `--silent-approve`
86
+
87
+
Suppresses the plaintext approve marker. With `--gate --silent-approve`, Approve emits empty stdout (instead of `The user approved.`), matching Close. Send Annotations still emits feedback.
88
+
89
+
This is the shape naive hooks want — the ones that treat any non-empty stdout as a block signal:
90
+
91
+
- Approve → empty → hook passes → agent proceeds.
92
+
- Close → empty → hook passes → agent proceeds.
93
+
- Send Annotations → feedback → hook blocks with that feedback as the reason.
94
+
95
+
`--silent-approve` only affects plaintext mode. In `--json` mode, Approve still emits `{"decision":"approved"}` — JSON callers route on the `decision` field, so there's no ambiguity to silence. The flag is accepted silently on OpenCode and Pi for the same reason `--json` is: those harnesses don't use stdout as the signal channel.
Copy file name to clipboardExpand all lines: apps/marketing/src/content/docs/guides/hook-integration.md
+22-5Lines changed: 22 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,13 +6,14 @@ sidebar:
6
6
section: "Guides"
7
7
---
8
8
9
-
The `--gate`and `--json` flags on `plannotator annotate` and `plannotator annotate-last` turn annotation into a structured review decision. This guide shows how to wire them into agent hooks so a human can gate the agent at specific points in a workflow.
9
+
The `--gate`, `--json`, and `--silent-approve` flags on `plannotator annotate` and `plannotator annotate-last` turn annotation into a structured review decision. This guide shows how to wire them into agent hooks so a human can gate the agent at specific points in a workflow.
10
10
11
11
See [Annotate → Flags](/docs/commands/annotate/#flags) for the full stdout matrix. The short version:
- Without `--json`: Approve emits the line `The user approved.`, Close emits empty stdout, Send Annotations emits the feedback markdown. Three distinguishable outputs without parsing JSON.
15
-
- With `--json`: every decision emits a structured `{ "decision": "approved" | "annotated" | "dismissed", "feedback": "..." }` object.
14
+
- Plaintext default: Approve emits the line `The user approved.`, Close emits empty stdout, Send Annotations emits the feedback markdown. Three distinguishable outputs without parsing JSON.
15
+
-`--silent-approve` collapses Approve to empty stdout, matching Close. Use this with naive "any stdout = block" hooks so silence means permission.
16
+
-`--json` emits every decision as a structured `{ "decision": "approved" | "annotated" | "dismissed", "feedback": "..." }` object.
16
17
17
18
## Recipe 1: PostToolUse spec gate
18
19
@@ -47,7 +48,21 @@ Behavior:
47
48
-**Send Annotations** → feedback markdown on stdout. Claude Code reports the feedback back.
48
49
-**Close** → empty stdout. Claude Code proceeds silently.
49
50
50
-
If your hook treats any non-empty stdout as a block signal (some scripts do), filter the approve marker explicitly, or use the `--json` recipe below to route on the parsed decision instead.
51
+
### Silence-is-permission (`--silent-approve`)
52
+
53
+
If your hook treats any non-empty stdout as a block signal (spec-kit and similar naive PostToolUse hooks), add `--silent-approve` so Approve also emits empty stdout:
-**Send Annotations** → feedback on stdout → hook blocks with feedback as the reason.
64
+
65
+
Approve and Close collapse into the same "silent = allow" cell, which is what this class of hook expects. Only Send Annotations carries content the agent needs to react to.
51
66
52
67
### Structured (`--json`)
53
68
@@ -104,6 +119,8 @@ Behavior:
104
119
-**Send Annotations** → feedback on stdout → Claude Code re-prompts with the feedback.
105
120
-**Close** → empty stdout → turn ends.
106
121
122
+
Add `--silent-approve` if your Stop hook treats any stdout as a re-prompt trigger — Approve then emits empty stdout too, so only Send Annotations re-fires the turn with feedback.
123
+
107
124
### Structured
108
125
109
126
Same pattern as the PostToolUse recipe — pipe `--gate --json` through a shell wrapper if you want distinct handling per decision.
@@ -116,7 +133,7 @@ The same `--gate` flag works in OpenCode's `/plannotator-annotate` and Pi's `/pl
116
133
/plannotator-annotate spec.md --gate
117
134
```
118
135
119
-
On those harnesses there is no stdout channel back to the agent — the plugin writes back via `session.prompt` (OpenCode) or `sendUserMessage` (Pi). Approve and Close both result in no session injection; Send Annotations injects the feedback. `--json`is accepted silently on these harnesses so recipes stay portable.
136
+
On those harnesses there is no stdout channel back to the agent — the plugin writes back via `session.prompt` (OpenCode) or `sendUserMessage` (Pi). Approve and Close both result in no session injection; Send Annotations injects the feedback. `--json`and `--silent-approve` are accepted silently on these harnesses so recipes stay portable.
120
137
121
138
Third-party Pi or OpenCode plugins that want explicit decision routing can read `approved` directly from the server's decision object:
0 commit comments