@linimin/pi-letscook 0.1.44 → 0.1.46

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,615 @@
1
+ import { promises as fsp } from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
+ import {
5
+ buildProfileRecord,
6
+ defaultActiveSlice,
7
+ defaultPlan,
8
+ defaultState,
9
+ defaultVerificationEvidence,
10
+ detectDocsSurfaces,
11
+ findRepoRoot,
12
+ loadCompletionSnapshot,
13
+ writeJsonFile,
14
+ } from "./state-store";
15
+ import type { CompletionStateSnapshot } from "./types";
16
+
17
+ type ContextProposalAnalysis = {
18
+ taskType?: string;
19
+ evaluationProfile?: string;
20
+ critique: string[];
21
+ risks: string[];
22
+ possibleNoise: string[];
23
+ };
24
+
25
+ type ContextProposal = {
26
+ mission: string;
27
+ scope: string[];
28
+ constraints: string[];
29
+ acceptance: string[];
30
+ analysis: ContextProposalAnalysis;
31
+ goalText: string;
32
+ basisPreview: string;
33
+ source: "session" | "analyst";
34
+ };
35
+
36
+ type ContextProposalDecision = {
37
+ missionAnchor: string;
38
+ goalText: string;
39
+ analysis: ContextProposalAnalysis;
40
+ };
41
+
42
+ type ExistingWorkflowDecision =
43
+ | { action: "continue"; currentMissionAnchor: string }
44
+ | { action: "refocus"; currentMissionAnchor: string; missionAnchor: string };
45
+
46
+ type ActiveWorkflowProposalAssessment = {
47
+ action: "continue" | "refocus" | "unclear";
48
+ currentMissionAnchor: string;
49
+ proposal?: ContextProposal;
50
+ reason: "matching_mission" | "clear_refocus" | "missing_proposal" | "ambiguous_discussion";
51
+ };
52
+
53
+ type ExistingWorkflowChooserOptions = {
54
+ intro?: string;
55
+ comparison?: "strict" | "semantic";
56
+ proposedMissionLabel?: string;
57
+ refocusChoiceLabel?: string;
58
+ };
59
+
60
+ type DriverContext = {
61
+ cwd: string;
62
+ hasUI: boolean;
63
+ ui: any;
64
+ sessionManager?: any;
65
+ model?: any;
66
+ modelRegistry?: any;
67
+ };
68
+
69
+ type DriverContinuationTracker = {
70
+ fingerprint: string;
71
+ attempts: number;
72
+ inFlight: boolean;
73
+ warned: boolean;
74
+ };
75
+
76
+ export type CompletionDriverDeps = {
77
+ bareOnlyGuidance: string;
78
+ structuredDiscussionFailureDetail: string;
79
+ mainChatRerunGuidance: string;
80
+ cookCommandSpec: {
81
+ description: string;
82
+ };
83
+ getCtxCwd: (ctx: { cwd: string }) => string;
84
+ getCtxHasUI: (ctx: { hasUI: boolean }) => boolean;
85
+ getCtxUi: <T extends { ui: any }>(ctx: T) => any | undefined;
86
+ emitCommandText: (
87
+ ctx: { hasUI: boolean; ui: any },
88
+ text: string,
89
+ level?: "info" | "success" | "warning" | "error",
90
+ ) => void;
91
+ completionRootKey: (snapshot: CompletionStateSnapshot | undefined, cwd: string) => string;
92
+ hasRunningCompletionRole: (rootKey: string) => boolean;
93
+ completionKickoff: (
94
+ goal: string,
95
+ taskType: string,
96
+ evaluationProfile: string,
97
+ intent?: "auto" | "continue" | "refocus",
98
+ missionAnchor?: string,
99
+ ) => string;
100
+ completionResumePrompt: (taskType: string, evaluationProfile: string) => string;
101
+ deriveCookContextProposal: (ctx: DriverContext, projectName: string) => Promise<ContextProposal | undefined>;
102
+ confirmContextProposal: (
103
+ ctx: { hasUI: boolean; ui: any },
104
+ proposal: ContextProposal,
105
+ options: { title: string; nonInteractiveBehavior?: "accept" | "cancel" },
106
+ ) => Promise<ContextProposalDecision | undefined>;
107
+ finalizeContextProposalAnalysis: (analysis: ContextProposalAnalysis | undefined, hintTexts?: string[]) => ContextProposalAnalysis;
108
+ buildContextProposalContinuationReason: (prefix: string, goalText: string, analysis: ContextProposalAnalysis) => string;
109
+ scaffoldCompletionFiles: (
110
+ root: string,
111
+ missionAnchor: string,
112
+ options?: { analysis?: ContextProposalAnalysis; continuationReason?: string },
113
+ ) => Promise<{ root: string; created: string[] }>;
114
+ maybeWriteActiveWorkflowRoutingSnapshot: (assessment: ActiveWorkflowProposalAssessment) => void;
115
+ missionAnchorsLikelyEquivalent: (left: string, right: string) => boolean;
116
+ missionAnchorsStrictlyEquivalent: (left: string, right: string) => boolean;
117
+ shouldTreatBareActiveWorkflowProposalAsClearRefocus: (proposal: ContextProposal) => boolean;
118
+ maybeWriteTestSnapshot: (targetPath: string | undefined, content: string) => void;
119
+ completionTestDriverPromptPath: () => string | undefined;
120
+ completionTestAutoContinuePromptPath: () => string | undefined;
121
+ completionTestExistingWorkflowChooserSnapshotPath: () => string | undefined;
122
+ completionTestWorkflowActionOverride: () => "continue" | "refocus" | "cancel" | undefined;
123
+ shouldSkipDriverKickoffForTests: () => boolean;
124
+ shouldTestAutoContinueOnSessionStart: () => boolean;
125
+ };
126
+
127
+ const DRIVER_AUTO_CONTINUE_MAX_ATTEMPTS = 2;
128
+ const driverContinuationByRoot = new Map<string, DriverContinuationTracker>();
129
+
130
+ function asString(value: unknown): string | undefined {
131
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
132
+ }
133
+
134
+ function asNumber(value: unknown): number | undefined {
135
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
136
+ }
137
+
138
+ function asStringArray(value: unknown): string[] {
139
+ return Array.isArray(value)
140
+ ? value.filter((item): item is string => typeof item === "string" && item.trim().length > 0)
141
+ : [];
142
+ }
143
+
144
+ function roleFromEnv(): string | undefined {
145
+ return asString(process.env.PI_COMPLETION_ROLE);
146
+ }
147
+
148
+ function buildCookCancellationMessage(prefix: string, deps: CompletionDriverDeps): string {
149
+ return `${prefix}. ${deps.mainChatRerunGuidance}`;
150
+ }
151
+
152
+ function buildCookStructuredDiscussionFailureMessage(deps: CompletionDriverDeps, prefix?: string): string {
153
+ return prefix ? `${prefix} ${deps.structuredDiscussionFailureDetail}` : deps.structuredDiscussionFailureDetail;
154
+ }
155
+
156
+ function currentMissionAnchor(snapshot: CompletionStateSnapshot): string {
157
+ return (
158
+ asString(snapshot.state?.mission_anchor) ??
159
+ asString(snapshot.plan?.mission_anchor) ??
160
+ asString(snapshot.active?.mission_anchor) ??
161
+ path.basename(snapshot.files.root)
162
+ );
163
+ }
164
+
165
+ function currentTaskType(snapshot: CompletionStateSnapshot): string | undefined {
166
+ return (
167
+ asString(snapshot.active?.task_type) ??
168
+ asString(snapshot.state?.task_type) ??
169
+ asString(snapshot.plan?.task_type) ??
170
+ asString(snapshot.profile?.task_type)
171
+ );
172
+ }
173
+
174
+ function currentEvaluationProfile(snapshot: CompletionStateSnapshot): string | undefined {
175
+ return (
176
+ asString(snapshot.active?.evaluation_profile) ??
177
+ asString(snapshot.state?.evaluation_profile) ??
178
+ asString(snapshot.plan?.evaluation_profile) ??
179
+ asString(snapshot.profile?.evaluation_profile)
180
+ );
181
+ }
182
+
183
+ export function completionContinuationFingerprint(snapshot: CompletionStateSnapshot): string | undefined {
184
+ if (asString(snapshot.state?.continuation_policy) !== "continue") return undefined;
185
+ const nextMandatoryRole = asString(snapshot.state?.next_mandatory_role);
186
+ if (!nextMandatoryRole) return undefined;
187
+ return JSON.stringify({
188
+ mission_anchor: asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? null,
189
+ task_type: currentTaskType(snapshot) ?? null,
190
+ evaluation_profile: currentEvaluationProfile(snapshot) ?? null,
191
+ current_phase: asString(snapshot.state?.current_phase) ?? null,
192
+ next_mandatory_role: nextMandatoryRole,
193
+ next_mandatory_action: asString(snapshot.state?.next_mandatory_action) ?? null,
194
+ active_status: asString(snapshot.active?.status) ?? null,
195
+ active_slice_id: asString(snapshot.active?.slice_id) ?? asString(snapshot.activeSlice?.slice_id) ?? null,
196
+ latest_completed_slice: asString(snapshot.state?.latest_completed_slice) ?? null,
197
+ latest_verified_slice: asString(snapshot.state?.latest_verified_slice) ?? null,
198
+ });
199
+ }
200
+
201
+ function noteQueuedDriverPrompt(rootKey: string, fingerprint: string): void {
202
+ const tracker = driverContinuationByRoot.get(rootKey);
203
+ if (tracker && tracker.fingerprint === fingerprint) {
204
+ tracker.attempts += 1;
205
+ tracker.inFlight = false;
206
+ tracker.warned = false;
207
+ return;
208
+ }
209
+ driverContinuationByRoot.set(rootKey, {
210
+ fingerprint,
211
+ attempts: 1,
212
+ inFlight: false,
213
+ warned: false,
214
+ });
215
+ }
216
+
217
+ export function markQueuedDriverPromptInFlight(rootKey: string, fingerprint: string): void {
218
+ const tracker = driverContinuationByRoot.get(rootKey);
219
+ if (!tracker || tracker.fingerprint !== fingerprint) return;
220
+ tracker.inFlight = true;
221
+ }
222
+
223
+ function clearDriverContinuationTracker(rootKey: string): void {
224
+ driverContinuationByRoot.delete(rootKey);
225
+ }
226
+
227
+ function isWorkflowDriverActive(snapshot: CompletionStateSnapshot | undefined): boolean {
228
+ return Boolean(snapshot) && asString(snapshot?.state?.continuation_policy) === "continue";
229
+ }
230
+
231
+ function isDriverContinuationStateParked(rootKey: string, fingerprint: string): boolean {
232
+ const tracker = driverContinuationByRoot.get(rootKey);
233
+ if (!tracker || tracker.fingerprint !== fingerprint) return false;
234
+ return tracker.warned;
235
+ }
236
+
237
+ function rememberParkedDriverContinuation(rootKey: string, fingerprint: string): void {
238
+ const tracker = driverContinuationByRoot.get(rootKey);
239
+ if (!tracker || tracker.fingerprint !== fingerprint) return;
240
+ tracker.warned = true;
241
+ tracker.inFlight = false;
242
+ }
243
+
244
+ async function queueCompletionDriverPrompt(
245
+ pi: ExtensionAPI,
246
+ ctx: { cwd: string; hasUI: boolean; ui: any },
247
+ rootKey: string,
248
+ fingerprint: string,
249
+ prompt: string,
250
+ kind: "kickoff" | "resume" | "auto-resume",
251
+ deps: CompletionDriverDeps,
252
+ ): Promise<boolean> {
253
+ const snapshotPath = kind === "auto-resume" ? deps.completionTestAutoContinuePromptPath() : deps.completionTestDriverPromptPath();
254
+ deps.maybeWriteTestSnapshot(snapshotPath, `${prompt}\n`);
255
+ noteQueuedDriverPrompt(rootKey, fingerprint);
256
+ if (deps.shouldSkipDriverKickoffForTests()) {
257
+ deps.emitCommandText(ctx, `Skipped completion workflow ${kind} prompt (test mode)`, "info");
258
+ return false;
259
+ }
260
+ pi.sendUserMessage(prompt);
261
+ deps.emitCommandText(ctx, `Queued completion workflow ${kind}`, "info");
262
+ return true;
263
+ }
264
+
265
+ export async function autoContinueWorkflowIfNeeded(
266
+ pi: ExtensionAPI,
267
+ ctx: { cwd: string; hasUI: boolean; ui: any },
268
+ deps: CompletionDriverDeps,
269
+ ): Promise<void> {
270
+ if (roleFromEnv()) return;
271
+ const snapshot = await loadCompletionSnapshot(deps.getCtxCwd(ctx));
272
+ const rootKey = deps.completionRootKey(snapshot, deps.getCtxCwd(ctx));
273
+ if (!snapshot) {
274
+ clearDriverContinuationTracker(rootKey);
275
+ return;
276
+ }
277
+ const fingerprint = completionContinuationFingerprint(snapshot);
278
+ if (!fingerprint) {
279
+ clearDriverContinuationTracker(rootKey);
280
+ return;
281
+ }
282
+ if (!isWorkflowDriverActive(snapshot) || deps.hasRunningCompletionRole(rootKey)) return;
283
+ const tracker = driverContinuationByRoot.get(rootKey);
284
+ if (tracker && tracker.fingerprint === fingerprint) {
285
+ if (tracker.inFlight) {
286
+ tracker.inFlight = false;
287
+ if (tracker.attempts >= DRIVER_AUTO_CONTINUE_MAX_ATTEMPTS) {
288
+ if (!isDriverContinuationStateParked(rootKey, fingerprint)) {
289
+ rememberParkedDriverContinuation(rootKey, fingerprint);
290
+ deps.emitCommandText(
291
+ ctx,
292
+ `Completion workflow is parked before mandatory role dispatch: ${asString(snapshot.state?.next_mandatory_role) ?? "(unknown)"}. Rerun /cook to continue from canonical state.`,
293
+ "warning",
294
+ );
295
+ }
296
+ return;
297
+ }
298
+ } else {
299
+ return;
300
+ }
301
+ }
302
+ const resumePrompt = deps.completionResumePrompt(currentTaskType(snapshot) ?? "(missing)", currentEvaluationProfile(snapshot) ?? "(missing)");
303
+ await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, resumePrompt, "auto-resume", deps);
304
+ }
305
+
306
+ async function assessActiveWorkflowProposalRouting(
307
+ ctx: DriverContext,
308
+ snapshot: CompletionStateSnapshot,
309
+ deps: CompletionDriverDeps,
310
+ ): Promise<ActiveWorkflowProposalAssessment> {
311
+ const currentMission = currentMissionAnchor(snapshot);
312
+ const projectName = path.basename(snapshot.files.root);
313
+ const proposal = await deps.deriveCookContextProposal(ctx, projectName);
314
+ if (!proposal) {
315
+ const assessment: ActiveWorkflowProposalAssessment = {
316
+ action: "unclear",
317
+ currentMissionAnchor: currentMission,
318
+ reason: "missing_proposal",
319
+ };
320
+ deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
321
+ return assessment;
322
+ }
323
+ if (deps.missionAnchorsLikelyEquivalent(currentMission, proposal.mission)) {
324
+ const assessment: ActiveWorkflowProposalAssessment = {
325
+ action: "continue",
326
+ currentMissionAnchor: currentMission,
327
+ proposal,
328
+ reason: "matching_mission",
329
+ };
330
+ deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
331
+ return assessment;
332
+ }
333
+ if (deps.shouldTreatBareActiveWorkflowProposalAsClearRefocus(proposal)) {
334
+ const assessment: ActiveWorkflowProposalAssessment = {
335
+ action: "refocus",
336
+ currentMissionAnchor: currentMission,
337
+ proposal,
338
+ reason: "clear_refocus",
339
+ };
340
+ deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
341
+ return assessment;
342
+ }
343
+ const assessment: ActiveWorkflowProposalAssessment = {
344
+ action: "unclear",
345
+ currentMissionAnchor: currentMission,
346
+ proposal,
347
+ reason: "ambiguous_discussion",
348
+ };
349
+ deps.maybeWriteActiveWorkflowRoutingSnapshot(assessment);
350
+ return assessment;
351
+ }
352
+
353
+ async function resumeActiveWorkflowFromCanonicalState(
354
+ pi: ExtensionAPI,
355
+ ctx: { cwd: string; hasUI: boolean; ui: any },
356
+ snapshot: CompletionStateSnapshot,
357
+ deps: CompletionDriverDeps,
358
+ ): Promise<void> {
359
+ const mission = currentMissionAnchor(snapshot);
360
+ pi.setSessionName(`completion: ${mission.slice(0, 60)}`);
361
+ const resumePrompt = deps.completionResumePrompt(currentTaskType(snapshot) ?? "(missing)", currentEvaluationProfile(snapshot) ?? "(missing)");
362
+ const rootKey = deps.completionRootKey(snapshot, deps.getCtxCwd(ctx));
363
+ const fingerprint = completionContinuationFingerprint(snapshot) ?? JSON.stringify({
364
+ kind: "resume",
365
+ mission_anchor: mission,
366
+ current_phase: asString(snapshot.state?.current_phase) ?? null,
367
+ next_mandatory_role: asString(snapshot.state?.next_mandatory_role) ?? null,
368
+ });
369
+ const resumeKind = deps.shouldTestAutoContinueOnSessionStart() && deps.completionTestAutoContinuePromptPath() ? "auto-resume" : "resume";
370
+ await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, resumePrompt, resumeKind, deps);
371
+ }
372
+
373
+ async function confirmExistingWorkflowProposal(
374
+ ctx: { hasUI: boolean; ui: any },
375
+ snapshot: CompletionStateSnapshot,
376
+ proposal: ContextProposal,
377
+ deps: CompletionDriverDeps,
378
+ options: ExistingWorkflowChooserOptions = {},
379
+ ): Promise<ExistingWorkflowDecision | undefined> {
380
+ const currentMission = currentMissionAnchor(snapshot);
381
+ const comparison = options.comparison ?? "semantic";
382
+ const missionsMatch =
383
+ comparison === "strict"
384
+ ? deps.missionAnchorsStrictlyEquivalent(currentMission, proposal.mission)
385
+ : deps.missionAnchorsLikelyEquivalent(currentMission, proposal.mission);
386
+ if (missionsMatch) {
387
+ return { action: "continue", currentMissionAnchor: currentMission };
388
+ }
389
+ const title = [
390
+ "Existing completion workflow found",
391
+ "",
392
+ options.intro ?? "A workflow is already in progress. Choose how /cook should proceed:",
393
+ "",
394
+ "Current mission",
395
+ currentMission,
396
+ "",
397
+ options.proposedMissionLabel ?? "New proposed mission",
398
+ proposal.mission,
399
+ ].join("\n");
400
+ const continueChoice = "Continue current workflow\n\nKeep the current mission and treat the new goal as extra direction only.";
401
+ const refocusChoice =
402
+ options.refocusChoiceLabel ??
403
+ "Abandon current workflow and start this new one\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.";
404
+ const cancelChoice = `Cancel\n\nKeep the current workflow unchanged. ${deps.mainChatRerunGuidance}`;
405
+ deps.maybeWriteTestSnapshot(
406
+ deps.completionTestExistingWorkflowChooserSnapshotPath(),
407
+ `${JSON.stringify({ title, choices: [continueChoice, refocusChoice, cancelChoice] }, null, 2)}\n`,
408
+ );
409
+ const actionOverride = deps.completionTestWorkflowActionOverride();
410
+ if (actionOverride === "continue") {
411
+ return { action: "continue", currentMissionAnchor: currentMission };
412
+ }
413
+ if (actionOverride === "refocus") {
414
+ return { action: "refocus", currentMissionAnchor: currentMission, missionAnchor: proposal.mission };
415
+ }
416
+ if (actionOverride === "cancel") return undefined;
417
+ if (!deps.getCtxHasUI(ctx)) {
418
+ return { action: "continue", currentMissionAnchor: currentMission };
419
+ }
420
+ const ui = deps.getCtxUi(ctx);
421
+ if (!ui) {
422
+ return { action: "continue", currentMissionAnchor: currentMission };
423
+ }
424
+ const choice = await ui.select(title, [continueChoice, refocusChoice, cancelChoice]);
425
+ if (!choice || choice === cancelChoice) return undefined;
426
+ if (choice === refocusChoice) {
427
+ return { action: "refocus", currentMissionAnchor: currentMission, missionAnchor: proposal.mission };
428
+ }
429
+ return { action: "continue", currentMissionAnchor: currentMission };
430
+ }
431
+
432
+ async function refocusCompletionMission(
433
+ snapshot: CompletionStateSnapshot,
434
+ missionAnchor: string,
435
+ rawGoal: string,
436
+ analysis: ContextProposalAnalysis | undefined,
437
+ deps: CompletionDriverDeps,
438
+ ): Promise<void> {
439
+ const requiredStopJudges = asNumber(snapshot.profile?.required_stop_judges) ?? 3;
440
+ const root = snapshot.files.root;
441
+ const routing = deps.finalizeContextProposalAnalysis(analysis, [rawGoal, missionAnchor]);
442
+ const docsSurfaces = asStringArray(snapshot.profile?.docs_surfaces);
443
+ const nextProfile = buildProfileRecord({
444
+ projectName: asString(snapshot.profile?.project_name) ?? path.basename(root),
445
+ requiredStopJudges,
446
+ priorityPolicyId: asString(snapshot.profile?.priority_policy_id) ?? "completion-default",
447
+ docsSurfaces: docsSurfaces.length > 0 ? docsSurfaces : await detectDocsSurfaces(root),
448
+ taskType: routing.taskType,
449
+ evaluationProfile: routing.evaluationProfile,
450
+ });
451
+ const nextState = {
452
+ ...defaultState(missionAnchor, {
453
+ taskType: routing.taskType,
454
+ evaluationProfile: routing.evaluationProfile,
455
+ continuationReason: deps.buildContextProposalContinuationReason("User refocused workflow via /cook:", rawGoal, routing),
456
+ }),
457
+ remaining_stop_judges: requiredStopJudges,
458
+ next_mandatory_action: "Reconcile canonical state from current repo truth for the refocused mission",
459
+ };
460
+ const nextPlan = {
461
+ ...defaultPlan(missionAnchor, { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile }),
462
+ plan_basis: "user_refocus",
463
+ };
464
+ const nextActive = defaultActiveSlice(missionAnchor, { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile });
465
+ await Promise.all([
466
+ fsp.writeFile(path.join(snapshot.files.agentDir, "mission.md"), buildMission(path.basename(root), missionAnchor), "utf8"),
467
+ writeJsonFile(snapshot.files.profilePath, nextProfile),
468
+ writeJsonFile(snapshot.files.statePath, nextState),
469
+ writeJsonFile(snapshot.files.planPath, nextPlan),
470
+ writeJsonFile(snapshot.files.activePath, nextActive),
471
+ writeJsonFile(snapshot.files.verificationEvidencePath, defaultVerificationEvidence()),
472
+ ]);
473
+ }
474
+
475
+ function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
476
+ return asString(snapshot?.state?.continuation_policy) === "done";
477
+ }
478
+
479
+ function buildMission(projectName: string, missionAnchor: string): string {
480
+ 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`;
481
+ }
482
+
483
+ export function registerCookCommand(pi: ExtensionAPI, deps: CompletionDriverDeps): void {
484
+ pi.registerCommand("cook", {
485
+ description: deps.cookCommandSpec.description,
486
+ handler: async (args, ctx) => {
487
+ if (args.trim().length > 0) {
488
+ deps.emitCommandText(ctx, deps.bareOnlyGuidance, "info");
489
+ return;
490
+ }
491
+ let goal: string | undefined;
492
+ const cwd = deps.getCtxCwd(ctx);
493
+ let snapshot = await loadCompletionSnapshot(cwd);
494
+ const workflowDone = isWorkflowDone(snapshot);
495
+ let kickoffIntent: "auto" | "continue" | "refocus" = "auto";
496
+ let kickoffMissionAnchor = snapshot ? currentMissionAnchor(snapshot) : undefined;
497
+ let kickoffAnalysis: ContextProposalAnalysis | undefined;
498
+
499
+ if (!snapshot) {
500
+ const root = findRepoRoot(cwd) ?? cwd;
501
+ const projectName = path.basename(root);
502
+ const proposal = await deps.deriveCookContextProposal(ctx, projectName);
503
+ if (!proposal) {
504
+ deps.emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(deps), "info");
505
+ return;
506
+ }
507
+ const decision = await deps.confirmContextProposal(ctx, proposal, {
508
+ title: "Start a completion workflow from the recent discussion?",
509
+ });
510
+ if (!decision) {
511
+ deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal", deps), "info");
512
+ return;
513
+ }
514
+ goal = decision.goalText;
515
+ kickoffMissionAnchor = decision.missionAnchor;
516
+ kickoffAnalysis = decision.analysis;
517
+ const startupRouting = deps.finalizeContextProposalAnalysis(kickoffAnalysis, [goal ?? kickoffMissionAnchor ?? projectName]);
518
+ const created = await deps.scaffoldCompletionFiles(root, kickoffMissionAnchor ?? projectName, {
519
+ analysis: startupRouting,
520
+ continuationReason: deps.buildContextProposalContinuationReason(
521
+ "User started workflow via /cook:",
522
+ goal ?? kickoffMissionAnchor ?? projectName,
523
+ startupRouting,
524
+ ),
525
+ });
526
+ deps.emitCommandText(
527
+ ctx,
528
+ `Initialized completion control plane in ${created.root}${created.created.length > 0 ? ` (${created.created.length} files created)` : ""}`,
529
+ "info",
530
+ );
531
+ snapshot = await loadCompletionSnapshot(root);
532
+ }
533
+ if (!snapshot) {
534
+ deps.emitCommandText(ctx, "Failed to load completion workflow state", "error");
535
+ return;
536
+ }
537
+ if (!goal) {
538
+ if (workflowDone) {
539
+ const projectName = path.basename(snapshot.files.root);
540
+ const proposal = await deps.deriveCookContextProposal(ctx, projectName);
541
+ if (!proposal) {
542
+ deps.emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(deps, "The previous completion workflow is already done."), "info");
543
+ return;
544
+ }
545
+ const decision = await deps.confirmContextProposal(ctx, proposal, {
546
+ title: "The previous completion workflow is done. Start the next workflow round from the recent discussion?",
547
+ });
548
+ if (!decision) {
549
+ deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled next workflow round proposal", deps), "info");
550
+ return;
551
+ }
552
+ goal = decision.goalText;
553
+ kickoffIntent = "refocus";
554
+ kickoffMissionAnchor = decision.missionAnchor;
555
+ await refocusCompletionMission(snapshot, decision.missionAnchor, decision.goalText, decision.analysis, deps);
556
+ snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
557
+ deps.emitCommandText(ctx, `Started a new completion workflow round from recent discussion: ${decision.missionAnchor}`, "info");
558
+ } else {
559
+ const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, deps);
560
+ if (assessment.action !== "refocus" || !assessment.proposal) {
561
+ await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
562
+ return;
563
+ }
564
+ const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, deps, {
565
+ intro: "Recent non-command discussion suggests a different workflow. Choose how /cook should proceed:",
566
+ proposedMissionLabel: "Proposed mission from recent discussion",
567
+ refocusChoiceLabel:
568
+ "Start new workflow from recent discussion\n\nReview the proposed replacement in a final Start/Cancel confirmation before /cook rewrites canonical workflow state.",
569
+ });
570
+ if (!decision) {
571
+ deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled existing workflow confirmation", deps), "info");
572
+ return;
573
+ }
574
+ if (decision.action === "continue") {
575
+ await resumeActiveWorkflowFromCanonicalState(pi, ctx, snapshot, deps);
576
+ return;
577
+ }
578
+ const proposalDecision = await deps.confirmContextProposal(ctx, assessment.proposal, {
579
+ title: "Start the replacement workflow from recent discussion?",
580
+ });
581
+ if (!proposalDecision) {
582
+ deps.emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal", deps), "info");
583
+ return;
584
+ }
585
+ goal = proposalDecision.goalText;
586
+ kickoffIntent = "refocus";
587
+ kickoffMissionAnchor = proposalDecision.missionAnchor;
588
+ await refocusCompletionMission(snapshot, proposalDecision.missionAnchor, proposalDecision.goalText, proposalDecision.analysis, deps);
589
+ snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
590
+ deps.emitCommandText(ctx, `Refocused completion mission from recent discussion to: ${proposalDecision.missionAnchor}`, "info");
591
+ }
592
+ }
593
+ kickoffMissionAnchor = kickoffMissionAnchor ?? currentMissionAnchor(snapshot);
594
+ const kickoffGoal = goal ?? kickoffMissionAnchor;
595
+ pi.setSessionName(`completion: ${kickoffMissionAnchor.slice(0, 60)}`);
596
+ const kickoffPrompt = deps.completionKickoff(
597
+ kickoffGoal,
598
+ currentTaskType(snapshot) ?? "(missing)",
599
+ currentEvaluationProfile(snapshot) ?? "(missing)",
600
+ kickoffIntent,
601
+ kickoffMissionAnchor,
602
+ );
603
+ const rootKey = deps.completionRootKey(snapshot, deps.getCtxCwd(ctx));
604
+ const fingerprint = completionContinuationFingerprint(snapshot) ?? JSON.stringify({
605
+ kind: "kickoff",
606
+ mission_anchor: kickoffMissionAnchor,
607
+ goal: kickoffGoal,
608
+ intent: kickoffIntent,
609
+ task_type: currentTaskType(snapshot) ?? "(missing)",
610
+ evaluation_profile: currentEvaluationProfile(snapshot) ?? "(missing)",
611
+ });
612
+ await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, kickoffPrompt, "kickoff", deps);
613
+ },
614
+ });
615
+ }