@linimin/pi-letscook 0.1.37 → 0.1.40

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.
@@ -467,7 +467,7 @@ type ActiveWorkflowProposalAssessment = {
467
467
  action: "continue" | "refocus" | "unclear";
468
468
  currentMissionAnchor: string;
469
469
  proposal?: ContextProposal;
470
- reason: "matching_mission" | "clear_refocus" | "explicit_goal" | "missing_proposal" | "ambiguous_discussion";
470
+ reason: "matching_mission" | "clear_refocus" | "missing_proposal" | "ambiguous_discussion";
471
471
  };
472
472
 
473
473
  type ExistingWorkflowChooserOptions = {
@@ -539,8 +539,10 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
539
539
  }
540
540
 
541
541
  const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
542
+ const COOK_INLINE_ARG_REJECTION_MESSAGE =
543
+ "Inline /cook arguments are no longer supported. Clarify the mission in the main chat and rerun bare /cook.";
542
544
  const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
543
- "Bare /cook failed closed because recent discussion did not contain a clear structured Mission/Scope/Constraints/Acceptance proposal. Add that structure in the main chat or, as a temporary compatibility shim, pass /cook <text>.";
545
+ "Bare /cook failed closed because recent discussion did not contain a clear execution-ready Mission/Scope/Constraints/Acceptance proposal for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun bare /cook.";
544
546
 
545
547
  function buildCookCancellationMessage(prefix: string): string {
546
548
  return `${prefix}. ${COOK_MAIN_CHAT_RERUN_GUIDANCE}`;
@@ -753,6 +755,94 @@ function matchContextProposalRoutingHint(
753
755
  return label === "tasktype" ? { field: "taskType", value } : { field: "evaluationProfile", value };
754
756
  }
755
757
 
758
+ const CONTEXT_PROPOSAL_GENERIC_PLANNING_MISSION_REGEX =
759
+ /(?:\b(?:start(?:ing)?|begin|continue|continu(?:e|ing)|resume|implement(?:ing)?|execute|execut(?:e|ing)|carry out|work on|ship|build(?:ing)?)\b.*\b(?:this|that|the|current|latest)\s+(?:plan|proposal|spec(?:ification)?|design(?: doc(?:ument)?)?|migration plan)\b|(?:開始|著手|繼續|继续|恢復|恢复)?(?:實作|实现|執行|执行|落地|完成)(?:這個|这个|此|該|该)?(?:方案|計畫|计划|提案|規劃|规划|設計|设计))/iu;
760
+ const CONTEXT_PROPOSAL_PLANNING_ONLY_DELIVERABLE_REGEX =
761
+ /(?:\b(?:write|draft|prepare|create|produce|share|deliver|document|review)\b.*\b(?:plan|spec(?:ification)?|design(?: doc(?:ument)?)?|migration plan|proposal)\b|(?:撰寫|撰写|編寫|编写|起草|準備|准备|產出|产出|整理|分享|交付|審查|审查).*(?:計畫|计划|規格|规格|設計文件|设计文档|提案|方案))/iu;
762
+ const CONTEXT_PROPOSAL_DOCS_ONLY_SIGNAL_REGEX = /(?:\b(?:docs? only|documentation only)\b|(?:只改文件|僅文件|仅文件))/iu;
763
+ const CONTEXT_PROPOSAL_NO_IMPLEMENTATION_SIGNAL_REGEX =
764
+ /(?:\b(?:no code(?: changes?)?|without code(?: changes?)?|do not implement|don't implement|planning only|proposal only|spec only|design[- ]doc only|no runtime changes?)\b|(?:不改(?:動)?代碼|不改代码|不要實作|不要实现|只規劃|只规划|僅規劃|仅规划|不改(?:動)?執行|不改运行))/iu;
765
+ const CONTEXT_PROPOSAL_IMPLEMENTATION_SOURCE_REGEX =
766
+ /(?:\b(?:normalize|fix|update|add|remove|restore|refactor|ship|support|wire|route|rewrite|replace|preserve|filter|separate|refresh|reroute|suppress|align|convert|reconcile|repair|correct|implement|build|land|block|allow|keep|edit(?:ing)?|document(?:ing)?|writ(?:e|ing))\b|(?:修正|修復|修复|更新|新增|移除|恢復|恢复|重構|重构|調整|调整|正規化|规范化|規範化|过滤|過濾|分離|分离|刷新|替換|替换|抑制|對齊|对齐|實作|实现|落地|修補|修补|阻止|允許|允许|轉換|转换|保留|保持))/iu;
767
+
768
+ function contextProposalBodyTexts(proposal: Pick<ContextProposal, "scope" | "constraints" | "acceptance">): string[] {
769
+ return [...proposal.scope, ...proposal.constraints, ...proposal.acceptance];
770
+ }
771
+
772
+ function isGenericPlanningMissionAnchor(text: string): boolean {
773
+ const normalized = normalizeMissionAnchorText(text);
774
+ if (!normalized) return false;
775
+ return CONTEXT_PROPOSAL_GENERIC_PLANNING_MISSION_REGEX.test(normalized);
776
+ }
777
+
778
+ function hasExplicitPlanningOnlyDeliverable(texts: string[]): boolean {
779
+ return texts.some((text) => CONTEXT_PROPOSAL_PLANNING_ONLY_DELIVERABLE_REGEX.test(normalizeProposalLine(text)));
780
+ }
781
+
782
+ function normalizeImplementationMissionSourceText(text: string): string {
783
+ const normalized = normalizeProposalLine(text);
784
+ if (!normalized) return "";
785
+ return normalized
786
+ .replace(new RegExp(`${CONTEXT_PROPOSAL_DOCS_ONLY_SIGNAL_REGEX.source}[\\s::;,/\\-]*`, "giu"), " ")
787
+ .replace(/\s+([,.;:!?])/g, "$1")
788
+ .replace(/\s+/g, " ")
789
+ .trim();
790
+ }
791
+
792
+ function hasClearNoImplementationSignal(texts: string[]): boolean {
793
+ return texts.some((text) => CONTEXT_PROPOSAL_NO_IMPLEMENTATION_SIGNAL_REGEX.test(normalizeProposalLine(text)));
794
+ }
795
+
796
+ function implementationMissionSourceCandidateText(text: string): string | undefined {
797
+ const normalized = normalizeImplementationMissionSourceText(text);
798
+ if (!normalized) return undefined;
799
+ if (hasExplicitPlanningOnlyDeliverable([normalized])) return undefined;
800
+ if (hasClearNoImplementationSignal([normalized])) return undefined;
801
+ if (!CONTEXT_PROPOSAL_IMPLEMENTATION_SOURCE_REGEX.test(normalized)) return undefined;
802
+ return normalized;
803
+ }
804
+
805
+ function pickImplementationMissionSource(proposal: Pick<ContextProposal, "scope" | "constraints" | "acceptance">): string | undefined {
806
+ for (const item of proposal.scope) {
807
+ const candidate = implementationMissionSourceCandidateText(item);
808
+ if (candidate) return candidate;
809
+ }
810
+ for (const item of proposal.acceptance) {
811
+ const candidate = implementationMissionSourceCandidateText(item);
812
+ if (candidate) return candidate;
813
+ }
814
+ return undefined;
815
+ }
816
+
817
+ function hasPlanningArtifactOnlyContext(
818
+ proposal: Pick<ContextProposal, "mission" | "scope" | "constraints" | "acceptance">,
819
+ ): boolean {
820
+ const texts = [proposal.mission, ...contextProposalBodyTexts(proposal)];
821
+ if (!hasExplicitPlanningOnlyDeliverable(texts)) return false;
822
+ return !pickImplementationMissionSource(proposal);
823
+ }
824
+
825
+ function finalizeContextProposal(proposal: ContextProposal, projectName: string): ContextProposal | undefined {
826
+ if (hasPlanningArtifactOnlyContext(proposal)) return undefined;
827
+ if (!isGenericPlanningMissionAnchor(proposal.mission)) return proposal;
828
+ const missionSource = pickImplementationMissionSource(proposal);
829
+ if (!missionSource) return undefined;
830
+ const nextMission = assessMissionAnchor(missionSource, projectName).derived;
831
+ const normalizedNextMission = normalizeMissionAnchorText(nextMission);
832
+ if (!normalizedNextMission || isWeakMissionAnchor(normalizedNextMission)) return undefined;
833
+ if (missionAnchorsStrictlyEquivalent(nextMission, proposal.mission)) return proposal;
834
+ return {
835
+ ...proposal,
836
+ mission: nextMission,
837
+ goalText: buildContextProposalGoalText({
838
+ mission: nextMission,
839
+ scope: proposal.scope,
840
+ constraints: proposal.constraints,
841
+ acceptance: proposal.acceptance,
842
+ }),
843
+ };
844
+ }
845
+
756
846
  const MISSION_SCOPE_FILTER_STOPWORDS = new Set([
757
847
  "a",
758
848
  "an",
@@ -887,19 +977,14 @@ function shouldTreatBareActiveWorkflowProposalAsClearRefocus(proposal: ContextPr
887
977
  );
888
978
  }
889
979
 
890
- function maybeWriteActiveWorkflowRoutingSnapshot(
891
- assessment: ActiveWorkflowProposalAssessment,
892
- options: { mode: "bare" | "explicit"; explicitGoal?: string },
893
- ): void {
980
+ function maybeWriteActiveWorkflowRoutingSnapshot(assessment: ActiveWorkflowProposalAssessment): void {
894
981
  const snapshotPath = completionTestActiveWorkflowRoutingSnapshotPath();
895
982
  if (!snapshotPath) return;
896
983
  maybeWriteTestSnapshot(
897
984
  snapshotPath,
898
985
  `${JSON.stringify(
899
986
  {
900
- mode: options.mode,
901
- explicitGoalProvided: Boolean(options.explicitGoal),
902
- explicitGoal: options.explicitGoal ?? null,
987
+ mode: "bare",
903
988
  action: assessment.action,
904
989
  reason: assessment.reason,
905
990
  currentMissionAnchor: assessment.currentMissionAnchor,
@@ -928,7 +1013,6 @@ const CONTEXT_PROPOSAL_ANALYST_SYSTEM_PROMPT = [
928
1013
  "risks must contain concrete failure modes or regressions that the later workflow should keep in view.",
929
1014
  "task_type and evaluation_profile should be candidate routing hints only; reuse the existing completion vocabulary when it clearly fits instead of inventing new schema names.",
930
1015
  "possible_noise should list discussion points that look stale, weakly related, or unsafe to promote into scope.",
931
- "When an explicit goal is provided, keep the mission anchored to that goal instead of replacing it with a broader or different mission.",
932
1016
  "When discussion is insufficient, prefer empty arrays and a low confidence value over invention.",
933
1017
  ].join(" ");
934
1018
 
@@ -948,12 +1032,9 @@ function collectRecentDiscussionEntries(ctx: { sessionManager: any }, limit = 8)
948
1032
  let text = "";
949
1033
  let role: RecentDiscussionEntry["role"] | undefined;
950
1034
  const messageRole = asString(message.role);
951
- if (messageRole === "user" || messageRole === "assistant" || messageRole === "custom") {
1035
+ if (messageRole === "user" || messageRole === "custom") {
952
1036
  text = extractTextFromMessageContent(message.content);
953
1037
  role = messageRole;
954
- } else if (messageRole === "branchSummary" || messageRole === "compactionSummary") {
955
- text = asString(message.summary) ?? "";
956
- role = "summary";
957
1038
  }
958
1039
  if (!text || !role) continue;
959
1040
  const trimmed = text.trim();
@@ -983,11 +1064,7 @@ function extractJsonObjectFromText(text: string): string | undefined {
983
1064
  return unfenced.slice(start, end + 1);
984
1065
  }
985
1066
 
986
- function parseContextProposalAnalystOutput(
987
- raw: string,
988
- projectName: string,
989
- explicitGoal?: string,
990
- ): ContextProposal | undefined {
1067
+ function parseContextProposalAnalystOutput(raw: string, projectName: string): ContextProposal | undefined {
991
1068
  const jsonText = extractJsonObjectFromText(raw);
992
1069
  if (!jsonText) return undefined;
993
1070
  let parsed: unknown;
@@ -997,8 +1074,7 @@ function parseContextProposalAnalystOutput(
997
1074
  return undefined;
998
1075
  }
999
1076
  if (!isRecord(parsed)) return undefined;
1000
- const explicit = explicitGoal ? parseContextProposal(explicitGoal, projectName) : undefined;
1001
- const missionSource = explicit?.mission ?? explicitGoal ?? asString(parsed.mission);
1077
+ const missionSource = asString(parsed.mission);
1002
1078
  if (!missionSource) return undefined;
1003
1079
  const assessment = assessMissionAnchor(missionSource, projectName);
1004
1080
  const normalizedMission = normalizeMissionAnchorText(missionSource);
@@ -1007,31 +1083,28 @@ function parseContextProposalAnalystOutput(
1007
1083
  const scope = uniqueProposalItems(asStringArray(parsed.scope));
1008
1084
  const constraints = uniqueProposalItems(asStringArray(parsed.constraints));
1009
1085
  const acceptance = uniqueProposalItems(asStringArray(parsed.acceptance));
1010
- const analysis = mergeContextProposalAnalysis(
1011
- [
1012
- explicit?.analysis,
1013
- buildContextProposalAnalysis({
1014
- taskType: parsed.task_type ?? parsed.taskType,
1015
- evaluationProfile: parsed.evaluation_profile ?? parsed.evaluationProfile,
1016
- critique: asStringArray(parsed.critique),
1017
- risks: asStringArray(parsed.risks ?? parsed.risk),
1018
- possibleNoise: asStringArray(parsed.possible_noise ?? parsed.possibleNoise),
1019
- hintTexts: [raw, mission, ...scope, ...constraints, ...acceptance],
1020
- }),
1021
- ],
1022
- [explicitGoal ?? "", raw, mission, ...scope, ...constraints, ...acceptance],
1023
- );
1086
+ const analysis = buildContextProposalAnalysis({
1087
+ taskType: parsed.task_type ?? parsed.taskType,
1088
+ evaluationProfile: parsed.evaluation_profile ?? parsed.evaluationProfile,
1089
+ critique: asStringArray(parsed.critique),
1090
+ risks: asStringArray(parsed.risks ?? parsed.risk),
1091
+ possibleNoise: asStringArray(parsed.possible_noise ?? parsed.possibleNoise),
1092
+ hintTexts: [raw, mission, ...scope, ...constraints, ...acceptance],
1093
+ });
1024
1094
  const goalText = buildContextProposalGoalText({ mission, scope, constraints, acceptance });
1025
- return {
1026
- mission,
1027
- scope,
1028
- constraints,
1029
- acceptance,
1030
- analysis,
1031
- goalText,
1032
- basisPreview: raw.replace(/\s+/g, " ").trim(),
1033
- source: "analyst",
1034
- };
1095
+ return finalizeContextProposal(
1096
+ {
1097
+ mission,
1098
+ scope,
1099
+ constraints,
1100
+ acceptance,
1101
+ analysis,
1102
+ goalText,
1103
+ basisPreview: raw.replace(/\s+/g, " ").trim(),
1104
+ source: "analyst",
1105
+ },
1106
+ projectName,
1107
+ );
1035
1108
  }
1036
1109
 
1037
1110
  function contextProposalAnalystModelArg(model: unknown): string | undefined {
@@ -1041,17 +1114,9 @@ function contextProposalAnalystModelArg(model: unknown): string | undefined {
1041
1114
  return provider && id ? `${provider}/${id}` : undefined;
1042
1115
  }
1043
1116
 
1044
- function buildContextProposalAnalystPrompt(projectName: string, recentEntries: RecentDiscussionEntry[], explicitGoal?: string): string {
1117
+ function buildContextProposalAnalystPrompt(projectName: string, recentEntries: RecentDiscussionEntry[]): string {
1045
1118
  const discussion = serializeRecentDiscussionEntries(recentEntries);
1046
- return [
1047
- `Project: ${projectName}`,
1048
- explicitGoal
1049
- ? `Explicit goal (keep this mission anchor):\n${explicitGoal}`
1050
- : "Explicit goal: none provided; infer the current mission from the discussion.",
1051
- "",
1052
- "Recent discussion:",
1053
- discussion,
1054
- ].join("\n");
1119
+ return [`Project: ${projectName}`, "Infer the current mission from the discussion.", "", "Recent discussion:", discussion].join("\n");
1055
1120
  }
1056
1121
 
1057
1122
  function contextProposalAnalystProgressLines(activity: LiveRoleActivity): string[] {
@@ -1080,14 +1145,13 @@ async function runContextProposalAnalystSubprocess(
1080
1145
  ctx: { cwd: string; hasUI: boolean; ui: any; model?: any },
1081
1146
  projectName: string,
1082
1147
  recentEntries: RecentDiscussionEntry[],
1083
- explicitGoal?: string,
1084
1148
  ): Promise<string | undefined> {
1085
1149
  const modelArg = contextProposalAnalystModelArg(ctx.model);
1086
1150
  if (!modelArg) return undefined;
1087
1151
  const cwd = getCtxCwd(ctx);
1088
1152
  const runCwd = findCompletionRoot(cwd) ?? findRepoRoot(cwd) ?? cwd;
1089
1153
  const rootKey = completionRootKey(undefined, cwd);
1090
- const prompt = buildContextProposalAnalystPrompt(projectName, recentEntries, explicitGoal);
1154
+ const prompt = buildContextProposalAnalystPrompt(projectName, recentEntries);
1091
1155
  const systemPromptTemp = await writeTempFile("pi-cook-proposal-analyst-", CONTEXT_PROPOSAL_ANALYST_SYSTEM_PROMPT);
1092
1156
  const analystRole = "cook-proposal-analyst";
1093
1157
  const args: string[] = ["--mode", "json", "-p", "--no-session", "--append-system-prompt", systemPromptTemp.filePath, "--model", modelArg, prompt];
@@ -1189,18 +1253,17 @@ async function analyzeContextProposalWithAgent(
1189
1253
  ctx: { cwd: string; hasUI: boolean; ui: any; model?: any; modelRegistry?: any },
1190
1254
  projectName: string,
1191
1255
  recentEntries: RecentDiscussionEntry[],
1192
- explicitGoal?: string,
1193
1256
  ): Promise<ContextProposal | undefined> {
1194
1257
  if (shouldDisableContextProposalAnalyst()) return undefined;
1195
1258
  const testOutput = completionTestContextProposalAnalystOutput();
1196
1259
  if (testOutput) {
1197
- return parseContextProposalAnalystOutput(testOutput, projectName, explicitGoal);
1260
+ return parseContextProposalAnalystOutput(testOutput, projectName);
1198
1261
  }
1199
1262
  if (recentEntries.length === 0) return undefined;
1200
1263
  try {
1201
- const raw = await runContextProposalAnalystSubprocess(ctx, projectName, recentEntries, explicitGoal);
1264
+ const raw = await runContextProposalAnalystSubprocess(ctx, projectName, recentEntries);
1202
1265
  if (!raw) return undefined;
1203
- return parseContextProposalAnalystOutput(raw, projectName, explicitGoal);
1266
+ return parseContextProposalAnalystOutput(raw, projectName);
1204
1267
  } catch (error) {
1205
1268
  console.warn("[completion] context proposal analyst failed", error);
1206
1269
  return undefined;
@@ -1562,16 +1625,19 @@ function parseContextProposal(text: string, projectName: string): ContextProposa
1562
1625
  hintTexts: [cleaned, mission, ...scope, ...constraints, ...acceptance, ...critique, ...risks],
1563
1626
  });
1564
1627
  const goalText = buildContextProposalGoalText({ mission, scope, constraints, acceptance });
1565
- return {
1566
- mission,
1567
- scope,
1568
- constraints,
1569
- acceptance,
1570
- analysis,
1571
- goalText,
1572
- basisPreview,
1573
- source: "session",
1574
- };
1628
+ return finalizeContextProposal(
1629
+ {
1630
+ mission,
1631
+ scope,
1632
+ constraints,
1633
+ acceptance,
1634
+ analysis,
1635
+ goalText,
1636
+ basisPreview,
1637
+ source: "session",
1638
+ },
1639
+ projectName,
1640
+ );
1575
1641
  }
1576
1642
 
1577
1643
  function hasStructuredContextProposalSignal(text: string): boolean {
@@ -1627,7 +1693,12 @@ function parseStrictStructuredSessionProposal(text: string, projectName: string)
1627
1693
 
1628
1694
  const proposal = parseContextProposal(cleaned, projectName);
1629
1695
  if (!proposal) return undefined;
1630
- if (normalizeMissionAnchorText(proposal.mission) !== distinctMissionAnchors[0]) return undefined;
1696
+ if (
1697
+ normalizeMissionAnchorText(proposal.mission) !== distinctMissionAnchors[0] &&
1698
+ !isGenericPlanningMissionAnchor(distinctMissionAnchors[0])
1699
+ ) {
1700
+ return undefined;
1701
+ }
1631
1702
  if (proposal.scope.length === 0 || proposal.constraints.length === 0 || proposal.acceptance.length === 0) return undefined;
1632
1703
  return { ...proposal, source: "session" };
1633
1704
  }
@@ -1648,58 +1719,17 @@ function extractContextProposalFromStructuredSession(
1648
1719
  async function extractContextProposalFromSession(
1649
1720
  ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
1650
1721
  projectName: string,
1651
- explicitGoal?: string,
1652
1722
  ): Promise<ContextProposal | undefined> {
1653
1723
  const recentEntries = collectRecentDiscussionEntries(ctx);
1654
- return (await analyzeContextProposalWithAgent(ctx, projectName, recentEntries, explicitGoal)) ??
1724
+ return (await analyzeContextProposalWithAgent(ctx, projectName, recentEntries)) ??
1655
1725
  extractContextProposalFromStructuredSession(recentEntries, projectName);
1656
1726
  }
1657
1727
 
1658
- async function buildGoalAnchoredContextProposal(
1659
- ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
1660
- goal: string,
1661
- projectName: string,
1662
- ): Promise<ContextProposal> {
1663
- const explicit = parseContextProposal(goal, projectName);
1664
- const sessionProposal = await extractContextProposalFromSession(ctx, projectName, goal);
1665
- const missionSource = explicit?.mission ?? goal;
1666
- const assessment = assessMissionAnchor(missionSource, projectName);
1667
- const mission = assessment.derived;
1668
- const mergeSessionBody = sessionProposal?.source === "analyst";
1669
- const explicitScope = explicit?.scope ?? [];
1670
- const sessionScope = mergeSessionBody
1671
- ? (sessionProposal?.scope ?? []).filter((item) => isSessionScopeItemMissionRelevant(item, mission))
1672
- : [];
1673
- const sessionConstraints = mergeSessionBody ? sessionProposal?.constraints ?? [] : [];
1674
- const sessionAcceptance = mergeSessionBody ? sessionProposal?.acceptance ?? [] : [];
1675
- const scope = uniqueProposalItems([...explicitScope, ...sessionScope]);
1676
- const constraints = uniqueProposalItems([...(explicit?.constraints ?? []), ...sessionConstraints]);
1677
- const acceptance = uniqueProposalItems([...(explicit?.acceptance ?? []), ...sessionAcceptance]);
1678
- const analysis = mergeContextProposalAnalysis(
1679
- [explicit?.analysis, sessionProposal?.analysis],
1680
- [goal, mission, ...(sessionProposal?.analysis.possibleNoise ?? []), ...scope, ...constraints, ...acceptance],
1681
- );
1682
- const goalText = buildContextProposalGoalText({ mission, scope, constraints, acceptance });
1683
- return {
1684
- mission,
1685
- scope,
1686
- constraints,
1687
- acceptance,
1688
- analysis,
1689
- goalText,
1690
- basisPreview: sessionProposal?.basisPreview ?? explicit?.basisPreview ?? goal,
1691
- source: sessionProposal?.source ?? explicit?.source ?? "session",
1692
- };
1693
- }
1694
-
1695
1728
  async function deriveCookContextProposal(
1696
1729
  ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
1697
1730
  projectName: string,
1698
- explicitGoal?: string,
1699
1731
  ): Promise<ContextProposal | undefined> {
1700
- return explicitGoal
1701
- ? await buildGoalAnchoredContextProposal(ctx, explicitGoal, projectName)
1702
- : await extractContextProposalFromSession(ctx, projectName);
1732
+ return await extractContextProposalFromSession(ctx, projectName);
1703
1733
  }
1704
1734
 
1705
1735
  async function confirmContextProposal(
@@ -1975,44 +2005,37 @@ function buildEvaluationRoleReminderText(snapshot: CompletionStateSnapshot, role
1975
2005
  async function assessActiveWorkflowProposalRouting(
1976
2006
  ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
1977
2007
  snapshot: CompletionStateSnapshot,
1978
- explicitGoal?: string,
1979
2008
  ): Promise<ActiveWorkflowProposalAssessment> {
1980
2009
  const currentMission = currentMissionAnchor(snapshot);
1981
2010
  const projectName = path.basename(snapshot.files.root);
1982
- const proposal = explicitGoal
1983
- ? await deriveCookContextProposal(ctx, projectName, explicitGoal)
1984
- : await deriveCookContextProposal(ctx, projectName);
1985
- const mode = explicitGoal ? "explicit" : "bare";
2011
+ const proposal = await deriveCookContextProposal(ctx, projectName);
1986
2012
  if (!proposal) {
1987
2013
  const assessment: ActiveWorkflowProposalAssessment = {
1988
2014
  action: "unclear",
1989
2015
  currentMissionAnchor: currentMission,
1990
2016
  reason: "missing_proposal",
1991
2017
  };
1992
- maybeWriteActiveWorkflowRoutingSnapshot(assessment, { mode, explicitGoal });
2018
+ maybeWriteActiveWorkflowRoutingSnapshot(assessment);
1993
2019
  return assessment;
1994
2020
  }
1995
- const missionsMatch = explicitGoal
1996
- ? missionAnchorsStrictlyEquivalent(currentMission, proposal.mission)
1997
- : missionAnchorsLikelyEquivalent(currentMission, proposal.mission);
1998
- if (missionsMatch) {
2021
+ if (missionAnchorsLikelyEquivalent(currentMission, proposal.mission)) {
1999
2022
  const assessment: ActiveWorkflowProposalAssessment = {
2000
2023
  action: "continue",
2001
2024
  currentMissionAnchor: currentMission,
2002
2025
  proposal,
2003
2026
  reason: "matching_mission",
2004
2027
  };
2005
- maybeWriteActiveWorkflowRoutingSnapshot(assessment, { mode, explicitGoal });
2028
+ maybeWriteActiveWorkflowRoutingSnapshot(assessment);
2006
2029
  return assessment;
2007
2030
  }
2008
- if (explicitGoal || shouldTreatBareActiveWorkflowProposalAsClearRefocus(proposal)) {
2031
+ if (shouldTreatBareActiveWorkflowProposalAsClearRefocus(proposal)) {
2009
2032
  const assessment: ActiveWorkflowProposalAssessment = {
2010
2033
  action: "refocus",
2011
2034
  currentMissionAnchor: currentMission,
2012
2035
  proposal,
2013
- reason: explicitGoal ? "explicit_goal" : "clear_refocus",
2036
+ reason: "clear_refocus",
2014
2037
  };
2015
- maybeWriteActiveWorkflowRoutingSnapshot(assessment, { mode, explicitGoal });
2038
+ maybeWriteActiveWorkflowRoutingSnapshot(assessment);
2016
2039
  return assessment;
2017
2040
  }
2018
2041
  const assessment: ActiveWorkflowProposalAssessment = {
@@ -2021,7 +2044,7 @@ async function assessActiveWorkflowProposalRouting(
2021
2044
  proposal,
2022
2045
  reason: "ambiguous_discussion",
2023
2046
  };
2024
- maybeWriteActiveWorkflowRoutingSnapshot(assessment, { mode, explicitGoal });
2047
+ maybeWriteActiveWorkflowRoutingSnapshot(assessment);
2025
2048
  return assessment;
2026
2049
  }
2027
2050
 
@@ -4050,11 +4073,14 @@ export default function completionExtension(pi: ExtensionAPI) {
4050
4073
  pi.registerCommand("cook", {
4051
4074
  description: "Discussion-driven /cook workflow: start, continue, refocus, or start the next round",
4052
4075
  handler: async (args, ctx) => {
4053
- const explicitGoal = args.trim();
4054
- let goal = explicitGoal;
4076
+ const inlineArgs = args.trim();
4077
+ if (inlineArgs) {
4078
+ emitCommandText(ctx, COOK_INLINE_ARG_REJECTION_MESSAGE, "info");
4079
+ return;
4080
+ }
4081
+ let goal: string | undefined;
4055
4082
  const cwd = getCtxCwd(ctx);
4056
4083
  let snapshot = await loadCompletionSnapshot(cwd);
4057
- const hadSnapshot = Boolean(snapshot);
4058
4084
  const workflowDone = isWorkflowDone(snapshot);
4059
4085
  let kickoffIntent: "auto" | "continue" | "refocus" = "auto";
4060
4086
  let kickoffMissionAnchor = snapshot ? currentMissionAnchor(snapshot) : undefined;
@@ -4063,40 +4089,21 @@ export default function completionExtension(pi: ExtensionAPI) {
4063
4089
  if (!snapshot) {
4064
4090
  const root = findRepoRoot(cwd) ?? cwd;
4065
4091
  const projectName = path.basename(root);
4066
- if (!goal) {
4067
- const proposal = await deriveCookContextProposal(ctx, projectName);
4068
- if (!proposal) {
4069
- emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(), "info");
4070
- return;
4071
- }
4072
- const decision = await confirmContextProposal(ctx, proposal, {
4073
- title: "Start a completion workflow from the recent discussion?",
4074
- });
4075
- if (!decision) {
4076
- emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal"), "info");
4077
- return;
4078
- }
4079
- goal = decision.goalText;
4080
- kickoffMissionAnchor = decision.missionAnchor;
4081
- kickoffAnalysis = decision.analysis;
4082
- } else {
4083
- const proposal = await deriveCookContextProposal(ctx, projectName, goal);
4084
- if (!proposal) {
4085
- emitCommandText(ctx, "Failed to derive a workflow startup proposal from this goal.", "error");
4086
- return;
4087
- }
4088
- const decision = await confirmContextProposal(ctx, proposal, {
4089
- title: "Start a completion workflow from this goal?",
4090
- nonInteractiveBehavior: "accept",
4091
- });
4092
- if (!decision) {
4093
- emitCommandText(ctx, buildCookCancellationMessage("Cancelled workflow startup proposal"), "info");
4094
- return;
4095
- }
4096
- goal = decision.goalText;
4097
- kickoffMissionAnchor = decision.missionAnchor;
4098
- kickoffAnalysis = decision.analysis;
4092
+ const proposal = await deriveCookContextProposal(ctx, projectName);
4093
+ if (!proposal) {
4094
+ emitCommandText(ctx, buildCookStructuredDiscussionFailureMessage(), "info");
4095
+ return;
4096
+ }
4097
+ const decision = await confirmContextProposal(ctx, proposal, {
4098
+ title: "Start a completion workflow from the recent discussion?",
4099
+ });
4100
+ if (!decision) {
4101
+ emitCommandText(ctx, buildCookCancellationMessage("Cancelled recent-discussion workflow proposal"), "info");
4102
+ return;
4099
4103
  }
4104
+ goal = decision.goalText;
4105
+ kickoffMissionAnchor = decision.missionAnchor;
4106
+ kickoffAnalysis = decision.analysis;
4100
4107
  const startupRouting = finalizeContextProposalAnalysis(kickoffAnalysis, [goal ?? kickoffMissionAnchor ?? projectName]);
4101
4108
  const created = await scaffoldCompletionFiles(root, kickoffMissionAnchor ?? projectName, {
4102
4109
  analysis: startupRouting,
@@ -4174,73 +4181,6 @@ export default function completionExtension(pi: ExtensionAPI) {
4174
4181
  }
4175
4182
  }
4176
4183
  kickoffMissionAnchor = kickoffMissionAnchor ?? currentMissionAnchor(snapshot);
4177
- if (hadSnapshot && explicitGoal) {
4178
- if (workflowDone) {
4179
- const projectName = path.basename(snapshot.files.root);
4180
- const proposal = await deriveCookContextProposal(ctx, projectName, goal);
4181
- if (!proposal) {
4182
- emitCommandText(ctx, "Failed to derive the next workflow round proposal from this goal.", "error");
4183
- return;
4184
- }
4185
- const decision = await confirmContextProposal(ctx, proposal, {
4186
- title: "Start the next workflow round from this goal?",
4187
- nonInteractiveBehavior: "accept",
4188
- });
4189
- if (!decision) {
4190
- emitCommandText(ctx, buildCookCancellationMessage("Cancelled next workflow round proposal"), "info");
4191
- return;
4192
- }
4193
- goal = decision.goalText;
4194
- kickoffIntent = "refocus";
4195
- kickoffMissionAnchor = decision.missionAnchor;
4196
- await refocusCompletionMission(snapshot, decision.missionAnchor, decision.goalText, decision.analysis);
4197
- snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
4198
- emitCommandText(ctx, `Started a new completion workflow round from explicit goal: ${decision.missionAnchor}`, "info");
4199
- } else {
4200
- const assessment = await assessActiveWorkflowProposalRouting(ctx, snapshot, goal);
4201
- if (!assessment.proposal) {
4202
- emitCommandText(ctx, "Failed to derive the replacement workflow proposal from this goal.", "error");
4203
- return;
4204
- }
4205
- if (assessment.action === "continue") {
4206
- kickoffIntent = "continue";
4207
- kickoffMissionAnchor = assessment.currentMissionAnchor;
4208
- if (normalizeMissionAnchorText(goal) !== normalizeMissionAnchorText(assessment.currentMissionAnchor)) {
4209
- emitCommandText(ctx, `Continuing existing workflow without changing mission anchor: ${assessment.currentMissionAnchor}`, "info");
4210
- }
4211
- } else {
4212
- const decision = await confirmExistingWorkflowProposal(ctx, snapshot, assessment.proposal, { comparison: "strict" });
4213
- if (!decision) {
4214
- emitCommandText(ctx, buildCookCancellationMessage("Cancelled existing workflow confirmation"), "info");
4215
- return;
4216
- }
4217
- kickoffIntent = decision.action;
4218
- kickoffMissionAnchor = decision.currentMissionAnchor;
4219
- if (decision.action === "refocus") {
4220
- const proposalDecision = await confirmContextProposal(ctx, assessment.proposal, {
4221
- title: "Start the replacement workflow from this goal?",
4222
- nonInteractiveBehavior: "accept",
4223
- });
4224
- if (!proposalDecision) {
4225
- emitCommandText(ctx, buildCookCancellationMessage("Cancelled replacement workflow proposal"), "info");
4226
- return;
4227
- }
4228
- goal = proposalDecision.goalText;
4229
- await refocusCompletionMission(
4230
- snapshot,
4231
- proposalDecision.missionAnchor,
4232
- proposalDecision.goalText,
4233
- proposalDecision.analysis,
4234
- );
4235
- snapshot = (await loadCompletionSnapshot(snapshot.files.root)) ?? snapshot;
4236
- kickoffMissionAnchor = proposalDecision.missionAnchor;
4237
- emitCommandText(ctx, `Refocused completion mission to: ${proposalDecision.missionAnchor}`, "info");
4238
- } else if (normalizeMissionAnchorText(goal) !== normalizeMissionAnchorText(decision.currentMissionAnchor)) {
4239
- emitCommandText(ctx, `Continuing existing workflow without changing mission anchor: ${decision.currentMissionAnchor}`, "info");
4240
- }
4241
- }
4242
- }
4243
- }
4244
4184
  pi.setSessionName(`completion: ${kickoffMissionAnchor.slice(0, 60)}`);
4245
4185
  const kickoffPrompt = completionKickoff(
4246
4186
  goal,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.37",
3
+ "version": "0.1.40",
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,