@linimin/pi-letscook 0.1.50 → 0.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ pi() {
6
+ env -u PI_COMPLETION_ROLE command pi --no-extensions "$@"
7
+ }
8
+ TMPDIR="$(mktemp -d)"
9
+ trap 'rm -rf "$TMPDIR"' EXIT
10
+
11
+ write_session() {
12
+ local session_path="$1"
13
+ local cwd="$2"
14
+ local text="$3"
15
+ python3 - "$session_path" "$cwd" "$text" <<'PY'
16
+ import json
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ session_path = Path(sys.argv[1])
21
+ cwd = sys.argv[2]
22
+ text = sys.argv[3]
23
+ session_path.parent.mkdir(parents=True, exist_ok=True)
24
+ entries = [
25
+ {
26
+ "type": "session",
27
+ "version": 3,
28
+ "id": "11111111-1111-4111-8111-111111111111",
29
+ "timestamp": "2026-01-01T00:00:00.000Z",
30
+ "cwd": cwd,
31
+ },
32
+ {
33
+ "type": "message",
34
+ "id": "a1b2c3d4",
35
+ "parentId": None,
36
+ "timestamp": "2026-01-01T00:00:01.000Z",
37
+ "message": {
38
+ "role": "user",
39
+ "content": text,
40
+ "timestamp": 1767225601000,
41
+ },
42
+ },
43
+ ]
44
+ with session_path.open('w', encoding='utf-8') as fh:
45
+ for entry in entries:
46
+ fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
47
+ PY
48
+ }
49
+
50
+ write_fallback_extension() {
51
+ local target="$1"
52
+ cat >"$target" <<'TS'
53
+ import { promises as fsp } from "node:fs";
54
+ import * as path from "node:path";
55
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
56
+
57
+ export default function (pi: ExtensionAPI) {
58
+ pi.on("input", async (event) => {
59
+ const targetPath = process.env.PI_COOK_TRIGGER_FALLBACK_PATH;
60
+ const allowedSource = process.env.PI_COOK_TRIGGER_FALLBACK_SOURCE ?? "interactive";
61
+ if (!targetPath) return { action: "continue" };
62
+ if (allowedSource !== "any" && event.source !== allowedSource) return { action: "continue" };
63
+ await fsp.mkdir(path.dirname(targetPath), { recursive: true });
64
+ await fsp.writeFile(
65
+ targetPath,
66
+ `${JSON.stringify({ text: event.text, source: event.source ?? null }, null, 2)}\n`,
67
+ "utf8",
68
+ );
69
+ return { action: "handled" };
70
+ });
71
+ }
72
+ TS
73
+ }
74
+
75
+ write_extension_sender_extension() {
76
+ local target="$1"
77
+ cat >"$target" <<'TS'
78
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
79
+
80
+ export default function (pi: ExtensionAPI) {
81
+ let sent = false;
82
+ pi.on("input", async (event) => {
83
+ if (sent || event.source !== "interactive" || event.text !== "__seed__") return { action: "continue" };
84
+ sent = true;
85
+ const text = process.env.PI_COOK_TRIGGER_EXTENSION_SOURCE_TEXT?.trim();
86
+ if (text) pi.sendUserMessage(text);
87
+ return { action: "handled" };
88
+ });
89
+ }
90
+ TS
91
+ }
92
+
93
+ FALLBACK_EXTENSION="$TMPDIR/fallback-handler.ts"
94
+ SENDER_EXTENSION="$TMPDIR/extension-sender.ts"
95
+ write_fallback_extension "$FALLBACK_EXTENSION"
96
+ write_extension_sender_extension "$SENDER_EXTENSION"
97
+
98
+ DISCUSSION=$'Mission: Route natural-language handoff into the shared /cook entry.\nScope:\n- Add an input hook before the primary agent starts implementation work.\n- Keep /cook as the canonical workflow boundary.\nConstraints:\n- Do not transform natural-language input into /cook.\nAcceptance:\n- Route execution handoff text into the shared /cook entry behind approval-only confirmation.'
99
+ MISSION='Route natural-language handoff into the shared /cook entry.'
100
+ ROUTE_CLASSIFIER_OUTPUT='{"intent":"route_to_cook","confidence":0.95,"reason":"The latest input is an execution handoff that should transfer control into /cook.","focusHint":"shared /cook entry handoff","evidence":["current input is a start-execution phrase","recent discussion already defines a concrete workflow mission"],"riskFlags":[]}'
101
+ NORMAL_CLASSIFIER_OUTPUT='{"intent":"normal_prompt","confidence":0.82,"reason":"The latest input is still asking the main agent to explain instead of handing control to /cook.","evidence":["the user is still asking for explanation in the main chat"],"riskFlags":["possible-normal-agent-request"]}'
102
+
103
+ # Assist-mode accepted routing should enter the shared /cook flow before the primary agent sees the handoff.
104
+ ROUTE_ROOT="$TMPDIR/route-repo"
105
+ ROUTE_SESSION="$TMPDIR/route-session.jsonl"
106
+ ROUTE_PROMPT="$TMPDIR/route-driver-prompt.txt"
107
+ ROUTE_ROUTING="$TMPDIR/route-routing.json"
108
+ ROUTE_CLASSIFIER="$TMPDIR/route-classifier.json"
109
+ ROUTE_FALLBACK="$TMPDIR/route-fallback.json"
110
+ mkdir -p "$ROUTE_ROOT"
111
+ cd "$ROUTE_ROOT"
112
+ git init -q
113
+ write_session "$ROUTE_SESSION" "$ROUTE_ROOT" "$DISCUSSION"
114
+
115
+ PI_COOK_TRIGGER_FALLBACK_PATH="$ROUTE_FALLBACK" \
116
+ PI_COOK_TRIGGER_FALLBACK_SOURCE=interactive \
117
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
118
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
119
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
120
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$ROUTE_PROMPT" \
121
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_OUTPUT="$ROUTE_CLASSIFIER_OUTPUT" \
122
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_SNAPSHOT_PATH="$ROUTE_CLASSIFIER" \
123
+ PI_COMPLETION_TEST_TRIGGER_CONFIRM_ACTION=start \
124
+ PI_COMPLETION_TEST_TRIGGER_ROUTING_PATH="$ROUTE_ROUTING" \
125
+ pi --session "$ROUTE_SESSION" -e "$PKG_ROOT" -e "$FALLBACK_EXTENSION" -p "開始做" \
126
+ >"$TMPDIR/pi-cook-trigger-route.out" 2>"$TMPDIR/pi-cook-trigger-route.err"
127
+
128
+ python3 - "$ROUTE_PROMPT" "$ROUTE_ROUTING" "$ROUTE_CLASSIFIER" "$ROUTE_FALLBACK" "$MISSION" "$TMPDIR/pi-cook-trigger-route.out" "$TMPDIR/pi-cook-trigger-route.err" <<'PY'
129
+ import json
130
+ import sys
131
+ from pathlib import Path
132
+
133
+ prompt = Path(sys.argv[1]).read_text()
134
+ routing = json.loads(Path(sys.argv[2]).read_text())
135
+ classifier = json.loads(Path(sys.argv[3]).read_text())
136
+ fallback = Path(sys.argv[4])
137
+ mission = sys.argv[5]
138
+ output = Path(sys.argv[6]).read_text() + Path(sys.argv[7]).read_text()
139
+ profile = json.loads(Path('.agent/profile.json').read_text())
140
+ state = json.loads(Path('.agent/state.json').read_text())
141
+ plan = json.loads(Path('.agent/plan.json').read_text())
142
+ active = json.loads(Path('.agent/active-slice.json').read_text())
143
+
144
+ assert routing['action'] == 'routed_to_cook', 'accepted handoff should route into the shared /cook entry'
145
+ assert routing['reason'] == 'accepted_takeover', 'accepted handoff should record the takeover reason'
146
+ assert routing['classificationIntent'] == 'route_to_cook', 'accepted handoff should snapshot the route_to_cook classifier result'
147
+ assert routing['focusHint'] == 'shared /cook entry handoff', 'accepted handoff should preserve the classifier focus hint'
148
+ assert classifier['result']['status'] == 'classified', 'accepted handoff should snapshot a classified trigger result'
149
+ assert classifier['result']['classification']['intent'] == 'route_to_cook', 'accepted handoff classifier snapshot should preserve route_to_cook intent'
150
+ assert not fallback.exists(), 'accepted handoff should keep the original interactive input away from later fallback handlers'
151
+ assert 'Start or continue the completion workflow for this repo.' in prompt, 'accepted handoff should queue the shared completion driver prompt'
152
+ assert 'Canonical routing profile:' in prompt, 'accepted handoff driver prompt should keep the canonical routing metadata'
153
+ assert state['mission_anchor'] == mission, 'accepted handoff should bootstrap canonical mission state through the shared /cook entry'
154
+ assert plan['mission_anchor'] == mission, 'accepted handoff should bootstrap plan.json through the shared /cook entry'
155
+ assert active['mission_anchor'] == mission, 'accepted handoff should bootstrap active-slice.json through the shared /cook entry'
156
+ assert profile['task_type'] == 'completion-workflow', 'accepted handoff should keep the canonical task_type'
157
+ assert 'Routing natural-language handoff into /cook.' in output, 'accepted handoff should notify that /cook took over'
158
+ PY
159
+
160
+ # Candidate natural-language prompts classified as normal prompts should continue to the main agent path.
161
+ NORMAL_ROOT="$TMPDIR/normal-repo"
162
+ NORMAL_SESSION="$TMPDIR/normal-session.jsonl"
163
+ NORMAL_ROUTING="$TMPDIR/normal-routing.json"
164
+ NORMAL_CLASSIFIER="$TMPDIR/normal-classifier.json"
165
+ NORMAL_FALLBACK="$TMPDIR/normal-fallback.json"
166
+ NORMAL_PROMPT="$TMPDIR/normal-driver-prompt.txt"
167
+ mkdir -p "$NORMAL_ROOT"
168
+ cd "$NORMAL_ROOT"
169
+ git init -q
170
+ write_session "$NORMAL_SESSION" "$NORMAL_ROOT" "$DISCUSSION"
171
+
172
+ PI_COOK_TRIGGER_FALLBACK_PATH="$NORMAL_FALLBACK" \
173
+ PI_COOK_TRIGGER_FALLBACK_SOURCE=interactive \
174
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$NORMAL_PROMPT" \
175
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_OUTPUT="$NORMAL_CLASSIFIER_OUTPUT" \
176
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_SNAPSHOT_PATH="$NORMAL_CLASSIFIER" \
177
+ PI_COMPLETION_TEST_TRIGGER_ROUTING_PATH="$NORMAL_ROUTING" \
178
+ pi --session "$NORMAL_SESSION" -e "$PKG_ROOT" -e "$FALLBACK_EXTENSION" -p "start by explaining the current repo state" \
179
+ >"$TMPDIR/pi-cook-trigger-normal.out" 2>"$TMPDIR/pi-cook-trigger-normal.err"
180
+
181
+ python3 - "$NORMAL_ROUTING" "$NORMAL_CLASSIFIER" "$NORMAL_FALLBACK" "$NORMAL_PROMPT" <<'PY'
182
+ import json
183
+ import sys
184
+ from pathlib import Path
185
+
186
+ routing = json.loads(Path(sys.argv[1]).read_text())
187
+ classifier = json.loads(Path(sys.argv[2]).read_text())
188
+ fallback = json.loads(Path(sys.argv[3]).read_text())
189
+ driver_prompt = Path(sys.argv[4])
190
+
191
+ assert routing['action'] == 'continue', 'normal prompts should pass through to the main agent path'
192
+ assert routing['reason'] == 'classifier_normal_prompt', 'normal prompts should record the classifier_normal_prompt routing reason'
193
+ assert routing['classificationIntent'] == 'normal_prompt', 'normal prompts should snapshot the normal_prompt classifier intent'
194
+ assert classifier['result']['status'] == 'classified', 'normal prompt pass-through should snapshot a classified trigger result'
195
+ assert classifier['result']['classification']['intent'] == 'normal_prompt', 'normal prompt pass-through should preserve the normal_prompt intent'
196
+ assert fallback['source'] == 'interactive', 'normal prompt pass-through should reach a later interactive fallback handler'
197
+ assert fallback['text'] == 'start by explaining the current repo state', 'normal prompt pass-through should preserve the original prompt text'
198
+ assert not Path('.agent').exists(), 'normal prompt pass-through should not bootstrap canonical workflow state'
199
+ assert not driver_prompt.exists(), 'normal prompt pass-through should not queue a /cook driver prompt'
200
+ PY
201
+
202
+ # Extension-originated turns should bypass natural-language routing and continue unchanged.
203
+ EXT_ROOT="$TMPDIR/extension-source-repo"
204
+ EXT_ROUTING="$TMPDIR/extension-source-routing.json"
205
+ EXT_FALLBACK="$TMPDIR/extension-source-fallback.json"
206
+ EXT_CLASSIFIER="$TMPDIR/extension-source-classifier.json"
207
+ EXT_PROMPT="$TMPDIR/extension-source-driver-prompt.txt"
208
+ mkdir -p "$EXT_ROOT"
209
+ cd "$EXT_ROOT"
210
+ git init -q
211
+
212
+ PI_COOK_TRIGGER_EXTENSION_SOURCE_TEXT="開始做" \
213
+ PI_COOK_TRIGGER_FALLBACK_PATH="$EXT_FALLBACK" \
214
+ PI_COOK_TRIGGER_FALLBACK_SOURCE=extension \
215
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$EXT_PROMPT" \
216
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_SNAPSHOT_PATH="$EXT_CLASSIFIER" \
217
+ PI_COMPLETION_TEST_TRIGGER_ROUTING_PATH="$EXT_ROUTING" \
218
+ pi -e "$PKG_ROOT" -e "$SENDER_EXTENSION" -e "$FALLBACK_EXTENSION" -p "__seed__" \
219
+ >"$TMPDIR/pi-cook-trigger-extension-source.out" 2>"$TMPDIR/pi-cook-trigger-extension-source.err"
220
+
221
+ python3 - "$EXT_ROUTING" "$EXT_FALLBACK" "$EXT_CLASSIFIER" "$EXT_PROMPT" <<'PY'
222
+ import json
223
+ import sys
224
+ from pathlib import Path
225
+
226
+ routing = json.loads(Path(sys.argv[1]).read_text())
227
+ fallback = json.loads(Path(sys.argv[2]).read_text())
228
+ classifier = Path(sys.argv[3])
229
+ driver_prompt = Path(sys.argv[4])
230
+
231
+ assert routing['action'] == 'continue', 'extension-originated turns should bypass natural-language routing'
232
+ assert routing['reason'] == 'extension_source', 'extension-originated turns should record the extension_source bypass reason'
233
+ assert fallback['source'] == 'extension', 'extension-originated turns should continue to later extension-source handlers'
234
+ assert fallback['text'] == '開始做', 'extension-originated turns should preserve the original extension text'
235
+ assert not classifier.exists(), 'extension-originated turns should bypass the trigger classifier entirely'
236
+ assert not driver_prompt.exists(), 'extension-originated turns should not queue a /cook driver prompt'
237
+ assert not Path('.agent').exists(), 'extension-originated turns should not bootstrap canonical workflow state'
238
+ PY
239
+
240
+ # Explicit /cook command entry should continue through the command path without the input hook interfering.
241
+ COOK_ROOT="$TMPDIR/explicit-cook-repo"
242
+ COOK_SESSION="$TMPDIR/explicit-cook-session.jsonl"
243
+ COOK_ROUTING="$TMPDIR/explicit-cook-routing.json"
244
+ COOK_PROMPT="$TMPDIR/explicit-cook-driver-prompt.txt"
245
+ mkdir -p "$COOK_ROOT"
246
+ cd "$COOK_ROOT"
247
+ git init -q
248
+ write_session "$COOK_SESSION" "$COOK_ROOT" "$DISCUSSION"
249
+
250
+ PI_COMPLETION_CONTEXT_PROPOSAL_ACTION=accept \
251
+ PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST=1 \
252
+ PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
253
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$COOK_PROMPT" \
254
+ PI_COMPLETION_TEST_TRIGGER_ROUTING_PATH="$COOK_ROUTING" \
255
+ pi --session "$COOK_SESSION" -e "$PKG_ROOT" -p "/cook" \
256
+ >"$TMPDIR/pi-cook-trigger-explicit-cook.out" 2>"$TMPDIR/pi-cook-trigger-explicit-cook.err"
257
+
258
+ python3 - "$COOK_PROMPT" "$COOK_ROUTING" "$MISSION" <<'PY'
259
+ import json
260
+ import sys
261
+ from pathlib import Path
262
+
263
+ prompt = Path(sys.argv[1]).read_text()
264
+ routing = Path(sys.argv[2])
265
+ mission = sys.argv[3]
266
+ state = json.loads(Path('.agent/state.json').read_text())
267
+
268
+ assert 'Start or continue the completion workflow for this repo.' in prompt, 'explicit /cook should keep queuing the shared completion driver prompt'
269
+ assert not routing.exists(), 'explicit /cook should bypass the natural-language input-routing snapshot entirely'
270
+ assert state['mission_anchor'] == mission, 'explicit /cook should keep the existing startup behavior'
271
+ PY
272
+
273
+ # Classifier timeout/failure should conservatively stop the original input from reaching the main agent.
274
+ TIMEOUT_ROOT="$TMPDIR/timeout-repo"
275
+ TIMEOUT_SESSION="$TMPDIR/timeout-session.jsonl"
276
+ TIMEOUT_ROUTING="$TMPDIR/timeout-routing.json"
277
+ TIMEOUT_CLASSIFIER="$TMPDIR/timeout-classifier.json"
278
+ TIMEOUT_FALLBACK="$TMPDIR/timeout-fallback.json"
279
+ TIMEOUT_PROMPT="$TMPDIR/timeout-driver-prompt.txt"
280
+ mkdir -p "$TIMEOUT_ROOT"
281
+ cd "$TIMEOUT_ROOT"
282
+ git init -q
283
+ write_session "$TIMEOUT_SESSION" "$TIMEOUT_ROOT" "$DISCUSSION"
284
+
285
+ PI_COOK_TRIGGER_FALLBACK_PATH="$TIMEOUT_FALLBACK" \
286
+ PI_COOK_TRIGGER_FALLBACK_SOURCE=interactive \
287
+ PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$TIMEOUT_PROMPT" \
288
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_FAILURE=timeout \
289
+ PI_COMPLETION_TEST_TRIGGER_CLASSIFIER_SNAPSHOT_PATH="$TIMEOUT_CLASSIFIER" \
290
+ PI_COMPLETION_TEST_TRIGGER_ROUTING_PATH="$TIMEOUT_ROUTING" \
291
+ pi --session "$TIMEOUT_SESSION" -e "$PKG_ROOT" -e "$FALLBACK_EXTENSION" -p "開始做" \
292
+ >"$TMPDIR/pi-cook-trigger-timeout.out" 2>"$TMPDIR/pi-cook-trigger-timeout.err"
293
+
294
+ python3 - "$TIMEOUT_ROUTING" "$TIMEOUT_CLASSIFIER" "$TIMEOUT_FALLBACK" "$TIMEOUT_PROMPT" "$TMPDIR/pi-cook-trigger-timeout.out" "$TMPDIR/pi-cook-trigger-timeout.err" <<'PY'
295
+ import json
296
+ import sys
297
+ from pathlib import Path
298
+
299
+ routing = json.loads(Path(sys.argv[1]).read_text())
300
+ classifier = json.loads(Path(sys.argv[2]).read_text())
301
+ fallback = Path(sys.argv[3])
302
+ driver_prompt = Path(sys.argv[4])
303
+ output = Path(sys.argv[5]).read_text() + Path(sys.argv[6]).read_text()
304
+
305
+ assert routing['action'] == 'handled', 'classifier timeout should conservatively handle the original input'
306
+ assert routing['reason'] == 'classifier_timeout', 'classifier timeout should record the conservative timeout reason'
307
+ assert classifier['result']['status'] == 'timeout', 'classifier timeout should snapshot the timeout result'
308
+ assert not fallback.exists(), 'classifier timeout should keep the original interactive input away from later fallback handlers'
309
+ assert not driver_prompt.exists(), 'classifier timeout should not queue a /cook driver prompt'
310
+ assert not Path('.agent').exists(), 'classifier timeout should not bootstrap canonical workflow state'
311
+ assert 'run /cook explicitly' in output, 'classifier timeout should guide the user toward explicit /cook handoff'
312
+ PY
313
+
314
+ echo "cook trigger routing test passed: $TMPDIR"
@@ -4,10 +4,10 @@ set -euo pipefail
4
4
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  cd "$ROOT"
6
6
 
7
- echo "[release-check] running control-plane validation, bare /cook parity, role-runner extraction, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
7
+ echo "[release-check] running control-plane validation, /cook public parity, assist-mode natural-language handoff coverage, role-runner extraction, startup/refocus/context regressions, canonical evidence artifact, active-slice contract, observability, legacy cleanup, evaluator calibration, and rubric contract coverage"
8
8
  bash .agent/verify_completion_control_plane.sh
9
9
 
10
- echo "[release-check] verifying public /cook parity"
10
+ echo "[release-check] verifying public /cook parity and assist-mode natural-language handoff docs/help"
11
11
  python3 - <<'PY'
12
12
  import re
13
13
  from pathlib import Path
@@ -15,6 +15,9 @@ from pathlib import Path
15
15
  checks = {
16
16
  "README.md": [
17
17
  "`/cook` supports both bare discussion-driven startup and optional inline intent hints.",
18
+ "assist-mode natural-language handoff that can offer to enter the same `/cook` flow before the primary agent starts implementation work",
19
+ "Execution handoff phrases like `開始做`, `開始實作`, or `go ahead` can also trigger an assist-mode confirmation asking whether `/cook` should take over.",
20
+ "Assist-mode natural-language handoff is optional. It only offers to enter the same `/cook` flow from recent discussion; explicit `/cook` remains the canonical workflow boundary and the fallback when the trigger is unclear or unavailable.",
18
21
  "`/cook <hint>` acts as a high-priority intent hint that helps proposal derivation interpret the recent discussion",
19
22
  "clarify the mission in the main chat before rerunning `/cook`",
20
23
  "Matching or unclear discussion resumes from canonical `.agent/**` state.",
@@ -25,17 +28,20 @@ checks = {
25
28
  "assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not",
26
29
  "Assistant/summary artifacts or plan/spec/design-doc/proposal-only context do not refocus the workflow.",
27
30
  "Optional `/cook <hint>` text biases that routing and candidate ranking toward the hinted implementation intent",
31
+ "bash ./scripts/cook-trigger-routing-test.sh",
28
32
  ],
29
33
  "CHANGELOG.md": [
34
+ "documented the shipped assist-mode natural-language handoff that can offer to route `開始做`, `開始實作`, or `go ahead` style execution handoffs into the canonical `/cook` flow",
35
+ "added `bash ./scripts/cook-trigger-routing-test.sh` to `npm run release-check` so packaged release parity now covers the natural-language takeover path",
30
36
  "restored optional `/cook <hint>` support as a soft intent hint that biases context analysis, proposal ranking, active-workflow disambiguation, and next-round startup without bypassing fail-closed routing or the approval-only Start/Cancel gate",
31
37
  "removed inline `/cook <text>` argument support so bare `/cook` is now the only supported workflow entrypoint",
32
38
  "historically allowed `/cook <hint>` as an analyst-only high-priority prompt",
33
39
  ],
34
40
  "extensions/completion/index.ts": [
35
- 'description: "/cook workflow: start, continue, refocus, or start the next round (optional hint supported)"',
41
+ 'description: "/cook workflow: start, continue, refocus, or start the next round; assist-mode natural-language handoff can offer the same /cook boundary"',
36
42
  'const COOK_BARE_ONLY_GUIDANCE =',
37
- '"/cook supports optional inline hints as high-priority intent cues, but mission selection still comes from recent discussion, repo truth, and the approval-only confirmation flow."',
38
- '"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
43
+ '"/cook remains the canonical workflow boundary. Assist-mode natural-language handoff can offer to enter the same /cook flow before implementation starts, while mission selection still comes from recent discussion, repo truth, and the approval-only confirmation flow."',
44
+ '"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Natural-language handoff only offers to enter the same /cook flow, so clarify the concrete repo changes in the main chat and rerun /cook."',
39
45
  ],
40
46
  }
41
47
 
@@ -58,6 +64,7 @@ for path, needles in forbidden.items():
58
64
  raise SystemExit(f"[release-check] found stale compatibility wording in {path}: {needle}")
59
65
  PY
60
66
 
67
+ bash ./scripts/cook-trigger-routing-test.sh
61
68
  npm run smoke-test
62
69
  npm run refocus-test
63
70
  npm run context-proposal-test