@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.
- package/CHANGELOG.md +11 -0
- package/README.md +80 -52
- package/extensions/completion/driver.ts +151 -131
- package/extensions/completion/index.ts +8 -3
- package/extensions/completion/input-routing.ts +350 -0
- package/extensions/completion/prompt-surfaces.ts +99 -1
- package/extensions/completion/proposal.ts +1 -1
- package/extensions/completion/role-runner.ts +285 -2
- package/extensions/completion/types.ts +42 -0
- package/package.json +1 -1
- package/scripts/cook-trigger-routing-test.sh +314 -0
- package/scripts/release-check.sh +12 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.51
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- shipped assist-mode natural-language handoff that can offer to route `開始做`, `開始實作`, or `go ahead` style execution handoffs into the canonical `/cook` flow before the primary agent starts implementation work, while keeping `/cook` as the explicit workflow boundary and approval gate
|
|
10
|
+
- added `bash ./scripts/cook-trigger-routing-test.sh` to `npm run release-check` so packaged release parity now covers the natural-language takeover path alongside the existing `/cook` startup/refocus/context regressions
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- streamlined the README into a more user-facing guide with a 30-second quick start, common actions table, clearer natural-language handoff expectations, and shorter `/cook` usage explanations
|
|
15
|
+
|
|
5
16
|
## 0.1.50
|
|
6
17
|
|
|
7
18
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
# @linimin/pi-letscook
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`/cook` turns main-chat discussion about concrete repo changes into a resumable repo workflow stored in repo-local `.agent/**` state.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Assist-mode natural-language handoff can also offer to enter that same `/cook` flow before the primary agent starts implementation work, but `/cook` remains the canonical workflow boundary.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Use it when
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- keep review, audit, and verification tied to the repo
|
|
9
|
+
- work spans multiple sessions
|
|
10
|
+
- you want one mission tracked in repo state instead of chat memory
|
|
11
|
+
- you want clear continue / refocus / next-round behavior
|
|
12
|
+
- you want review, audit, and verification tied to the repo
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
- one-off chat tasks
|
|
17
|
-
- brainstorming
|
|
18
|
-
- planning docs without immediate implementation
|
|
14
|
+
## Skip it when
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
- repo-local canonical state in `.agent/**`
|
|
24
|
-
- resumable long-running workflows
|
|
25
|
-
- discussion-first startup, continue, refocus, and next-round routing
|
|
26
|
-
- fail-closed guidance that sends you back to the main chat when the mission still needs clarification
|
|
27
|
-
- deterministic verification, review, audit, and stop checks
|
|
16
|
+
- you only need a one-off answer
|
|
17
|
+
- you are brainstorming
|
|
18
|
+
- you are writing planning docs but are not ready to start concrete repo changes
|
|
28
19
|
|
|
29
20
|
## Install
|
|
30
21
|
|
|
@@ -34,62 +25,94 @@ pi install npm:@linimin/pi-letscook
|
|
|
34
25
|
|
|
35
26
|
Then run `/reload` in Pi.
|
|
36
27
|
|
|
37
|
-
##
|
|
28
|
+
## 30-second quick start
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
1. Install the package:
|
|
31
|
+
`pi install npm:@linimin/pi-letscook`
|
|
32
|
+
2. Run `/reload` in Pi.
|
|
33
|
+
3. In the main chat, describe the concrete repo change you want.
|
|
34
|
+
4. Run `/cook` or `/cook <hint>`.
|
|
35
|
+
5. Review the proposal and choose **Start** or **Cancel**.
|
|
36
|
+
6. Later, run `/cook` again to continue, refocus, or start the next round.
|
|
40
37
|
|
|
41
38
|
```text
|
|
42
39
|
/cook
|
|
43
40
|
/cook login redirect
|
|
44
41
|
```
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
## Common actions
|
|
44
|
+
|
|
45
|
+
| If you want to... | Do this |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Start a long-running task | Discuss the concrete repo change in the main chat, then run `/cook` |
|
|
48
|
+
| Bias mission detection toward one intent | Run `/cook <hint>` |
|
|
49
|
+
| Hand off from discussion into the same `/cook` flow | Say `開始做`, `開始實作`, or `go ahead`, then accept the confirmation |
|
|
50
|
+
| Continue the current workflow | Run `/cook` |
|
|
51
|
+
| Use the canonical fallback when the natural-language trigger does not fire | Run `/cook` explicitly |
|
|
47
52
|
|
|
48
|
-
What
|
|
49
|
-
- start a brand-new workflow from recent discussion
|
|
50
|
-
- continue the current workflow when recent discussion still matches it, or when discussion is too weak or ambiguous to justify a refocus
|
|
51
|
-
- surface a conservative refocus chooser when recent discussion clearly points to a different workflow
|
|
52
|
-
- start the next workflow round after the previous one is `done`
|
|
53
|
+
## What `/cook` expects
|
|
53
54
|
|
|
54
|
-
What it expects:
|
|
55
55
|
- recent main-chat discussion about concrete repo changes
|
|
56
56
|
- README/CHANGELOG updates still count as concrete repo changes
|
|
57
57
|
- assistant-produced summaries and plan/spec/design-doc/proposal-only artifacts do not
|
|
58
58
|
|
|
59
|
-
`/cook <hint>` acts as a high-priority intent hint
|
|
59
|
+
`/cook <hint>` acts as a high-priority intent hint for interpreting recent discussion, but it does not bypass fail-closed behavior or the approval-only Start/Cancel confirmation flow.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
If recent discussion is missing, weak, ambiguous, assistant-produced, or only describes planning artifacts instead of concrete repo changes, `/cook` fails closed, leaves canonical `.agent/**` state unchanged, and tells you to clarify the mission in the main chat before rerunning `/cook`.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## Natural-language handoff (assist mode)
|
|
64
64
|
|
|
65
|
-
`/cook`
|
|
65
|
+
After you have discussed a concrete repo change in the main chat, short execution handoff phrases such as `開始做`, `開始實作`, or `go ahead` can offer to enter the same `/cook` flow before the primary agent starts implementation work.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
Important behavior:
|
|
68
|
+
- the handoff is only a shortcut into `/cook`; `/cook` is still the canonical workflow boundary
|
|
69
|
+
- it asks for confirmation before `/cook` takes over
|
|
70
|
+
- if the trigger is unclear or unavailable, nothing is auto-started and you can run `/cook` explicitly
|
|
71
|
+
- ordinary questions and explicit slash commands continue normally
|
|
72
72
|
|
|
73
|
-
##
|
|
73
|
+
## Typical examples
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Start a new workflow from recent discussion:
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
```text
|
|
78
|
+
I want to add login redirect handling and tests.
|
|
79
|
+
/cook
|
|
80
|
+
```
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
Bias proposal derivation toward a specific intent:
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
```text
|
|
85
|
+
/cook login redirect
|
|
86
|
+
```
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
- **Start new workflow from recent discussion**
|
|
87
|
-
- **Start alternate workflow from recent discussion** (when a second plausible mission exists)
|
|
88
|
-
- **Cancel**
|
|
88
|
+
Hand off from discussion into the same `/cook` flow:
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
```text
|
|
91
|
+
We should implement the natural-language routing path next.
|
|
92
|
+
開始做
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## What happens when you run `/cook`
|
|
96
|
+
|
|
97
|
+
`/cook` supports both bare discussion-driven startup and optional inline intent hints. Assist-mode natural-language handoff is optional; explicit `/cook` is always the canonical fallback.
|
|
98
|
+
|
|
99
|
+
| Repo state | What you'll see |
|
|
100
|
+
|---|---|
|
|
101
|
+
| No workflow yet | A startup proposal built from recent main-chat discussion. You choose **Start** or **Cancel**. Weak or planning-only discussion fails closed. |
|
|
102
|
+
| Active workflow exists | Usually a resume of the current workflow. If recent discussion clearly points to a different concrete repo change, `/cook` shows a chooser first and only rewrites canonical state after confirmation. Ambiguous discussion stays conservative. |
|
|
103
|
+
| Previous workflow is `done` | A next-round proposal from recent main-chat discussion, again behind **Start** or **Cancel**. Discussion that only restates already-finished work fails closed. |
|
|
104
|
+
|
|
105
|
+
## Confirmation and fail-closed behavior
|
|
91
106
|
|
|
92
|
-
|
|
107
|
+
`/cook` never silently starts or rewrites canonical `.agent/**` state on unclear input.
|
|
108
|
+
|
|
109
|
+
- startup, next-round, and refocus proposals are approval-only
|
|
110
|
+
- actions are **Start** and **Cancel**
|
|
111
|
+
- **Cancel** is side-effect free: discuss changes in the main chat and rerun `/cook`
|
|
112
|
+
- weak, ambiguous, assistant-produced, or planning-only discussion does not start a workflow
|
|
113
|
+
- when recent discussion suggests a different workflow, `/cook` shows a chooser before any canonical state rewrite
|
|
114
|
+
|
|
115
|
+
When you accept startup or refocus, `/cook` persists the chosen workflow state in canonical `.agent/**` files before the re-ground round begins.
|
|
93
116
|
|
|
94
117
|
## Observability
|
|
95
118
|
|
|
@@ -109,6 +132,10 @@ While a `completion_role` subprocess is running:
|
|
|
109
132
|
- running-role output distinguishes tool work from `PROGRESS`, `RATIONALE`, `NEXT`, `VERIFYING`, and `STATE-DELTA`
|
|
110
133
|
- waiting and stalled states are surfaced deterministically from timestamps
|
|
111
134
|
|
|
135
|
+
## Maintainer and protocol details
|
|
136
|
+
|
|
137
|
+
The sections below are mainly useful if you maintain the extension, inspect canonical `.agent/**` state, or work on the packaged completion protocol itself.
|
|
138
|
+
|
|
112
139
|
## Structured evaluation rubrics
|
|
113
140
|
|
|
114
141
|
The packaged completion workflow now defines a shared structured evaluation-rubric contract for the read-only evaluation roles:
|
|
@@ -225,6 +252,7 @@ Run validation from the package root:
|
|
|
225
252
|
npm run smoke-test
|
|
226
253
|
npm run refocus-test
|
|
227
254
|
npm run context-proposal-test
|
|
255
|
+
bash ./scripts/cook-trigger-routing-test.sh
|
|
228
256
|
bash scripts/canonical-evidence-artifact-test.sh
|
|
229
257
|
npm run observability-status-test
|
|
230
258
|
npm run evaluator-calibration-test
|
|
@@ -232,7 +260,7 @@ npm run rubric-contract-test
|
|
|
232
260
|
npm run release-check
|
|
233
261
|
```
|
|
234
262
|
|
|
235
|
-
`npm run release-check` is the broad packaged-release verifier. It begins with `bash .agent/verify_completion_control_plane.sh`, so missing or stale `.agent/verification-evidence.json` parity fails closed before the broader suite runs, then asserts the shipped single-command `/cook` public parity surfaces in `README.md`, `CHANGELOG.md`, and the `/cook` help/fail-closed copy in `extensions/completion/index.ts`, reruns the startup/refocus/context checks — including the critique-aware `/cook` confirmation regression and the smoke auto-resume prompt path — includes deterministic canonical evidence artifact coverage and includes deterministic active-slice contract coverage plus observability coverage, evaluator calibration, and the rubric-contract regression, and finishes with `npm pack --dry-run`.
|
|
263
|
+
`npm run release-check` is the broad packaged-release verifier. It begins with `bash .agent/verify_completion_control_plane.sh`, so missing or stale `.agent/verification-evidence.json` parity fails closed before the broader suite runs, then asserts the shipped single-command `/cook` public parity surfaces in `README.md`, `CHANGELOG.md`, and the `/cook` help/fail-closed copy in `extensions/completion/index.ts`, reruns `bash ./scripts/cook-trigger-routing-test.sh` for the assist-mode natural-language handoff path, reruns the startup/refocus/context checks — including the critique-aware `/cook` confirmation regression and the smoke auto-resume prompt path — includes deterministic canonical evidence artifact coverage and includes deterministic active-slice contract coverage plus observability coverage, evaluator calibration, and the rubric-contract regression, and finishes with `npm pack --dry-run`.
|
|
236
264
|
|
|
237
265
|
The direct package-root verifier commands above intentionally self-isolate the repo-local extension when they shell back into `pi`, so you should not need to wrap them with `pi --no-extensions` even if `@linimin/pi-letscook` is also installed globally on the same machine.
|
|
238
266
|
|
|
@@ -530,142 +530,162 @@ function buildMission(projectName: string, missionAnchor: string): string {
|
|
|
530
530
|
return `# Mission\n\nProject: ${projectName}\n\nMission anchor:\n${missionAnchor}\n\nThis file is a tracked human-readable statement of the repo's completion mission. Re-grounders may refine this file when repo truth becomes clearer, but it must stay truthful to shipped behavior and the active completion objective.\n`;
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
export
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
533
|
+
export type CookInvocationOrigin = "command" | "natural-language-trigger";
|
|
534
|
+
|
|
535
|
+
export type RunCookEntryOptions = {
|
|
536
|
+
origin: CookInvocationOrigin;
|
|
537
|
+
hintText?: string;
|
|
538
|
+
originalInput?: string;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
export async function runCookEntry(
|
|
542
|
+
pi: ExtensionAPI,
|
|
543
|
+
ctx: DriverContext,
|
|
544
|
+
deps: CompletionDriverDeps,
|
|
545
|
+
options: RunCookEntryOptions,
|
|
546
|
+
): Promise<void> {
|
|
547
|
+
const explicitHint = options.hintText?.trim() ? options.hintText.trim() : undefined;
|
|
548
|
+
let goal: string | undefined;
|
|
549
|
+
const cwd = deps.getCtxCwd(ctx);
|
|
550
|
+
let snapshot = await loadCompletionSnapshot(cwd);
|
|
551
|
+
const workflowDone = isWorkflowDone(snapshot);
|
|
552
|
+
let kickoffIntent: "auto" | "continue" | "refocus" = "auto";
|
|
553
|
+
let kickoffMissionAnchor = snapshot ? currentMissionAnchor(snapshot) : undefined;
|
|
554
|
+
let kickoffAnalysis: ContextProposalAnalysis | undefined;
|
|
555
|
+
|
|
556
|
+
if (!snapshot) {
|
|
557
|
+
const root = findRepoRoot(cwd) ?? cwd;
|
|
558
|
+
const projectName = path.basename(root);
|
|
559
|
+
const proposal = await deps.deriveCookContextProposal(ctx, projectName, explicitHint);
|
|
560
|
+
if (!proposal) {
|
|
561
|
+
deps.emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(deps), "info");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
565
|
+
title: "Start a completion workflow from the recent discussion?",
|
|
566
|
+
});
|
|
567
|
+
if (!decision) {
|
|
568
|
+
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal", deps), "info");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
goal = decision.goalText;
|
|
572
|
+
kickoffMissionAnchor = decision.missionAnchor;
|
|
573
|
+
kickoffAnalysis = decision.analysis;
|
|
574
|
+
const startupRouting = deps.finalizeContextProposalAnalysis(kickoffAnalysis, [goal ?? kickoffMissionAnchor ?? projectName]);
|
|
575
|
+
const created = await deps.scaffoldCompletionFiles(root, kickoffMissionAnchor ?? projectName, {
|
|
576
|
+
analysis: startupRouting,
|
|
577
|
+
continuationReason: deps.buildContextProposalContinuationReason(
|
|
578
|
+
"User started workflow via /cook:",
|
|
579
|
+
goal ?? kickoffMissionAnchor ?? projectName,
|
|
580
|
+
startupRouting,
|
|
581
|
+
),
|
|
582
|
+
});
|
|
583
|
+
deps.emitCommandText(
|
|
584
|
+
ctx,
|
|
585
|
+
`Initialized completion control plane in ${created.root}${created.created.length > 0 ? ` (${created.created.length} files created)` : ""}`,
|
|
586
|
+
"info",
|
|
587
|
+
);
|
|
588
|
+
snapshot = await loadCompletionSnapshot(root);
|
|
589
|
+
}
|
|
590
|
+
if (!snapshot) {
|
|
591
|
+
deps.emitCommandText(ctx, "Failed to load completion workflow state", "error");
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
deps.activateCompletionRoutingForRoot(snapshot.files.root);
|
|
595
|
+
if (!goal) {
|
|
596
|
+
if (workflowDone) {
|
|
597
|
+
const projectName = path.basename(snapshot.files.root);
|
|
598
|
+
const proposal = await deps.deriveCookContextProposal(ctx, projectName, explicitHint);
|
|
599
|
+
if (!proposal) {
|
|
600
|
+
deps.emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(deps, "The previous completion workflow is already done."), "info");
|
|
601
|
+
return;
|
|
579
602
|
}
|
|
580
|
-
|
|
581
|
-
|
|
603
|
+
const decision = await deps.confirmContextProposal(ctx, proposal, {
|
|
604
|
+
title: "The previous completion workflow is done. Start the next workflow round from the recent discussion?",
|
|
605
|
+
});
|
|
606
|
+
if (!decision) {
|
|
607
|
+
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled next workflow round proposal", deps), "info");
|
|
582
608
|
return;
|
|
583
609
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
if (decision.action === "continue") {
|
|
627
|
-
await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
const selectedProposal = decision.proposal;
|
|
631
|
-
const proposalDecision = await deps.confirmContextProposal(ctx, selectedProposal, {
|
|
632
|
-
title:
|
|
633
|
-
assessment.action === "refocus"
|
|
634
|
-
? "Start the replacement workflow from recent discussion?"
|
|
635
|
-
: "Start the latest inferred workflow from recent discussion?",
|
|
636
|
-
});
|
|
637
|
-
if (!proposalDecision) {
|
|
638
|
-
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
goal = proposalDecision.goalText;
|
|
642
|
-
kickoffIntent = "refocus";
|
|
643
|
-
kickoffMissionAnchor = proposalDecision.missionAnchor;
|
|
644
|
-
await refocusCompletionMission(snapshot, proposalDecision.missionAnchor, proposalDecision.goalText, proposalDecision.analysis, deps);
|
|
645
|
-
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
646
|
-
deps.emitCommandText(ctx, `Refocused completion mission from recent discussion to: ${proposalDecision.missionAnchor}`, "info");
|
|
647
|
-
}
|
|
610
|
+
goal = decision.goalText;
|
|
611
|
+
kickoffIntent = "refocus";
|
|
612
|
+
kickoffMissionAnchor = decision.missionAnchor;
|
|
613
|
+
await refocusCompletionMission(snapshot, decision.missionAnchor, decision.goalText, decision.analysis, deps);
|
|
614
|
+
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
615
|
+
deps.emitCommandText(ctx, `Started a new completion workflow round from recent discussion: ${decision.missionAnchor}`, "info");
|
|
616
|
+
} else {
|
|
617
|
+
const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps, explicitHint);
|
|
618
|
+
if (!assessment.proposal || assessment.action === "continue") {
|
|
619
|
+
await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
|
|
623
|
+
intro:
|
|
624
|
+
assessment.action === "refocus"
|
|
625
|
+
? "Recent non-command discussion suggests a different workflow. Choose how /cook should proceed:"
|
|
626
|
+
: "Recent discussion may point to a different implementation goal. Review the current mission and the latest inferred mission before deciding how /cook should proceed:",
|
|
627
|
+
proposedMissionLabel: "Proposed mission from recent discussion",
|
|
628
|
+
refocusChoiceLabel:
|
|
629
|
+
"Start new workflow from recent discussion\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
|
|
630
|
+
comparison: assessment.action === "refocus" ? "semantic" : "strict",
|
|
631
|
+
});
|
|
632
|
+
if (!decision) {
|
|
633
|
+
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled existing workflow confirmation", deps), "info");
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (decision.action === "continue") {
|
|
637
|
+
await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const selectedProposal = decision.proposal;
|
|
641
|
+
const proposalDecision = await deps.confirmContextProposal(ctx, selectedProposal, {
|
|
642
|
+
title:
|
|
643
|
+
assessment.action === "refocus"
|
|
644
|
+
? "Start the replacement workflow from recent discussion?"
|
|
645
|
+
: "Start the latest inferred workflow from recent discussion?",
|
|
646
|
+
});
|
|
647
|
+
if (!proposalDecision) {
|
|
648
|
+
deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
|
|
649
|
+
return;
|
|
648
650
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
651
|
+
goal = proposalDecision.goalText;
|
|
652
|
+
kickoffIntent = "refocus";
|
|
653
|
+
kickoffMissionAnchor = proposalDecision.missionAnchor;
|
|
654
|
+
await refocusCompletionMission(snapshot, proposalDecision.missionAnchor, proposalDecision.goalText, proposalDecision.analysis, deps);
|
|
655
|
+
snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
|
|
656
|
+
deps.emitCommandText(ctx, `Refocused completion mission from recent discussion to: ${proposalDecision.missionAnchor}`, "info");
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
kickoffMissionAnchor = kickoffMissionAnchor ?? currentMissionAnchor(snapshot);
|
|
660
|
+
const kickoffGoal = goal ?? kickoffMissionAnchor;
|
|
661
|
+
pi.setSessionName(`completion: ${kickoffMissionAnchor.slice(0, 60)}`);
|
|
662
|
+
const kickoffPrompt = deps.completionKickoff(
|
|
663
|
+
kickoffGoal,
|
|
664
|
+
currentTaskType(snapshot) ?? "(missing)",
|
|
665
|
+
currentEvaluationProfile(snapshot) ?? "(missing)",
|
|
666
|
+
kickoffIntent,
|
|
667
|
+
kickoffMissionAnchor,
|
|
668
|
+
);
|
|
669
|
+
const rootKey = deps.completionRootKey(snapshot, deps.getCtxCwd(ctx));
|
|
670
|
+
const fingerprint = completionContinuationFingerprint(snapshot) ?? JSON.stringify({
|
|
671
|
+
kind: "kickoff",
|
|
672
|
+
mission_anchor: kickoffMissionAnchor,
|
|
673
|
+
goal: kickoffGoal,
|
|
674
|
+
intent: kickoffIntent,
|
|
675
|
+
task_type: currentTaskType(snapshot) ?? "(missing)",
|
|
676
|
+
evaluation_profile: currentEvaluationProfile(snapshot) ?? "(missing)",
|
|
677
|
+
});
|
|
678
|
+
await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, kickoffPrompt, "kickoff", deps);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export function registerCookCommand(pi: ExtensionAPI, deps: CompletionDriverDeps): void {
|
|
682
|
+
pi.registerCommand("cook", {
|
|
683
|
+
description: deps.cookCommandSpec.description,
|
|
684
|
+
handler: async (args, ctx) => {
|
|
685
|
+
await runCookEntry(pi, ctx, deps, {
|
|
686
|
+
origin: "command",
|
|
687
|
+
hintText: args,
|
|
667
688
|
});
|
|
668
|
-
await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, kickoffPrompt, "kickoff", deps);
|
|
669
689
|
},
|
|
670
690
|
});
|
|
671
691
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
markQueuedDriverPromptInFlight,
|
|
15
15
|
registerCookCommand,
|
|
16
16
|
} from "./driver";
|
|
17
|
+
import { handleCookNaturalLanguageTrigger } from "./input-routing";
|
|
17
18
|
import {
|
|
18
19
|
assessMissionAnchor,
|
|
19
20
|
collectRecentDiscussionEntries,
|
|
@@ -207,9 +208,9 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
|
|
|
207
208
|
|
|
208
209
|
const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
|
|
209
210
|
const COOK_BARE_ONLY_GUIDANCE =
|
|
210
|
-
"/cook
|
|
211
|
+
"/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.";
|
|
211
212
|
const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
|
|
212
|
-
"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes.
|
|
213
|
+
"/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.";
|
|
213
214
|
|
|
214
215
|
function buildCookCancellationMessage(prefix: string): string {
|
|
215
216
|
return `${prefix}. ${COOK_MAIN_CHAT_RERUN_GUIDANCE}`;
|
|
@@ -922,7 +923,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
922
923
|
structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
|
|
923
924
|
mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
|
|
924
925
|
cookCommandSpec: {
|
|
925
|
-
description: "/cook workflow: start, continue, refocus, or start the next round
|
|
926
|
+
description: "/cook workflow: start, continue, refocus, or start the next round; assist-mode natural-language handoff can offer the same /cook boundary",
|
|
926
927
|
},
|
|
927
928
|
buildContextProposalContinuationReason,
|
|
928
929
|
completionKickoff,
|
|
@@ -952,6 +953,10 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
952
953
|
shouldTreatBareActiveWorkflowProposalAsClearRefocus,
|
|
953
954
|
};
|
|
954
955
|
|
|
956
|
+
pi.on("input", async (event, ctx) => {
|
|
957
|
+
return await handleCookNaturalLanguageTrigger(pi, event, ctx, driverDeps);
|
|
958
|
+
});
|
|
959
|
+
|
|
955
960
|
pi.on("session_start", async (_event, ctx) => {
|
|
956
961
|
await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });
|
|
957
962
|
if (shouldTestAutoContinueOnSessionStart()) {
|