@linimin/pi-letscook 0.1.55 → 0.1.56

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  - made `/cook` derive a confirmable startup brief from recent discussion before any canonical workflow rewrite, then preserve the confirmed brief in canonical state as advisory intake for later re-grounding
8
8
  - removed inline `/cook` arguments from the shipped entry path again so explicit bare `/cook` is the only public command, and fail closed when recent discussion is insufficient or unreliable
9
+ - added a pre-`/cook` ordinary-chat handoff boundary so the primary agent is instructed to stop at `/cook` once a task has matured into completion-workflow scope instead of starting long-running implementation directly in ordinary chat
9
10
 
10
11
  ## 0.1.54
11
12
 
package/README.md CHANGED
@@ -62,10 +62,13 @@ If you pass inline arguments to `/cook`, it also fails closed and tells you to m
62
62
 
63
63
  Only explicit `/cook` enters the workflow. Ordinary prompts stay in the main chat and go straight to the primary agent.
64
64
 
65
+ If a task has clearly matured into completion-workflow scope, the primary agent should hand you off to `/cook` instead of starting long-running implementation directly in ordinary chat.
66
+
65
67
  Important behavior:
66
68
  - `/cook` is the canonical workflow boundary and manual entry point
67
69
  - startup, refocus, and next-round routing stay confirm-first; nothing silently starts a workflow
68
70
  - explicit slash commands other than `/cook` continue normally in the main chat
71
+ - ordinary main-chat discussion may clarify or propose, but mature long-running implementation should be handed off to `/cook`
69
72
 
70
73
  ## Typical examples
71
74
 
@@ -38,6 +38,7 @@ import {
38
38
  buildContextProposalConfirmationLayout as buildExtractedContextProposalConfirmationLayout,
39
39
  buildContextProposalConfirmationSelectItems,
40
40
  buildContextProposalContinuationReason as buildExtractedContextProposalContinuationReason,
41
+ buildCookHandoffBoundaryReminder as buildExtractedCookHandoffBoundaryReminder,
41
42
  buildEvaluationRoleContextLines as buildExtractedEvaluationRoleContextLines,
42
43
  buildEvaluationRoleReminderText as buildExtractedEvaluationRoleReminderText,
43
44
  buildResumeCapsule as buildExtractedResumeCapsule,
@@ -181,6 +182,10 @@ function completionTestSystemReminderPath(): string | undefined {
181
182
  return asString(process.env.PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH);
182
183
  }
183
184
 
185
+ function completionTestCookHandoffReminderPath(): string | undefined {
186
+ return asString(process.env.PI_COMPLETION_TEST_COOK_HANDOFF_REMINDER_PATH);
187
+ }
188
+
184
189
  function maybeWriteTestSnapshot(targetPath: string | undefined, content: string): void {
185
190
  if (!targetPath) return;
186
191
  try {
@@ -225,6 +230,19 @@ function shouldInjectCompletionWorkflowContext(snapshot: CompletionStateSnapshot
225
230
  return hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx);
226
231
  }
227
232
 
233
+ function shouldInjectCookHandoffBoundary(event: { prompt?: string }, ctx: { sessionManager?: any }): boolean {
234
+ if (roleFromEnv()) return false;
235
+ if (isCompletionDriverPromptTurn(ctx)) return false;
236
+ const prompt = typeof event.prompt === "string" ? event.prompt.trim() : "";
237
+ if (!prompt) return false;
238
+ if (prompt.startsWith("/")) return false;
239
+ return true;
240
+ }
241
+
242
+ function buildCookHandoffBoundaryReminder(): string {
243
+ return buildExtractedCookHandoffBoundaryReminder();
244
+ }
245
+
228
246
  function buildDoneWorkflowBoundaryReminder(snapshot: CompletionStateSnapshot): string {
229
247
  const missionAnchor = asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? "(unknown)";
230
248
  const continuationReason = asString(snapshot.state?.continuation_reason) ?? "(unknown)";
@@ -926,7 +944,7 @@ export default function completionExtension(pi: ExtensionAPI) {
926
944
  }
927
945
  });
928
946
 
929
- pi.on("before_agent_start", async (_event, ctx) => {
947
+ pi.on("before_agent_start", async (event, ctx) => {
930
948
  const loaded = await loadCompletionDataForReminder(getCtxCwd(ctx));
931
949
  const driverPromptTurn = isCompletionDriverPromptTurn(ctx);
932
950
  if (loaded && driverPromptTurn) {
@@ -934,28 +952,35 @@ export default function completionExtension(pi: ExtensionAPI) {
934
952
  const fingerprint = completionContinuationFingerprint(loaded.snapshot);
935
953
  if (fingerprint) markQueuedDriverPromptInFlight(rootKey, fingerprint);
936
954
  }
937
- if (!loaded || !shouldInjectCompletionWorkflowContext(loaded.snapshot, ctx)) return;
938
- const additions = isWorkflowDone(loaded.snapshot)
939
- ? [buildDoneWorkflowBoundaryReminder(loaded.snapshot)]
940
- : [composeSystemReminder(loaded.snapshot, loaded.sliceHistory, loaded.stopHistory)];
941
- if (!isWorkflowDone(loaded.snapshot)) {
942
- const markerText = await readText(loaded.snapshot.files.compactionMarkerPath);
943
- let marker: JsonRecord | undefined;
944
- if (markerText) {
945
- try {
946
- const parsed = JSON.parse(markerText);
947
- marker = isRecord(parsed) ? parsed : undefined;
948
- } catch {
949
- marker = undefined;
955
+ const systemPrompt = getSystemPromptSafe(ctx);
956
+ if (!systemPrompt) return;
957
+ if (loaded && shouldInjectCompletionWorkflowContext(loaded.snapshot, ctx)) {
958
+ const additions = isWorkflowDone(loaded.snapshot)
959
+ ? [buildDoneWorkflowBoundaryReminder(loaded.snapshot)]
960
+ : [composeSystemReminder(loaded.snapshot, loaded.sliceHistory, loaded.stopHistory)];
961
+ if (!isWorkflowDone(loaded.snapshot)) {
962
+ const markerText = await readText(loaded.snapshot.files.compactionMarkerPath);
963
+ let marker: JsonRecord | undefined;
964
+ if (markerText) {
965
+ try {
966
+ const parsed = JSON.parse(markerText);
967
+ marker = isRecord(parsed) ? parsed : undefined;
968
+ } catch {
969
+ marker = undefined;
970
+ }
950
971
  }
972
+ if (marker) additions.push(buildPostCompactionDriverInstructions(loaded.snapshot, marker));
951
973
  }
952
- if (marker) additions.push(buildPostCompactionDriverInstructions(loaded.snapshot, marker));
974
+ maybeWriteTestSnapshot(completionTestSystemReminderPath(), additions.join("\n\n"));
975
+ return {
976
+ systemPrompt: `${systemPrompt}\n\n${additions.join("\n\n")}`,
977
+ };
953
978
  }
954
- maybeWriteTestSnapshot(completionTestSystemReminderPath(), additions.join("\n\n"));
955
- const systemPrompt = getSystemPromptSafe(ctx);
956
- if (!systemPrompt) return;
979
+ if (!shouldInjectCookHandoffBoundary(event, ctx)) return;
980
+ const handoffReminder = buildCookHandoffBoundaryReminder();
981
+ maybeWriteTestSnapshot(completionTestCookHandoffReminderPath(), handoffReminder);
957
982
  return {
958
- systemPrompt: `${systemPrompt}\n\n${additions.join("\n\n")}`,
983
+ systemPrompt: `${systemPrompt}\n\n${handoffReminder}`,
959
984
  };
960
985
  });
961
986
 
@@ -24,6 +24,18 @@ export type AdvisoryStartupBrief = {
24
24
  evaluation_profile?: string;
25
25
  };
26
26
 
27
+ export function buildCookHandoffBoundaryReminder(): string {
28
+ return [
29
+ "You are still in ordinary main chat before any explicit /cook workflow entry.",
30
+ "Use ordinary chat to clarify requirements, discuss tradeoffs, and propose implementation approaches.",
31
+ "/cook is the only explicit entrypoint into long-running completion workflow.",
32
+ "When you judge that the task has matured into completion-workflow scope — for example the user has clearly shifted from exploration into implementation intent, you have just produced a concrete plan or proposal whose next step would naturally be implementation, or the task spans multiple files, steps, or verification surfaces — stop short of long-running implementation and tell the user to run /cook.",
33
+ "At that handoff point, do not begin long-running product implementation in ordinary chat, do not edit tracked product files for that workflow-level task, and do not act as though /cook had already been invoked.",
34
+ "When handing off, explain that /cook will derive a startup brief from recent discussion and ask for confirmation before workflow start.",
35
+ "If the task is still ordinary Q&A, lightweight brainstorming, or a tiny one-off fix, continue normally without forcing /cook.",
36
+ ].join(" ");
37
+ }
38
+
27
39
  export function buildContextProposalGoalText(proposal: {
28
40
  mission: string;
29
41
  scope: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -17,6 +17,7 @@ checks = {
17
17
  "README.md": [
18
18
  "`/cook` is the explicit workflow boundary for starting, continuing, refocusing, or beginning the next round of long-running repo work.",
19
19
  "Only explicit `/cook` enters the workflow. Ordinary prompts stay in the main chat and go straight to the primary agent.",
20
+ "If a task has clearly matured into completion-workflow scope, the primary agent should hand you off to `/cook` instead of starting long-running implementation directly in ordinary chat.",
20
21
  "`/cook` is the canonical workflow boundary and manual entry point",
21
22
  "Discuss the concrete repo change in the main chat, then run `/cook`",
22
23
  "The confirmed startup brief is also preserved there as advisory intake for later re-grounding.",
@@ -24,10 +25,12 @@ checks = {
24
25
  "CHANGELOG.md": [
25
26
  "made `/cook` derive a confirmable startup brief from recent discussion before any canonical workflow rewrite, then preserve the confirmed brief in canonical state as advisory intake for later re-grounding",
26
27
  "removed inline `/cook` arguments from the shipped entry path again so explicit bare `/cook` is the only public command, and fail closed when recent discussion is insufficient or unreliable",
28
+ "added a pre-`/cook` ordinary-chat handoff boundary so the primary agent is instructed to stop at `/cook` once a task has matured into completion-workflow scope instead of starting long-running implementation directly in ordinary chat",
27
29
  ],
28
- "extensions/completion/index.ts": [
29
- 'description: "/cook workflow: derive a startup brief from recent discussion, then start, continue, refocus, or start the next round from the explicit /cook command"',
30
- '"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook."',
30
+ "extensions/completion/prompt-surfaces.ts": [
31
+ '"/cook is the only explicit entrypoint into long-running completion workflow."',
32
+ '"When you judge that the task has matured into completion-workflow scope',
33
+ '"If the task is still ordinary Q&A, lightweight brainstorming, or a tiny one-off fix, continue normally without forcing /cook."',
31
34
  ],
32
35
  }
33
36
 
@@ -51,6 +51,7 @@ ROOT="$TMPDIR/repo"
51
51
  KICKOFF_PROMPT="$TMPDIR/kickoff-prompt.txt"
52
52
  RESUME_PROMPT="$TMPDIR/resume-prompt.txt"
53
53
  ORDINARY_SYSTEM_REMINDER="$TMPDIR/ordinary-system-reminder.txt"
54
+ ORDINARY_HANDOFF_REMINDER="$TMPDIR/ordinary-handoff-reminder.txt"
54
55
  UNCLEAR_ROUTING_SNAPSHOT="$TMPDIR/active-unclear-routing.json"
55
56
  UNCLEAR_CHOOSER_SNAPSHOT="$TMPDIR/unexpected-existing-workflow-chooser.json"
56
57
  ORDINARY_AUTO_RESUME_PROMPT="$TMPDIR/ordinary-auto-resume-prompt.txt"
@@ -146,23 +147,29 @@ assert f'- task_type: {expected_task_type}' in kickoff, 'kickoff prompt missing
146
147
  assert f'- evaluation_profile: {expected_eval_profile}' in kickoff, 'kickoff prompt missing canonical evaluation_profile'
147
148
  PY
148
149
 
149
- rm -f "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT"
150
+ rm -f "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_HANDOFF_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT"
150
151
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
151
152
  PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH="$ORDINARY_SYSTEM_REMINDER" \
153
+ PI_COMPLETION_TEST_COOK_HANDOFF_REMINDER_PATH="$ORDINARY_HANDOFF_REMINDER" \
152
154
  PI_COMPLETION_TEST_AUTO_CONTINUE_ON_SESSION_START=1 \
153
155
  PI_COMPLETION_TEST_AUTO_CONTINUE_PROMPT_PATH="$ORDINARY_AUTO_RESUME_PROMPT" \
154
156
  pi -e "$PKG_ROOT" -p "Summarize the repo briefly." \
155
157
  >"$TMPDIR/pi-completion-smoke-ordinary.out" 2>"$TMPDIR/pi-completion-smoke-ordinary.err"
156
158
 
157
- python3 - "$TMPDIR/pi-completion-smoke-ordinary.out" "$TMPDIR/pi-completion-smoke-ordinary.err" "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT" <<'PY'
159
+ python3 - "$TMPDIR/pi-completion-smoke-ordinary.out" "$TMPDIR/pi-completion-smoke-ordinary.err" "$ORDINARY_SYSTEM_REMINDER" "$ORDINARY_HANDOFF_REMINDER" "$ORDINARY_AUTO_RESUME_PROMPT" <<'PY'
158
160
  import sys
159
161
  from pathlib import Path
160
162
 
161
163
  output = Path(sys.argv[1]).read_text() + Path(sys.argv[2]).read_text()
162
164
  reminder = Path(sys.argv[3])
163
- auto_resume = Path(sys.argv[4])
165
+ handoff = Path(sys.argv[4])
166
+ auto_resume = Path(sys.argv[5])
164
167
 
165
168
  assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from canonical state'
169
+ assert handoff.exists(), 'ordinary non-/cook turn should inject the /cook handoff boundary reminder'
170
+ handoff_text = handoff.read_text()
171
+ assert '/cook is the only explicit entrypoint into long-running completion workflow.' in handoff_text, 'ordinary handoff reminder should preserve the explicit /cook workflow boundary'
172
+ assert 'stop short of long-running implementation and tell the user to run /cook.' in handoff_text, 'ordinary handoff reminder should require primary-agent handoff before implementation'
166
173
  assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
167
174
  assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
168
175
  PY
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: cook-handoff-boundary
3
+ description: Ordinary-chat boundary contract for deciding when a task has matured enough that the primary agent must stop short of long-running implementation and hand the user off to `/cook`.
4
+ ---
5
+
6
+ # /cook Handoff Boundary
7
+
8
+ Load or summarize this contract when the primary agent is operating in ordinary main chat before the user has explicitly entered `/cook`.
9
+
10
+ This skill governs the boundary between:
11
+
12
+ - ordinary main-chat discussion, clarification, and proposal work
13
+ - explicit transition into long-running completion workflow through `/cook`
14
+
15
+ ## Core Contract
16
+
17
+ - Ordinary chat may be used to clarify requirements, discuss tradeoffs, and propose implementation approaches.
18
+ - `/cook` is the only explicit entrypoint into long-running completion workflow.
19
+ - When the primary agent judges that a task has matured into completion-workflow scope, it must stop short of implementation and direct the user to `/cook`.
20
+
21
+ ## When To Hand Off To `/cook`
22
+
23
+ The primary agent should consider `/cook` handoff appropriate when one or more of the following are true:
24
+
25
+ - the user has clearly shifted from exploration into implementation intent
26
+ - the agent has just produced a concrete plan or proposal whose natural next step would be implementation
27
+ - the task spans multiple files, steps, or verification surfaces
28
+ - the task needs resumability, review, audit, or canonical workflow state
29
+ - the task is better treated as a long-running repo mission than a one-off answer or tiny fix
30
+
31
+ ## Required Handoff Behavior
32
+
33
+ When the task is judged ready for completion workflow, the primary agent must:
34
+
35
+ - stop before long-running implementation
36
+ - not edit tracked product files in ordinary chat for that workflow-level task
37
+ - tell the user to run `/cook`
38
+ - explain that `/cook` will derive a startup brief from recent discussion and ask for confirmation before workflow start
39
+
40
+ Suggested wording:
41
+
42
+ > This task is now mature enough for the `/cook` workflow. If you want me to start implementation, run `/cook`. I’ll use our recent discussion to generate a startup brief for confirmation before the workflow begins.
43
+
44
+ A short recap may include mission, scope, or acceptance, but that recap must not be presented as canonical plan state.
45
+
46
+ ## Forbidden Behaviors
47
+
48
+ Once the task is judged ready for completion workflow, the primary agent must not:
49
+
50
+ - directly begin long-running implementation in ordinary chat
51
+ - modify tracked product files as part of that workflow-level task
52
+ - act as though `/cook` had already been invoked
53
+ - silently rewrite ordinary-chat discussion into active workflow state
54
+
55
+ ## Relationship To `completion-protocol`
56
+
57
+ This skill is only about pre-`/cook` ordinary-chat handoff behavior.
58
+
59
+ After the user explicitly enters `/cook`, the separate `completion-protocol` skill governs:
60
+
61
+ - canonical `.agent/**` workflow state
62
+ - workflow-driver behavior
63
+ - mandatory completion-role dispatch
64
+ - review, audit, and stop-wave rules