lee-spec-kit 0.7.0 → 0.7.1

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/README.en.md CHANGED
@@ -281,6 +281,7 @@ Use advanced selectors (`--component`, `--all`, `--done`) only when you need mul
281
281
  - `--ticket` is required for `--execute` only when the selected action has `requiresUserCheck=true`.
282
282
  - It is short-lived (5 minutes by default) and cannot be reused after one execution.
283
283
  - When you `--execute` a handoff-only command (`pre_pr_review_run`, `code_review_run`), the result is `approved_handoff_prepared` with `nextMainState` instead of the normal `approved_executed`.
284
+ - Treat `approved_handoff_prepared` as delegated-work-required: continue the delegated review/fix loop immediately, do not re-approve the same label, and refresh `context` only after the required evidence/state update.
284
285
 
285
286
  `context --json-compact` is the default recommended format, providing a minimal hot-path contract for agents.
286
287
  Use `context --json` only when full-detail debugging fields are required.
package/dist/index.js CHANGED
@@ -627,7 +627,7 @@ var koMessages = {
627
627
  prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (\uD655\uC778 \uD544\uC694)",
628
628
  prePrReviewFieldMissing: "tasks.md\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uC804 \uB9AC\uBDF0**: Pending | Running | Done` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
629
629
  prePrReviewRun: "\uCF54\uB4DC \uB9AC\uBDF0 \uC5D0\uC774\uC804\uD2B8\uB97C \uC2E4\uD589\uD574 `spec.md`/`plan.md`/`tasks.md` \uB300\uBE44 \uAD6C\uD604 \uC801\uD569\uC131\uC744 \uAC80\uD1A0\uD558\uACE0, `Summary`/`Feature Intent Summary`/`Implementation Fit`/`Missing Cases`/`Spec Alignment Checked`/`Finding Count`/`Blocking Findings`/`Findings`/`Residual Risks`\uAC00 \uD3EC\uD568\uB41C `review-trace.json`\uC744 \uC0DD\uC131\uD55C \uB4A4 `pre-pr-review`\uB85C \uB9AC\uBDF0 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. `pre-pr-review-run` \uC790\uCCB4\uB294 evidence\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC0C1\uD0DC\uB97C \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC73C\uBA70, \uD604\uC7AC evidence \uC815\uCC45\uC774 \uACBD\uB85C\uB97C \uC694\uAD6C\uD560 \uB54C\uB9CC `--evidence review-trace.json`\uC744 \uD568\uAED8 \uC0AC\uC6A9\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
630
- prePrReviewRunning: "Pre-PR \uB9AC\uBDF0 handoff\uAC00 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. delegated review\uB97C \uB9C8\uCE58\uACE0 `review-trace.json`\uC744 \uB9CC\uB4E0 \uB4A4 `pre-pr-review`\uB85C \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
630
+ prePrReviewRunning: "Pre-PR \uB9AC\uBDF0 handoff\uAC00 \uC774\uBBF8 \uC900\uBE44\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uAE30\uC874 delegated review\uB97C \uC7AC\uC0AC\uC6A9\uD558\uAC70\uB098 \uC774\uC5B4\uC11C \uC218\uD589\uD574 `review-trace.json`\uC744 \uB9CC\uB4E0 \uB4A4 `pre-pr-review`\uB85C \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. \uAC19\uC740 \uB77C\uBCA8\uC744 \uB2E4\uC2DC \uC2B9\uC778 \uB8E8\uD504\uB85C \uC5F4\uC9C0 \uB9C8\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
631
631
  prePrReviewEvidenceMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uD30C\uC77C \uACBD\uB85C\uC640 `Pre-PR Review Log`(\uB610\uB294 `PR \uC804 \uB9AC\uBDF0 \uB85C\uADF8`)\uC5D0 placeholder\uAC00 \uC544\uB2CC `Summary`/`Feature Intent Summary`/`Implementation Fit`/`Missing Cases`/`Spec Alignment Checked`/`Finding Count`/`Blocking Findings`/`Decision`/`Findings`(\uB610\uB294 \uBA85\uC2DC\uC801 `0 findings`)/`Residual Risks`\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
632
632
  prePrReviewDecisionMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uACB0\uC815 \uD615\uC2DD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `\uACB0\uC815: ...`(\uB610\uB294 `decision: ...`) \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
633
633
  prePrReviewFixRequired: "\uD604\uC7AC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{decision}`\uC785\uB2C8\uB2E4. PR \uC0DD\uC131 \uB2E8\uACC4\uB85C \uC774\uB3D9\uD558\uAE30 \uC804\uC5D0 pre-PR \uC9C0\uC801\uC0AC\uD56D\uC744 \uCF54\uB4DC\uC5D0 \uBC18\uC601\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
@@ -1183,7 +1183,7 @@ var enMessages = {
1183
1183
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
1184
1184
  prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Running | Done` and run context again. (CHECK required)",
1185
1185
  prePrReviewRun: "Run the code review agent, compare the implementation against `spec.md`/`plan.md`/`tasks.md`, and generate `review-trace.json` with `Summary`, `Feature Intent Summary`, `Implementation Fit`, `Missing Cases`, `Spec Alignment Checked`, `Finding Count`, `Blocking Findings`, `Findings`, and `Residual Risks`. Then record findings with `pre-pr-review`; `pre-pr-review-run` itself does not generate evidence or advance state, and you should use `--evidence review-trace.json` only when the active evidence policy requires a path. (CHECK required)",
1186
- prePrReviewRunning: "Pre-PR review handoff is already in progress. Finish the delegated review, generate `review-trace.json`, then record the result with `pre-pr-review`. (CHECK required)",
1186
+ prePrReviewRunning: "Pre-PR review handoff is already prepared. Reuse or resume the delegated review, generate `review-trace.json`, then record the result with `pre-pr-review`. Do not re-approve the same label. (CHECK required)",
1187
1187
  prePrReviewEvidenceMissing: "tasks.md `Pre-PR Evidence` is empty/invalid. Point to a real file and include a `Pre-PR Review Log` section with non-placeholder `Summary`, `Feature Intent Summary`, `Implementation Fit`, `Missing Cases`, `Spec Alignment Checked`, `Finding Count`, `Blocking Findings`, `Decision`, `Findings` (or explicit `0 findings`), and `Residual Risks`. (CHECK required)",
1188
1188
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1189
1189
  prePrReviewFixRequired: "Current `Pre-PR Decision` is `{decision}`. Apply the requested fixes from pre-PR findings before moving to PR creation. (CHECK required)",
@@ -1990,6 +1990,7 @@ In approval-waiting state:
1990
1990
  3. Do not paraphrase or omit these lines.
1991
1991
  4. Prefer \`approvalRequest.userFacingLines\` as the source for user-facing approval text.
1992
1992
  5. Prefer \`matchedFeature.currentSubstateOwner\` plus \`agentOrchestration.subAgentHandoff\` as the delegation SSOT. Treat \`currentActionShouldDelegate\` as a compatibility mirror for older consumers.
1993
+ 6. When \`matchedFeature.currentSubstateOwner="subagent"\` and \`agentOrchestration.subAgentHandoff.required=true\` with \`mode="command"\`, call \`spawn_agent\` first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, continue the delegated work immediately and do not re-open the same approval label.
1993
1994
 
1994
1995
  In non-approval state (progress updates, analysis, tool execution logs, unrelated Q&A):
1995
1996
 
@@ -2004,23 +2005,35 @@ If approval is still pending after answering an unrelated question:
2004
2005
  - \`actionOptions[*].approvalPrompt\` (label meaning included), and
2005
2006
  - \`approvalRequest.finalPrompt\` (format line).
2006
2007
  - Never output \`finalPrompt\` alone without the matching \`A: ...\` prompt.`;
2007
- function renderManagedBlock(lang, docsRepo) {
2008
+ function renderManagedSegment(lang, docsRepo) {
2008
2009
  return `${LEE_SPEC_KIT_AGENTS_BEGIN}
2009
2010
  ${CANONICAL_LEE_SPEC_KIT_AGENTS_TEXT}
2010
- ${LEE_SPEC_KIT_AGENTS_END}
2011
+ ${LEE_SPEC_KIT_AGENTS_END}`;
2012
+ }
2013
+ function renderManagedBlock(lang, docsRepo) {
2014
+ return `${renderManagedSegment()}
2011
2015
 
2012
2016
  `;
2013
2017
  }
2014
2018
  async function upsertLeeSpecKitAgentsMd(filePath, options) {
2015
2019
  const block = renderManagedBlock(options.lang, options.docsRepo);
2020
+ const segment = renderManagedSegment(options.lang, options.docsRepo);
2016
2021
  const exists = await fs.pathExists(filePath);
2017
2022
  if (!exists) {
2018
2023
  await fs.writeFile(filePath, block, "utf-8");
2019
2024
  return { changed: true, action: "created" };
2020
2025
  }
2021
2026
  const current = await fs.readFile(filePath, "utf-8");
2022
- if (current.includes(LEE_SPEC_KIT_AGENTS_BEGIN)) {
2023
- return { changed: false, action: "noop" };
2027
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_AGENTS_BEGIN);
2028
+ const endIndex = current.indexOf(LEE_SPEC_KIT_AGENTS_END);
2029
+ if (beginIndex !== -1 && endIndex !== -1 && beginIndex <= endIndex) {
2030
+ const replaceEnd = endIndex + LEE_SPEC_KIT_AGENTS_END.length;
2031
+ const next2 = `${current.slice(0, beginIndex)}${segment}${current.slice(replaceEnd)}`;
2032
+ if (next2 === current) {
2033
+ return { changed: false, action: "noop" };
2034
+ }
2035
+ await fs.writeFile(filePath, next2, "utf-8");
2036
+ return { changed: true, action: "updated" };
2024
2037
  }
2025
2038
  let next = current;
2026
2039
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
@@ -7491,7 +7504,7 @@ async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallb
7491
7504
  return fallbackSlug || fallbackFolderName;
7492
7505
  }
7493
7506
  function updateCommand(program2) {
7494
- program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "Cleanup legacy agents/skills copies (CLI-managed)").option("--templates", "Cleanup legacy feature-base copies (CLI-managed)").option(
7507
+ program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--agents-md", "Sync project-scoped AGENTS.md entrypoint").option("--skills", "Cleanup legacy agents/skills copies (CLI-managed)").option("--templates", "Cleanup legacy feature-base copies (CLI-managed)").option(
7495
7508
  "-f, --force",
7496
7509
  "Force overwrite even if docs has uncommitted changes"
7497
7510
  ).action(async (options) => {
@@ -7534,8 +7547,9 @@ async function runUpdate(options) {
7534
7547
  const docsLockPath = getDocsLockPath(docsDir);
7535
7548
  const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang, [docsLockPath]);
7536
7549
  const configBackfill = await backfillMissingConfigDefaults(docsDir);
7537
- const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
7550
+ const hasExplicitSelection = !!(options.agents || options.agentsMd || options.skills || options.templates);
7538
7551
  const updateAgents = options.agents || options.skills || !hasExplicitSelection;
7552
+ const updateAgentsMd = options.agentsMd || !hasExplicitSelection;
7539
7553
  const updateTemplates = options.templates || !hasExplicitSelection;
7540
7554
  const agentsMode = options.skills && !options.agents ? "skills" : "all";
7541
7555
  console.log(chalk8.blue(tr(lang, "cli", "update.start")));
@@ -7591,6 +7605,18 @@ async function runUpdate(options) {
7591
7605
  );
7592
7606
  }
7593
7607
  }
7608
+ if (updateAgentsMd) {
7609
+ const agentsMdTargets = await collectAgentsMdTargets(cwd, config);
7610
+ for (const target of agentsMdTargets) {
7611
+ const result = await upsertLeeSpecKitAgentsMd(target, {
7612
+ lang,
7613
+ docsRepo: config.docsRepo ?? "embedded"
7614
+ });
7615
+ if (result.changed) {
7616
+ updatedCount += 1;
7617
+ }
7618
+ }
7619
+ }
7594
7620
  if (updateTemplates) {
7595
7621
  console.log(chalk8.blue(tr(lang, "cli", "update.updatingFeatureBase")));
7596
7622
  console.log(chalk8.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
@@ -7625,6 +7651,42 @@ async function runUpdate(options) {
7625
7651
  { owner: "update" }
7626
7652
  );
7627
7653
  }
7654
+ function getGitTopLevelOrNull2(cwd) {
7655
+ try {
7656
+ const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
7657
+ cwd,
7658
+ encoding: "utf-8",
7659
+ stdio: ["ignore", "pipe", "ignore"]
7660
+ });
7661
+ const value = String(out || "").trim();
7662
+ return value ? value : null;
7663
+ } catch {
7664
+ return null;
7665
+ }
7666
+ }
7667
+ async function collectAgentsMdTargets(cwd, config) {
7668
+ if (!config) return [];
7669
+ const targets = /* @__PURE__ */ new Set();
7670
+ const docsRepo = config.docsRepo ?? "embedded";
7671
+ if (docsRepo === "embedded") {
7672
+ const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path12.resolve(config.docsDir, "..");
7673
+ targets.add(path12.join(repoRoot, "AGENTS.md"));
7674
+ return [...targets];
7675
+ }
7676
+ targets.add(path12.join(config.docsDir, "AGENTS.md"));
7677
+ const baseDir = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || process.cwd();
7678
+ const rawRoots = typeof config.projectRoot === "string" ? [config.projectRoot] : config.projectRoot && typeof config.projectRoot === "object" ? Object.values(config.projectRoot) : [];
7679
+ for (const rawRoot of rawRoots) {
7680
+ const value = String(rawRoot || "").trim();
7681
+ if (!value) continue;
7682
+ const resolved = path12.resolve(baseDir, value);
7683
+ if (!await fs.pathExists(resolved)) continue;
7684
+ const stat = await fs.stat(resolved);
7685
+ if (!stat.isDirectory()) continue;
7686
+ targets.add(path12.join(resolved, "AGENTS.md"));
7687
+ }
7688
+ return [...targets];
7689
+ }
7628
7690
  function isPlainObject(value) {
7629
7691
  return !!value && typeof value === "object" && !Array.isArray(value);
7630
7692
  }
@@ -8749,6 +8811,73 @@ function buildApprovalCommand(state, featureName, selectedComponent, execute) {
8749
8811
  }
8750
8812
  return `npx lee-spec-kit context ${featureRef}${componentArg} --approve <LABEL>`;
8751
8813
  }
8814
+ function buildFeatureComponentArgs(feature) {
8815
+ return feature.type && feature.type !== "single" ? ["--component", feature.type] : [];
8816
+ }
8817
+ function buildPrePrRecordCommand(feature, decision) {
8818
+ const args = [
8819
+ "pre-pr-review",
8820
+ feature.folderName,
8821
+ ...buildFeatureComponentArgs(feature),
8822
+ "--evidence",
8823
+ "review-trace.json",
8824
+ "--decision",
8825
+ decision
8826
+ ];
8827
+ return `npx lee-spec-kit ${args.join(" ")}`;
8828
+ }
8829
+ function buildDelegatedActionContract(state) {
8830
+ const feature = state.matchedFeature;
8831
+ const substateId = feature?.currentSubstateId;
8832
+ if (!feature || !substateId) return null;
8833
+ if (substateId === "pre_pr_review_run" || substateId === "pre_pr_review_running") {
8834
+ return {
8835
+ required: true,
8836
+ mode: "command",
8837
+ category: "pre_pr_review_run",
8838
+ currentSubstateId: substateId,
8839
+ delegatedWorkRequired: true,
8840
+ handoffOnly: true,
8841
+ advancesWorkflow: false,
8842
+ doNotReapproveSameLabel: substateId === "pre_pr_review_running",
8843
+ nextMainState: "pre_pr_review_running",
8844
+ reuseKey: `pre-pr:${feature.folderName}`,
8845
+ evidenceFile: "review-trace.json",
8846
+ nextStepRequirement: "generate_review_trace_then_record",
8847
+ recordCommands: {
8848
+ changesRequested: buildPrePrRecordCommand(
8849
+ feature,
8850
+ "changes_requested"
8851
+ ),
8852
+ approve: buildPrePrRecordCommand(feature, "approve")
8853
+ },
8854
+ guidance: substateId === "pre_pr_review_running" ? "A pre-PR review handoff is already prepared. Reuse or resume the delegated review, generate review-trace.json, then record the result with pre-pr-review. Do not re-approve the same label." : "After approval, spawn_agent first and hand off the delegated pre-PR review. Handoff-only execution only prepares the review session; continue the delegated review immediately after approval."
8855
+ };
8856
+ }
8857
+ if (substateId === "code_review_run" || substateId === "code_review_running") {
8858
+ return {
8859
+ required: true,
8860
+ mode: "command",
8861
+ category: "code_review_run",
8862
+ currentSubstateId: substateId,
8863
+ delegatedWorkRequired: true,
8864
+ handoffOnly: true,
8865
+ advancesWorkflow: false,
8866
+ doNotReapproveSameLabel: substateId === "code_review_running",
8867
+ nextMainState: "code_review_running",
8868
+ reuseKey: `code-review:${feature.folderName}`,
8869
+ guidance: substateId === "code_review_running" ? "A PR review handoff is already prepared. Reuse or resume the delegated review-fix work, then refresh PR Review Evidence and PR Review Decision before continuing. Do not re-approve the same label." : "After approval, spawn_agent first and hand off the delegated PR review-fix work. Handoff-only execution only prepares the delegated session; continue the delegated work immediately after approval."
8870
+ };
8871
+ }
8872
+ return null;
8873
+ }
8874
+ function buildDelegatedApprovalGuidance(handoffRequired, handoffMode) {
8875
+ const base = "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`).";
8876
+ const delegatedCommand = handoffRequired && handoffMode === "command" ? ' When `matchedFeature.currentSubstateOwner="subagent"` and `agentOrchestration.subAgentHandoff.required=true` with `mode="command"`, call spawn_agent first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, `--execute` only prepares the handoff; continue the delegated work immediately and do not re-approve the same label.' : "";
8877
+ const nonDelegated = " For non-delegated command actions, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed.";
8878
+ const orchestration = ' Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.';
8879
+ return `${base}${delegatedCommand}${nonDelegated}${orchestration}`;
8880
+ }
8752
8881
  function buildFinalApprovalPrompt(lang, actionOptions) {
8753
8882
  if (actionOptions.length === 0) return "";
8754
8883
  const labels = listLabels(actionOptions);
@@ -9358,6 +9487,28 @@ function getCommandExecutionLockPath(action, config) {
9358
9487
  }
9359
9488
  return getProjectExecutionLockPath(action.cwd);
9360
9489
  }
9490
+ function buildApprovedHandoffMetadata(action, featureRef) {
9491
+ if (action.category === "pre_pr_review_run") {
9492
+ return {
9493
+ delegatedWorkRequired: true,
9494
+ doNotReapproveSameLabel: true,
9495
+ reuseKey: `pre-pr:${featureRef}`,
9496
+ nextStepRequirement: "generate_review_trace_then_record",
9497
+ evidenceFile: "review-trace.json"
9498
+ };
9499
+ }
9500
+ if (action.category === "code_review_run") {
9501
+ return {
9502
+ delegatedWorkRequired: true,
9503
+ doNotReapproveSameLabel: true,
9504
+ reuseKey: `code-review:${featureRef}`
9505
+ };
9506
+ }
9507
+ return {
9508
+ delegatedWorkRequired: true,
9509
+ doNotReapproveSameLabel: true
9510
+ };
9511
+ }
9361
9512
  async function runApprovedOption(state, config, lang, featureName, selectionOptions, options) {
9362
9513
  const approval = options.approve || "";
9363
9514
  const ticketToken = (options.ticket || "").trim();
@@ -9588,6 +9739,10 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9588
9739
  );
9589
9740
  if (jsonMode) {
9590
9741
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9742
+ const handoffMetadata = buildApprovedHandoffMetadata(
9743
+ selectedAction,
9744
+ freshState.matchedFeature?.folderName ?? featureRef
9745
+ );
9591
9746
  console.log(
9592
9747
  JSON.stringify(
9593
9748
  {
@@ -9602,6 +9757,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9602
9757
  handoffOnly: true,
9603
9758
  advancesWorkflow: false,
9604
9759
  nextMainState: executionMetadata.nextMainState,
9760
+ ...handoffMetadata,
9605
9761
  stdout: execResult.stdout?.trim() || void 0,
9606
9762
  stderr: execResult.stderr?.trim() || void 0
9607
9763
  },
@@ -9782,6 +9938,11 @@ async function runContext(featureName, options) {
9782
9938
  state.matchedFeature?.folderName || null,
9783
9939
  state.matchedFeature?.currentSubstateOwner
9784
9940
  );
9941
+ const delegatedAction = buildDelegatedActionContract(state);
9942
+ const approvalGuidance = buildDelegatedApprovalGuidance(
9943
+ agentOrchestration.subAgentHandoff.required,
9944
+ agentOrchestration.subAgentHandoff.mode
9945
+ );
9785
9946
  if (options.approve || options.execute) {
9786
9947
  await runApprovedOption(
9787
9948
  state,
@@ -9840,6 +10001,7 @@ async function runContext(featureName, options) {
9840
10001
  agentOrchestration: {
9841
10002
  subAgentHandoff: agentOrchestration.subAgentHandoff
9842
10003
  },
10004
+ delegatedAction,
9843
10005
  autoRun: {
9844
10006
  available: autoRunPlan.available,
9845
10007
  policyEligible: autoRunPlan.policyEligible,
@@ -9975,13 +10137,14 @@ async function runContext(featureName, options) {
9975
10137
  "actionOptions[].detail",
9976
10138
  "actionOptions[].approvalPrompt"
9977
10139
  ] : [],
9978
- recommendation: 'Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`). For command execution, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed. Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.',
10140
+ recommendation: approvalGuidance,
9979
10141
  oneApprovalPerAction: approvalRequired,
9980
10142
  requireFreshContext: true,
9981
10143
  contextVersion: state.contextVersion,
9982
10144
  config: config.approval ?? { mode: "builtin" }
9983
10145
  },
9984
10146
  agentOrchestration,
10147
+ delegatedAction,
9985
10148
  autoRun: {
9986
10149
  available: autoRunPlan.available,
9987
10150
  policyEligible: autoRunPlan.policyEligible,
@@ -9995,7 +10158,10 @@ async function runContext(featureName, options) {
9995
10158
  guidance: 'Use auto-run only when `autoRun.available=true`. If `autoRun.policyEligible=true` but `autoRun.executableNow=false`, resolve `autoRun.manualBoundary` first. Do not treat `autoRun.available` alone as a delegation trigger; use `agentOrchestration.subAgentHandoff.required` + `mode="auto_run"` for actual delegation. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories.'
9996
10159
  },
9997
10160
  approvalRequest: {
9998
- guidance: 'User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`. Do not expose `requiredDocs`, `checkPolicy`, or raw `cmd` unless explicitly requested. For approved command actions, prefer one-shot `flow --approve <LABEL> --execute`. Keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.',
10161
+ guidance: approvalGuidance.replace(
10162
+ "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user.",
10163
+ "User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`."
10164
+ ),
9999
10165
  required: approvalRequired,
10000
10166
  finalPrompt: finalApprovalPrompt,
10001
10167
  userFacingLines: approvalUserFacingLines,
@@ -11403,6 +11569,7 @@ function toCompactFlowContextSnapshot(state) {
11403
11569
  actionOptions: state.actionOptions.map(
11404
11570
  (option) => toCompactFlowActionOption(option)
11405
11571
  ),
11572
+ delegatedAction: buildDelegatedActionContract(state),
11406
11573
  primaryActionLabel: primaryAction?.label ?? null,
11407
11574
  primaryActionType: primaryAction?.action.type ?? null,
11408
11575
  primaryActionCategory: primaryAction?.action.category ?? null,
@@ -16183,6 +16350,7 @@ async function runPrePrReviewRun(featureName, options) {
16183
16350
  suggestedParallelism: 1,
16184
16351
  fallbackToMainAgentWhenQuotaExceeded: true,
16185
16352
  nextStepRequirement: "generate_review_trace_then_record",
16353
+ delegatedWorkRequired: true,
16186
16354
  nextMainState: "pre_pr_review_running",
16187
16355
  evidenceFile: "review-trace.json",
16188
16356
  tasksUpdated,
@@ -16220,6 +16388,9 @@ async function runPrePrReviewRun(featureName, options) {
16220
16388
  console.log(`Suggested parallelism: 1`);
16221
16389
  console.log(`Next main state: pre_pr_review_running`);
16222
16390
  console.log(`Evidence file: review-trace.json`);
16391
+ console.log(
16392
+ config.lang === "ko" ? "Next required: delegated review\uB97C \uC774\uC5B4\uC11C \uC218\uD589\uD558\uACE0 review-trace.json\uC744 \uB9CC\uB4E0 \uB4A4 pre-pr-review\uB85C \uAE30\uB85D" : "Next required: continue the delegated review, generate review-trace.json, then record the result with pre-pr-review"
16393
+ );
16223
16394
  if (tasksUpdated) {
16224
16395
  console.log(`tasks.md updated: ${tasksPath}`);
16225
16396
  console.log(