lee-spec-kit 0.7.1 → 0.7.3

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.
Files changed (33) hide show
  1. package/dist/index.js +1172 -595
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/en/common/README.md +13 -0
  5. package/templates/en/common/agents/agents.md +5 -3
  6. package/templates/en/common/agents/issue-template.md +1 -5
  7. package/templates/en/common/agents/pr-template.md +2 -11
  8. package/templates/en/common/agents/skills/create-feature.md +3 -0
  9. package/templates/en/common/agents/skills/create-issue.md +4 -7
  10. package/templates/en/common/agents/skills/create-pr.md +4 -8
  11. package/templates/en/common/agents/skills/execute-task.md +3 -0
  12. package/templates/en/common/features/README.md +25 -0
  13. package/templates/en/common/features/feature-base/decisions.md +1 -0
  14. package/templates/en/common/features/feature-base/plan.md +1 -0
  15. package/templates/en/common/features/feature-base/spec.md +1 -0
  16. package/templates/en/common/features/feature-base/tasks.md +5 -0
  17. package/templates/en/common/ideas/README.md +14 -13
  18. package/templates/en/common/ideas/idea.md +36 -0
  19. package/templates/ko/common/README.md +13 -0
  20. package/templates/ko/common/agents/agents.md +5 -3
  21. package/templates/ko/common/agents/issue-template.md +1 -5
  22. package/templates/ko/common/agents/pr-template.md +2 -11
  23. package/templates/ko/common/agents/skills/create-feature.md +3 -0
  24. package/templates/ko/common/agents/skills/create-issue.md +4 -7
  25. package/templates/ko/common/agents/skills/create-pr.md +4 -8
  26. package/templates/ko/common/agents/skills/execute-task.md +4 -0
  27. package/templates/ko/common/features/README.md +25 -0
  28. package/templates/ko/common/features/feature-base/decisions.md +1 -0
  29. package/templates/ko/common/features/feature-base/plan.md +1 -0
  30. package/templates/ko/common/features/feature-base/spec.md +1 -0
  31. package/templates/ko/common/features/feature-base/tasks.md +5 -0
  32. package/templates/ko/common/ideas/README.md +14 -13
  33. package/templates/ko/common/ideas/idea.md +36 -0
package/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import path12 from 'path';
2
+ import path14 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { program } from 'commander';
5
5
  import fs from 'fs-extra';
6
6
  import prompts from 'prompts';
7
- import chalk8 from 'chalk';
7
+ import chalk9 from 'chalk';
8
8
  import { spawn, spawnSync, execFileSync, execSync } from 'child_process';
9
9
  import os from 'os';
10
10
  import { createHash, randomUUID } from 'crypto';
11
- import fs9 from 'fs';
11
+ import fs11 from 'fs';
12
12
  import { Buffer as Buffer$1 } from 'buffer';
13
13
 
14
14
  var getFilename = () => fileURLToPath(import.meta.url);
15
- var getDirname = () => path12.dirname(getFilename());
15
+ var getDirname = () => path14.dirname(getFilename());
16
16
  var __dirname$1 = /* @__PURE__ */ getDirname();
17
17
  async function walkFiles(fsAdapter, rootDir, options = {}) {
18
18
  const out = [];
@@ -25,7 +25,7 @@ async function walkFiles(fsAdapter, rootDir, options = {}) {
25
25
  async function visit(current) {
26
26
  const entries = await fsAdapter.readdir(current);
27
27
  for (const entryName of entries) {
28
- const absolute = path12.join(current, entryName);
28
+ const absolute = path14.join(current, entryName);
29
29
  const stat = await fsAdapter.stat(absolute);
30
30
  if (stat.isDirectory()) {
31
31
  if (ignored.has(entryName.trim().toLowerCase())) continue;
@@ -34,7 +34,7 @@ async function walkFiles(fsAdapter, rootDir, options = {}) {
34
34
  }
35
35
  if (!stat.isFile()) continue;
36
36
  if (normalizedExtensions.size > 0) {
37
- const ext = path12.extname(entryName).toLowerCase();
37
+ const ext = path14.extname(entryName).toLowerCase();
38
38
  if (!normalizedExtensions.has(ext)) continue;
39
39
  }
40
40
  out.push(absolute);
@@ -50,7 +50,7 @@ async function listSubdirectories(fsAdapter, rootDir) {
50
50
  const entries = await fsAdapter.readdir(rootDir);
51
51
  const dirs = [];
52
52
  for (const entryName of entries) {
53
- const absolute = path12.join(rootDir, entryName);
53
+ const absolute = path14.join(rootDir, entryName);
54
54
  const stat = await fsAdapter.stat(absolute);
55
55
  if (stat.isDirectory()) {
56
56
  dirs.push(absolute);
@@ -151,10 +151,10 @@ var DefaultFileSystemAdapter = class {
151
151
  }
152
152
  };
153
153
  var __filename2 = fileURLToPath(import.meta.url);
154
- var __dirname2 = path12.dirname(__filename2);
154
+ var __dirname2 = path14.dirname(__filename2);
155
155
  function getTemplatesDir() {
156
- const rootDir = path12.resolve(__dirname2, "..");
157
- return path12.join(rootDir, "templates");
156
+ const rootDir = path14.resolve(__dirname2, "..");
157
+ return path14.join(rootDir, "templates");
158
158
  }
159
159
 
160
160
  // src/utils/locales/ko/cli.ts
@@ -175,6 +175,8 @@ var koCli = {
175
175
  "feature.nextSteps1": " 1. {path}/spec.md \uC791\uC131",
176
176
  "feature.nextSteps2": " 2. \uC0AC\uC6A9\uC790 \uB9AC\uBDF0 \uC694\uCCAD",
177
177
  "feature.nextSteps3": " 3. \uC2B9\uC778 \uD6C4 plan.md \uC791\uC131",
178
+ "feature.ideaNotFound": "Idea \uBB38\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {ref}",
179
+ "feature.ideaAmbiguous": "{ref}\uC640 \uB9E4\uCE6D\uB418\uB294 Idea \uBB38\uC11C\uAC00 \uC5EC\uB7EC \uAC1C\uC785\uB2C8\uB2E4. \uC815\uD655\uD55C \uACBD\uB85C\uB098 \uC804\uCCB4 indexed \uC774\uB984\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
178
180
  "config.currentTitle": "\u{1F4CB} \uD604\uC7AC \uC124\uC815:",
179
181
  "config.pathLabel": "\uACBD\uB85C",
180
182
  "config.projectRootStandaloneOnly": "\u26A0\uFE0F projectRoot\uB294 standalone \uBAA8\uB4DC\uC5D0\uC11C\uB9CC \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.",
@@ -268,6 +270,13 @@ var koCli = {
268
270
  "init.log.gitInitialCommitDone": "\u2705 Git \uCD08\uAE30 \uCEE4\uBC0B \uC644\uB8CC!",
269
271
  "init.warn.skipGitInit": "\u26A0\uFE0F Git \uCD08\uAE30\uD654\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4 (\uC218\uB3D9\uC73C\uB85C \uCEE4\uBC0B\uD574\uC8FC\uC138\uC694)",
270
272
  "init.error.templateNotFound": "\uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
273
+ "idea.fileExists": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 Idea \uBB38\uC11C\uC785\uB2C8\uB2E4: {path}",
274
+ "idea.templateNotFound": "CLI \uB0B4\uC7A5 idea \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
275
+ "idea.created": "\u2705 Idea \uBB38\uC11C \uC0DD\uC131 \uC644\uB8CC: {path}",
276
+ "idea.nextStepsTitle": "\uB2E4\uC74C \uB2E8\uACC4:",
277
+ "idea.nextSteps1": " 1. \uBC94\uC704, PRD Refs, \uC2B9\uACA9 \uBA54\uBAA8\uB97C \uC791\uC131",
278
+ "idea.nextSteps2": " 2. Feature\uB85C \uC2B9\uACA9: npx lee-spec-kit feature <name> --idea {ideaId}",
279
+ "idea.nextSteps3": " 3. Feature\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uC744 \uACBD\uC6B0 Dropped\uB85C \uD45C\uC2DC",
271
280
  "github.cmdGithubDescription": "GitHub \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uB3C4\uC6B0\uBBF8 (issue/pr \uBCF8\uBB38 \uD15C\uD50C\uB9BF \uC0DD\uC131, \uAC80\uC99D, merge \uC7AC\uC2DC\uB3C4)",
272
281
  "github.cmdIssueDescription": "feature \uBB38\uC11C \uAE30\uBC18 GitHub issue \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131",
273
282
  "github.cmdPrDescription": "GitHub PR \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131 + tasks \uB3D9\uAE30\uD654 + merge \uC7AC\uC2DC\uB3C4",
@@ -434,11 +443,15 @@ var koCli = {
434
443
  "validation.workflowModeInvalid": "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uBAA8\uB4DC\uB294 {values} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
435
444
  "validation.featureIdEmpty": "Feature ID\uB294 \uBE44\uC5B4\uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
436
445
  "validation.featureIdFormat": "Feature ID\uB294 'F' + \uC22B\uC790 \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 (\uC608: F001).",
446
+ "validation.ideaIdEmpty": "Idea ID\uB294 \uBE44\uC5B4\uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
447
+ "validation.ideaIdFormat": "Idea ID\uB294 'I' + \uC22B\uC790 \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 (\uC608: I001).",
437
448
  "validation.pathEmpty": "\uACBD\uB85C\uB294 \uBE44\uC5B4\uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
438
449
  "validation.pathNullByte": "\uACBD\uB85C\uC5D0 null \uBB38\uC790\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
439
450
  "validation.genericFailed": "\uAC80\uC99D \uC2E4\uD328",
440
451
  "validation.context.featureName": "\uAE30\uB2A5 \uC774\uB984",
441
452
  "validation.context.featureId": "Feature ID",
453
+ "validation.context.ideaName": "Idea \uC774\uB984",
454
+ "validation.context.ideaId": "Idea ID",
442
455
  "validation.context.projectName": "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984",
443
456
  "validation.context.projectType": "\uD504\uB85C\uC81D\uD2B8 \uD0C0\uC785",
444
457
  "validation.context.language": "\uC5B8\uC5B4",
@@ -626,9 +639,9 @@ var koMessages = {
626
639
  taskCommitGateReasonMismatchLastDone: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B\uC774 \uC9C1\uC804 \uC644\uB8CC \uD0DC\uC2A4\uD06C\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
627
640
  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
641
  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
- 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 \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
- 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)",
642
+ prePrReviewRun: "Pre-PR \uB9AC\uBDF0 handoff\uB97C \uC900\uBE44\uD55C \uB4A4 \uC2E4\uC81C \uB9AC\uBDF0\uB97C \uC774\uC5B4\uC11C \uC218\uD589\uD558\uC138\uC694. `review-trace.json`\uC5D0\uB294 `baseSha`, `headSha`, `changedFiles`, `reviewedFiles`, `riskSummaries`, `Approval Rationale`, \uD30C\uC77C\uBCC4 findings, `Residual Risks`\uB97C \uAD6C\uC870\uD654\uD574 \uB123\uACE0, \uC774\uD6C4 `pre-pr-review`\uB85C \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. `pre-pr-review-run` \uC790\uCCB4\uB294 \uB9AC\uBDF0 \uC644\uB8CC\uB098 \uC0C1\uD0DC \uC804\uC774\uB97C \uC758\uBBF8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (\uD655\uC778 \uD544\uC694)",
643
+ prePrReviewRunning: "Pre-PR \uB9AC\uBDF0\uAC00 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. \uAE30\uC874 delegated review\uB97C \uC7AC\uC0AC\uC6A9\uD558\uAC70\uB098 \uC774\uC5B4\uC11C \uC218\uD589\uD574 \uAD6C\uC870\uD654\uB41C `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)",
644
+ prePrReviewEvidenceMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. repo \uB8E8\uD2B8 \uAE30\uC900 \uC0C1\uB300\uACBD\uB85C\uB97C \uC4F0\uB294 \uC2E4\uC81C JSON \uD30C\uC77C\uC744 \uAC00\uB9AC\uD0A4\uACE0, `baseSha`, `headSha`, `changedFiles`, `reviewedFiles`, `riskSummaries`, `Approval Rationale`, \uD30C\uC77C\uBCC4 findings, `Residual Risks`\uB97C \uD3EC\uD568\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
632
645
  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
646
  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)",
634
647
  prePrReviewDecisionReconfirm: "\uD604\uC7AC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{decision}`\uC785\uB2C8\uB2E4. \uC9C0\uC801\uC0AC\uD56D\uC744 \uBC18\uC601\uD55C \uB4A4 \uC774\uC804 \uC0C1\uD0DC \uC7AC\uC0AC\uC6A9\uC744 \uB9C9\uAE30 \uC704\uD574 \uBA85\uC2DC\uC801\uC73C\uB85C Decision\uC744 \uC9C0\uC815\uD574 \uC7AC\uC2E4\uD589\uD558\uC138\uC694: `{command}` (\uD655\uC778 \uD544\uC694)",
@@ -731,6 +744,8 @@ var enCli = {
731
744
  "feature.nextSteps1": " 1. Write {path}/spec.md",
732
745
  "feature.nextSteps2": " 2. Ask for review",
733
746
  "feature.nextSteps3": " 3. After approval, write plan.md",
747
+ "feature.ideaNotFound": "Idea document not found: {ref}",
748
+ "feature.ideaAmbiguous": "Multiple idea documents matched {ref}. Use an exact path or full indexed name.",
734
749
  "config.currentTitle": "\u{1F4CB} Current config:",
735
750
  "config.pathLabel": "Path",
736
751
  "config.projectRootStandaloneOnly": "\u26A0\uFE0F projectRoot can only be set in standalone mode.",
@@ -824,6 +839,13 @@ var enCli = {
824
839
  "init.log.gitInitialCommitDone": "\u2705 Initial Git commit created!",
825
840
  "init.warn.skipGitInit": "\u26A0\uFE0F Skipping Git initialization (please commit manually)",
826
841
  "init.error.templateNotFound": "Template not found: {path}",
842
+ "idea.fileExists": "Idea document already exists: {path}",
843
+ "idea.templateNotFound": "Built-in idea template not found.",
844
+ "idea.created": "\u2705 Idea document created: {path}",
845
+ "idea.nextStepsTitle": "Next steps:",
846
+ "idea.nextSteps1": " 1. Fill scope, PRD refs, and promotion notes",
847
+ "idea.nextSteps2": " 2. Promote it with: npx lee-spec-kit feature <name> --idea {ideaId}",
848
+ "idea.nextSteps3": " 3. Mark it dropped if it should not become a feature",
827
849
  "github.cmdGithubDescription": "GitHub workflow helpers (issue/pr templates, validation, merge retry)",
828
850
  "github.cmdIssueDescription": "Generate/create GitHub issue body from feature docs with validation",
829
851
  "github.cmdPrDescription": "Generate/create GitHub PR body with validation, tasks PR sync, and merge retry",
@@ -990,11 +1012,15 @@ var enCli = {
990
1012
  "validation.workflowModeInvalid": "Workflow mode must be one of: {values}.",
991
1013
  "validation.featureIdEmpty": "Feature ID cannot be empty.",
992
1014
  "validation.featureIdFormat": "Feature ID must be 'F' + digits (e.g., F001).",
1015
+ "validation.ideaIdEmpty": "Idea ID cannot be empty.",
1016
+ "validation.ideaIdFormat": "Idea ID must be 'I' + digits (e.g., I001).",
993
1017
  "validation.pathEmpty": "Path cannot be empty.",
994
1018
  "validation.pathNullByte": "Path cannot contain null bytes.",
995
1019
  "validation.genericFailed": "Validation failed",
996
1020
  "validation.context.featureName": "Feature name",
997
1021
  "validation.context.featureId": "Feature ID",
1022
+ "validation.context.ideaName": "Idea name",
1023
+ "validation.context.ideaId": "Idea ID",
998
1024
  "validation.context.projectName": "Project name",
999
1025
  "validation.context.projectType": "Project type",
1000
1026
  "validation.context.language": "Language",
@@ -1182,9 +1208,9 @@ var enMessages = {
1182
1208
  taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
1183
1209
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
1184
1210
  prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Running | Done` and run context again. (CHECK required)",
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 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
- 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)",
1211
+ prePrReviewRun: "Prepare the pre-PR review handoff, then continue the real review before recording anything. Generate structured `review-trace.json` evidence with `baseSha`, `headSha`, `changedFiles`, `reviewedFiles`, `riskSummaries`, `Approval Rationale`, per-file findings, and `Residual Risks`, then record the outcome with `pre-pr-review`. `pre-pr-review-run` itself does not complete the review or advance state. (CHECK required)",
1212
+ prePrReviewRunning: "A pre-PR review is already in progress. Reuse or resume the delegated review, generate structured `review-trace.json` evidence, then record the result with `pre-pr-review`. Do not re-approve the same label. (CHECK required)",
1213
+ prePrReviewEvidenceMissing: "tasks.md `Pre-PR Evidence` is empty/invalid. Point it to a real JSON file that uses repo-relative paths and includes `baseSha`, `headSha`, `changedFiles`, `reviewedFiles`, `riskSummaries`, `Approval Rationale`, per-file findings, and `Residual Risks`. (CHECK required)",
1188
1214
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1189
1215
  prePrReviewFixRequired: "Current `Pre-PR Decision` is `{decision}`. Apply the requested fixes from pre-PR findings before moving to PR creation. (CHECK required)",
1190
1216
  prePrReviewDecisionReconfirm: "Current `Pre-PR Decision` is `{decision}`. After fixing findings, rerun pre-PR review with an explicit decision to avoid replaying the prior state: `{command}` (CHECK required)",
@@ -1674,6 +1700,19 @@ function validateFeatureIdWithLang(id, lang) {
1674
1700
  }
1675
1701
  return { valid: true };
1676
1702
  }
1703
+ function validateIdeaIdWithLang(id, lang) {
1704
+ if (!id || id.trim().length === 0) {
1705
+ return { valid: false, error: tr(lang, "cli", "validation.ideaIdEmpty") };
1706
+ }
1707
+ const ideaIdPattern = /^I\d{3,}$/;
1708
+ if (!ideaIdPattern.test(id)) {
1709
+ return {
1710
+ valid: false,
1711
+ error: tr(lang, "cli", "validation.ideaIdFormat")
1712
+ };
1713
+ }
1714
+ return { valid: true };
1715
+ }
1677
1716
  function validatePathWithLang(inputPath, lang) {
1678
1717
  if (!inputPath || inputPath.trim().length === 0) {
1679
1718
  return { valid: false, error: tr(lang, "cli", "validation.pathEmpty") };
@@ -1702,10 +1741,10 @@ var DEFAULT_STALE_MS = 2 * 6e4;
1702
1741
  var RUNTIME_GIT_DIRNAME = "lee-spec-kit.runtime";
1703
1742
  var RUNTIME_TEMP_DIRNAME = "lee-spec-kit-runtime";
1704
1743
  function toScopeKey(value) {
1705
- return createHash("sha1").update(path12.resolve(value)).digest("hex").slice(0, 16);
1744
+ return createHash("sha1").update(path14.resolve(value)).digest("hex").slice(0, 16);
1706
1745
  }
1707
1746
  function getTempRuntimeDir(scopePath) {
1708
- return path12.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1747
+ return path14.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1709
1748
  }
1710
1749
  function resolveGitRuntimeDir(cwd) {
1711
1750
  try {
@@ -1719,38 +1758,38 @@ function resolveGitRuntimeDir(cwd) {
1719
1758
  }
1720
1759
  ).trim();
1721
1760
  if (!out) return null;
1722
- return path12.isAbsolute(out) ? out : path12.resolve(cwd, out);
1761
+ return path14.isAbsolute(out) ? out : path14.resolve(cwd, out);
1723
1762
  } catch {
1724
1763
  return null;
1725
1764
  }
1726
1765
  }
1727
1766
  function getRuntimeStateDir(cwd) {
1728
- const resolved = path12.resolve(cwd);
1767
+ const resolved = path14.resolve(cwd);
1729
1768
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1730
1769
  }
1731
1770
  function getDocsLockPath(docsDir) {
1732
- return path12.join(
1771
+ return path14.join(
1733
1772
  getRuntimeStateDir(docsDir),
1734
1773
  "locks",
1735
1774
  `docs-${toScopeKey(docsDir)}.lock`
1736
1775
  );
1737
1776
  }
1738
1777
  function getInitLockPath(targetDir) {
1739
- return path12.join(
1740
- getRuntimeStateDir(path12.dirname(path12.resolve(targetDir))),
1778
+ return path14.join(
1779
+ getRuntimeStateDir(path14.dirname(path14.resolve(targetDir))),
1741
1780
  "locks",
1742
1781
  `init-${toScopeKey(targetDir)}.lock`
1743
1782
  );
1744
1783
  }
1745
1784
  function getApprovalTicketStorePath(docsDir) {
1746
- return path12.join(
1785
+ return path14.join(
1747
1786
  getRuntimeStateDir(docsDir),
1748
1787
  "tickets",
1749
1788
  `approval-${toScopeKey(docsDir)}.json`
1750
1789
  );
1751
1790
  }
1752
1791
  function getProjectExecutionLockPath(cwd) {
1753
- return path12.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1792
+ return path14.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1754
1793
  }
1755
1794
  async function isStaleLock(lockPath, staleMs) {
1756
1795
  try {
@@ -1791,7 +1830,7 @@ function isProcessAlive(pid) {
1791
1830
  }
1792
1831
  }
1793
1832
  async function tryAcquire(lockPath, owner) {
1794
- await fs.ensureDir(path12.dirname(lockPath));
1833
+ await fs.ensureDir(path14.dirname(lockPath));
1795
1834
  try {
1796
1835
  const fd = await fs.open(lockPath, "wx");
1797
1836
  const payload = JSON.stringify(
@@ -1868,30 +1907,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
1868
1907
  "pr-template.md"
1869
1908
  ];
1870
1909
  var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
1871
- var ENGINE_MANAGED_FEATURE_PATH = path12.join(
1910
+ var ENGINE_MANAGED_FEATURE_PATH = path14.join(
1872
1911
  "features",
1873
1912
  "feature-base"
1874
1913
  );
1875
1914
  async function pruneEngineManagedDocs(docsDir) {
1876
1915
  const removed = [];
1877
1916
  for (const file of ENGINE_MANAGED_AGENT_FILES) {
1878
- const target = path12.join(docsDir, "agents", file);
1917
+ const target = path14.join(docsDir, "agents", file);
1879
1918
  if (await fs.pathExists(target)) {
1880
1919
  await fs.remove(target);
1881
- removed.push(path12.relative(docsDir, target));
1920
+ removed.push(path14.relative(docsDir, target));
1882
1921
  }
1883
1922
  }
1884
1923
  for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
1885
- const target = path12.join(docsDir, "agents", dir);
1924
+ const target = path14.join(docsDir, "agents", dir);
1886
1925
  if (await fs.pathExists(target)) {
1887
1926
  await fs.remove(target);
1888
- removed.push(path12.relative(docsDir, target));
1927
+ removed.push(path14.relative(docsDir, target));
1889
1928
  }
1890
1929
  }
1891
- const featureBasePath = path12.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1930
+ const featureBasePath = path14.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1892
1931
  if (await fs.pathExists(featureBasePath)) {
1893
1932
  await fs.remove(featureBasePath);
1894
- removed.push(path12.relative(docsDir, featureBasePath));
1933
+ removed.push(path14.relative(docsDir, featureBasePath));
1895
1934
  }
1896
1935
  return removed;
1897
1936
  }
@@ -1972,7 +2011,7 @@ Auto-run continuity (main/sub-agent orchestration):
1972
2011
  3. Else run \`npx lee-spec-kit context --json-compact\` (fallback: \`--json\`) and continue from current \`actionOptions\`/\`autoRun\`.
1973
2012
  - Pause and report to user only when:
1974
2013
  - \`approvalRequest.required === true\`, or
1975
- - \`autoRun.reasonCode\` is \`AUTO_GATE_REACHED\` or \`AUTO_MANUAL_REQUIRED\`, or
2014
+ - \`autoRun.reasonCode\` is \`AUTO_GATE_REACHED\`, \`AUTO_DELEGATED_HANDOFF\`, or \`AUTO_MANUAL_REQUIRED\`, or
1976
2015
  - command execution fails (non-zero/error), or
1977
2016
  - user explicitly asks to pause.
1978
2017
 
@@ -2171,7 +2210,7 @@ function initCommand(program2) {
2171
2210
  } catch (error) {
2172
2211
  if (error instanceof Error && error.message === "canceled") {
2173
2212
  const lang2 = options.lang ?? DEFAULT_LANG;
2174
- console.log(chalk8.yellow(`
2213
+ console.log(chalk9.yellow(`
2175
2214
  ${tr(lang2, "cli", "common.canceled")}`));
2176
2215
  return;
2177
2216
  }
@@ -2179,8 +2218,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
2179
2218
  const cliError = toCliError(error);
2180
2219
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
2181
2220
  console.error(
2182
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
2183
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
2221
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
2222
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
2184
2223
  );
2185
2224
  printCliErrorSuggestions(suggestions, lang);
2186
2225
  process.exitCode = 1;
@@ -2190,7 +2229,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
2190
2229
  }
2191
2230
  async function runInit(options) {
2192
2231
  const cwd = process.cwd();
2193
- const defaultName = path12.basename(cwd);
2232
+ const defaultName = path14.basename(cwd);
2194
2233
  let projectName = options.name || defaultName;
2195
2234
  let projectType = options.type;
2196
2235
  let components = parseComponentsOption(options.components);
@@ -2201,7 +2240,7 @@ async function runInit(options) {
2201
2240
  let docsRemote = options.docsRemote;
2202
2241
  let projectRoot;
2203
2242
  const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
2204
- const targetDir = path12.resolve(cwd, options.dir || "./docs");
2243
+ const targetDir = path14.resolve(cwd, options.dir || "./docs");
2205
2244
  const skipPrompts = !!options.yes || !!options.nonInteractive;
2206
2245
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
2207
2246
  throw createCliError(
@@ -2241,18 +2280,18 @@ async function runInit(options) {
2241
2280
  }
2242
2281
  console.log();
2243
2282
  console.log(
2244
- chalk8.blue(`${tr(lang, "cli", "init.currentDirectoryLabel")}: ${cwd}`)
2283
+ chalk9.blue(`${tr(lang, "cli", "init.currentDirectoryLabel")}: ${cwd}`)
2245
2284
  );
2246
2285
  if (isInsideGitRepo) {
2247
- console.log(chalk8.green(tr(lang, "cli", "init.gitDetected")));
2286
+ console.log(chalk9.green(tr(lang, "cli", "init.gitDetected")));
2248
2287
  console.log();
2249
- console.log(chalk8.gray(tr(lang, "cli", "init.insideProjectRoot")));
2250
- console.log(chalk8.gray(tr(lang, "cli", "init.modeEmbeddedDesc")));
2251
- console.log(chalk8.gray(tr(lang, "cli", "init.modeStandaloneDesc")));
2252
- console.log(chalk8.gray(tr(lang, "cli", "init.modeStandaloneMove")));
2288
+ console.log(chalk9.gray(tr(lang, "cli", "init.insideProjectRoot")));
2289
+ console.log(chalk9.gray(tr(lang, "cli", "init.modeEmbeddedDesc")));
2290
+ console.log(chalk9.gray(tr(lang, "cli", "init.modeStandaloneDesc")));
2291
+ console.log(chalk9.gray(tr(lang, "cli", "init.modeStandaloneMove")));
2253
2292
  } else {
2254
- console.log(chalk8.yellow(tr(lang, "cli", "init.gitNotDetected")));
2255
- console.log(chalk8.gray(tr(lang, "cli", "init.gitNotDetectedDetail")));
2293
+ console.log(chalk9.yellow(tr(lang, "cli", "init.gitNotDetected")));
2294
+ console.log(chalk9.gray(tr(lang, "cli", "init.gitNotDetectedDetail")));
2256
2295
  }
2257
2296
  console.log();
2258
2297
  const response = await prompts(
@@ -2574,31 +2613,31 @@ async function runInit(options) {
2574
2613
  initial: false
2575
2614
  });
2576
2615
  if (!overwrite) {
2577
- console.log(chalk8.yellow(tr(lang, "cli", "common.canceled")));
2616
+ console.log(chalk9.yellow(tr(lang, "cli", "common.canceled")));
2578
2617
  return;
2579
2618
  }
2580
2619
  }
2581
2620
  }
2582
2621
  }
2583
2622
  console.log();
2584
- console.log(chalk8.blue(tr(lang, "cli", "init.log.creatingDocs")));
2623
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.creatingDocs")));
2585
2624
  console.log(
2586
- chalk8.gray(
2625
+ chalk9.gray(
2587
2626
  ` ${tr(lang, "cli", "init.log.projectLabel")}: ${projectName}`
2588
2627
  )
2589
2628
  );
2590
2629
  console.log(
2591
- chalk8.gray(` ${tr(lang, "cli", "init.log.typeLabel")}: ${projectType}`)
2630
+ chalk9.gray(` ${tr(lang, "cli", "init.log.typeLabel")}: ${projectType}`)
2592
2631
  );
2593
2632
  console.log(
2594
- chalk8.gray(` ${tr(lang, "cli", "init.log.langLabel")}: ${lang}`)
2633
+ chalk9.gray(` ${tr(lang, "cli", "init.log.langLabel")}: ${lang}`)
2595
2634
  );
2596
2635
  console.log(
2597
- chalk8.gray(` ${tr(lang, "cli", "init.log.pathLabel")}: ${targetDir}`)
2636
+ chalk9.gray(` ${tr(lang, "cli", "init.log.pathLabel")}: ${targetDir}`)
2598
2637
  );
2599
2638
  console.log();
2600
2639
  const templatesDir = getTemplatesDir();
2601
- const commonPath = path12.join(templatesDir, lang, "common");
2640
+ const commonPath = path14.join(templatesDir, lang, "common");
2602
2641
  if (!await fs.pathExists(commonPath)) {
2603
2642
  throw new Error(
2604
2643
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
@@ -2607,11 +2646,11 @@ async function runInit(options) {
2607
2646
  const fsAdapter = new DefaultFileSystemAdapter();
2608
2647
  await copyTemplates(fsAdapter, commonPath, targetDir);
2609
2648
  if (projectType === "multi") {
2610
- const featuresRoot = path12.join(targetDir, "features");
2649
+ const featuresRoot = path14.join(targetDir, "features");
2611
2650
  for (const component of components) {
2612
- const componentDir = path12.join(featuresRoot, component);
2651
+ const componentDir = path14.join(featuresRoot, component);
2613
2652
  await fs.ensureDir(componentDir);
2614
- const readmePath = path12.join(componentDir, "README.md");
2653
+ const readmePath = path14.join(componentDir, "README.md");
2615
2654
  if (!await fs.pathExists(readmePath)) {
2616
2655
  await fs.writeFile(
2617
2656
  readmePath,
@@ -2672,20 +2711,20 @@ async function runInit(options) {
2672
2711
  config.projectRoot = projectRoot;
2673
2712
  }
2674
2713
  }
2675
- const configPath = path12.join(targetDir, ".lee-spec-kit.json");
2714
+ const configPath = path14.join(targetDir, ".lee-spec-kit.json");
2676
2715
  await fs.writeJson(configPath, config, { spaces: 2 });
2677
2716
  const extraCommitPathsAbs = [];
2678
2717
  try {
2679
2718
  if (docsRepo === "embedded") {
2680
2719
  const repoRoot = getGitTopLevelOrNull(cwd) || cwd;
2681
- const agentsMdPath = path12.join(repoRoot, "AGENTS.md");
2720
+ const agentsMdPath = path14.join(repoRoot, "AGENTS.md");
2682
2721
  const result = await upsertLeeSpecKitAgentsMd(agentsMdPath, {
2683
2722
  lang,
2684
2723
  docsRepo
2685
2724
  });
2686
2725
  if (result.changed) extraCommitPathsAbs.push(agentsMdPath);
2687
2726
  } else {
2688
- await upsertLeeSpecKitAgentsMd(path12.join(targetDir, "AGENTS.md"), {
2727
+ await upsertLeeSpecKitAgentsMd(path14.join(targetDir, "AGENTS.md"), {
2689
2728
  lang,
2690
2729
  docsRepo
2691
2730
  });
@@ -2695,16 +2734,16 @@ async function runInit(options) {
2695
2734
  } else if (projectRoot && typeof projectRoot === "object") {
2696
2735
  roots.push(...Object.values(projectRoot));
2697
2736
  }
2698
- const resolvedCwd = path12.resolve(cwd);
2737
+ const resolvedCwd = path14.resolve(cwd);
2699
2738
  for (const raw of roots) {
2700
2739
  const value = String(raw || "").trim();
2701
2740
  if (!value) continue;
2702
- const abs = path12.resolve(cwd, value);
2703
- if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path12.sep}`)) {
2741
+ const abs = path14.resolve(cwd, value);
2742
+ if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path14.sep}`)) {
2704
2743
  if (await fs.pathExists(abs)) {
2705
2744
  const stat = await fs.stat(abs);
2706
2745
  if (stat.isDirectory()) {
2707
- await upsertLeeSpecKitAgentsMd(path12.join(abs, "AGENTS.md"), {
2746
+ await upsertLeeSpecKitAgentsMd(path14.join(abs, "AGENTS.md"), {
2708
2747
  lang,
2709
2748
  docsRepo
2710
2749
  });
@@ -2715,7 +2754,7 @@ async function runInit(options) {
2715
2754
  }
2716
2755
  } catch {
2717
2756
  }
2718
- console.log(chalk8.green(tr(lang, "cli", "init.log.docsCreated")));
2757
+ console.log(chalk9.green(tr(lang, "cli", "init.log.docsCreated")));
2719
2758
  console.log();
2720
2759
  await initGit(
2721
2760
  cwd,
@@ -2726,14 +2765,14 @@ async function runInit(options) {
2726
2765
  docsRemote,
2727
2766
  extraCommitPathsAbs
2728
2767
  );
2729
- console.log(chalk8.blue(tr(lang, "cli", "init.log.nextStepsTitle")));
2768
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.nextStepsTitle")));
2730
2769
  console.log(
2731
- chalk8.gray(
2770
+ chalk9.gray(
2732
2771
  tr(lang, "cli", "init.log.nextSteps1", { docsDir: targetDir })
2733
2772
  )
2734
2773
  );
2735
- console.log(chalk8.gray(tr(lang, "cli", "init.log.nextSteps2")));
2736
- console.log(chalk8.gray(tr(lang, "cli", "init.log.nextSteps3")));
2774
+ console.log(chalk9.gray(tr(lang, "cli", "init.log.nextSteps2")));
2775
+ console.log(chalk9.gray(tr(lang, "cli", "init.log.nextSteps3")));
2737
2776
  console.log();
2738
2777
  },
2739
2778
  { owner: "init" }
@@ -2788,31 +2827,31 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2788
2827
  try {
2789
2828
  runGitOrThrow(["rev-parse", "--is-inside-work-tree"], gitWorkdir);
2790
2829
  console.log(
2791
- chalk8.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit"))
2830
+ chalk9.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit"))
2792
2831
  );
2793
2832
  } catch {
2794
- console.log(chalk8.blue(tr(lang, "cli", "init.log.gitInit")));
2833
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.gitInit")));
2795
2834
  runGitOrThrow(["init"], gitWorkdir);
2796
2835
  }
2797
- const relativePath = docsRepo === "standalone" ? "." : path12.relative(gitWorkdir, targetDir);
2836
+ const relativePath = docsRepo === "standalone" ? "." : path14.relative(gitWorkdir, targetDir);
2798
2837
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2799
2838
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2800
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.stagedChangesSkip")));
2801
- console.log(chalk8.gray(tr(lang, "cli", "init.warn.commitManually")));
2839
+ console.log(chalk9.yellow(tr(lang, "cli", "init.warn.stagedChangesSkip")));
2840
+ console.log(chalk9.gray(tr(lang, "cli", "init.warn.commitManually")));
2802
2841
  console.log();
2803
2842
  return;
2804
2843
  }
2805
2844
  if (relativePath !== "." && isPathIgnored(gitWorkdir, relativePath)) {
2806
2845
  const repoRelativePath = toRepoRelativePath(gitWorkdir, relativePath);
2807
2846
  console.log(
2808
- chalk8.yellow(
2847
+ chalk9.yellow(
2809
2848
  tr(lang, "cli", "init.warn.docsPathIgnoredSkipCommit", {
2810
2849
  path: repoRelativePath
2811
2850
  })
2812
2851
  )
2813
2852
  );
2814
2853
  console.log(
2815
- chalk8.gray(
2854
+ chalk9.gray(
2816
2855
  tr(lang, "cli", "init.warn.docsPathIgnoredHint", {
2817
2856
  path: repoRelativePath
2818
2857
  })
@@ -2821,7 +2860,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2821
2860
  console.log();
2822
2861
  return;
2823
2862
  }
2824
- const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path12.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
2863
+ const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path14.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
2825
2864
  const pathsToStage = [relativePath, ...extraRelativePaths];
2826
2865
  for (const p of pathsToStage) {
2827
2866
  runGitOrThrow(["add", p], gitWorkdir);
@@ -2840,34 +2879,34 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2840
2879
  try {
2841
2880
  runGitOrThrow(["remote", "add", "origin", docsRemote], gitWorkdir);
2842
2881
  console.log(
2843
- chalk8.green(
2882
+ chalk9.green(
2844
2883
  tr(lang, "cli", "init.log.gitRemoteSet", { remote: docsRemote })
2845
2884
  )
2846
2885
  );
2847
2886
  } catch {
2848
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.gitRemoteExists")));
2887
+ console.log(chalk9.yellow(tr(lang, "cli", "init.warn.gitRemoteExists")));
2849
2888
  }
2850
2889
  }
2851
- console.log(chalk8.green(tr(lang, "cli", "init.log.gitInitialCommitDone")));
2890
+ console.log(chalk9.green(tr(lang, "cli", "init.log.gitInitialCommitDone")));
2852
2891
  console.log();
2853
2892
  } catch {
2854
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.skipGitInit")));
2893
+ console.log(chalk9.yellow(tr(lang, "cli", "init.warn.skipGitInit")));
2855
2894
  console.log();
2856
2895
  }
2857
2896
  }
2858
2897
  function getAncestorDirs(startDir) {
2859
2898
  const dirs = [];
2860
- let current = path12.resolve(startDir);
2899
+ let current = path14.resolve(startDir);
2861
2900
  while (true) {
2862
2901
  dirs.push(current);
2863
- const parent = path12.dirname(current);
2902
+ const parent = path14.dirname(current);
2864
2903
  if (parent === current) break;
2865
2904
  current = parent;
2866
2905
  }
2867
2906
  return dirs;
2868
2907
  }
2869
2908
  function hasWorkspaceBoundary(dir) {
2870
- return fs.existsSync(path12.join(dir, "package.json")) || fs.existsSync(path12.join(dir, ".git"));
2909
+ return fs.existsSync(path14.join(dir, "package.json")) || fs.existsSync(path14.join(dir, ".git"));
2871
2910
  }
2872
2911
  function getSearchBaseDirs(cwd) {
2873
2912
  const ancestors = getAncestorDirs(cwd);
@@ -2883,7 +2922,7 @@ function normalizeComponentKeys(value) {
2883
2922
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2884
2923
  }
2885
2924
  async function inferComponentsFromFeaturesDir(docsDir) {
2886
- const featuresPath = path12.join(docsDir, "features");
2925
+ const featuresPath = path14.join(docsDir, "features");
2887
2926
  if (!await fs.pathExists(featuresPath)) return [];
2888
2927
  const entries = await fs.readdir(featuresPath, { withFileTypes: true });
2889
2928
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
@@ -2894,21 +2933,21 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2894
2933
  async function getConfig(cwd) {
2895
2934
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2896
2935
  const baseDirs = [
2897
- ...explicitDocsDir ? [path12.resolve(explicitDocsDir)] : [],
2936
+ ...explicitDocsDir ? [path14.resolve(explicitDocsDir)] : [],
2898
2937
  ...getSearchBaseDirs(cwd)
2899
2938
  ];
2900
2939
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2901
2940
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2902
2941
  for (const baseDir of baseDirs) {
2903
- const resolvedBaseDir = path12.resolve(baseDir);
2942
+ const resolvedBaseDir = path14.resolve(baseDir);
2904
2943
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2905
2944
  visitedBaseDirs.add(resolvedBaseDir);
2906
- const possibleDocsDirs = [path12.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2945
+ const possibleDocsDirs = [path14.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2907
2946
  for (const docsDir of possibleDocsDirs) {
2908
- const resolvedDocsDir = path12.resolve(docsDir);
2947
+ const resolvedDocsDir = path14.resolve(docsDir);
2909
2948
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2910
2949
  visitedDocsDirs.add(resolvedDocsDir);
2911
- const configPath = path12.join(resolvedDocsDir, ".lee-spec-kit.json");
2950
+ const configPath = path14.join(resolvedDocsDir, ".lee-spec-kit.json");
2912
2951
  if (await fs.pathExists(configPath)) {
2913
2952
  try {
2914
2953
  const configFile = await fs.readJson(configPath);
@@ -2938,16 +2977,16 @@ async function getConfig(cwd) {
2938
2977
  } catch {
2939
2978
  }
2940
2979
  }
2941
- const agentsPath = path12.join(resolvedDocsDir, "agents");
2942
- const featuresPath = path12.join(resolvedDocsDir, "features");
2980
+ const agentsPath = path14.join(resolvedDocsDir, "agents");
2981
+ const featuresPath = path14.join(resolvedDocsDir, "features");
2943
2982
  if (await fs.pathExists(agentsPath) && await fs.pathExists(featuresPath)) {
2944
2983
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2945
2984
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2946
2985
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2947
2986
  const langProbeCandidates = [
2948
- path12.join(agentsPath, "custom.md"),
2949
- path12.join(agentsPath, "constitution.md"),
2950
- path12.join(agentsPath, "agents.md")
2987
+ path14.join(agentsPath, "custom.md"),
2988
+ path14.join(agentsPath, "constitution.md"),
2989
+ path14.join(agentsPath, "agents.md")
2951
2990
  ];
2952
2991
  let lang = "en";
2953
2992
  for (const candidate of langProbeCandidates) {
@@ -3018,18 +3057,90 @@ async function patchMarkdownIfExists(filePath, transform) {
3018
3057
  await fs.writeFile(filePath, transform(content), "utf-8");
3019
3058
  }
3020
3059
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
3021
- await patchMarkdownIfExists(path12.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3060
+ await patchMarkdownIfExists(path14.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3022
3061
  await patchMarkdownIfExists(
3023
- path12.join(featureDir, "tasks.md"),
3062
+ path14.join(featureDir, "tasks.md"),
3024
3063
  (content) => sanitizeTasksForLocal(content, lang)
3025
3064
  );
3026
- await fs.remove(path12.join(featureDir, "issue.md"));
3027
- await fs.remove(path12.join(featureDir, "pr.md"));
3065
+ await fs.remove(path14.join(featureDir, "issue.md"));
3066
+ await fs.remove(path14.join(featureDir, "pr.md"));
3067
+ }
3068
+ var IDEA_REF_PATTERN = /\b(I\d{3,}(?:-[A-Za-z0-9._-]+)?)\b/;
3069
+ var IDEA_PATH_PATTERN = /\b(?:\.\/)?docs\/ideas\/[^\s]+\.md\b/;
3070
+ function extractExplicitIdeaRef(requestText) {
3071
+ const pathMatch = requestText.match(IDEA_PATH_PATTERN);
3072
+ if (pathMatch) return pathMatch[0];
3073
+ const refMatch = requestText.match(IDEA_REF_PATTERN);
3074
+ if (refMatch) return refMatch[1];
3075
+ return null;
3076
+ }
3077
+ async function resolveIdeaReference(docsDir, ref, lang) {
3078
+ const ideasDir = path14.join(docsDir, "ideas");
3079
+ const trimmedRef = ref.trim();
3080
+ if (!trimmedRef) {
3081
+ throw createCliError(
3082
+ "INVALID_ARGUMENT",
3083
+ tr(lang, "cli", "feature.ideaNotFound", { ref })
3084
+ );
3085
+ }
3086
+ if (trimmedRef.includes("/") || trimmedRef.endsWith(".md")) {
3087
+ const candidate = path14.resolve(process.cwd(), trimmedRef);
3088
+ if (await fs.pathExists(candidate)) {
3089
+ return { path: candidate };
3090
+ }
3091
+ throw createCliError(
3092
+ "INVALID_ARGUMENT",
3093
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3094
+ );
3095
+ }
3096
+ if (!await fs.pathExists(ideasDir)) {
3097
+ throw createCliError(
3098
+ "INVALID_ARGUMENT",
3099
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3100
+ );
3101
+ }
3102
+ const entries = await fs.readdir(ideasDir, { withFileTypes: true });
3103
+ const files = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md")).map((entry) => entry.name);
3104
+ const exactName = `${trimmedRef}.md`;
3105
+ if (files.includes(exactName)) {
3106
+ return { path: path14.join(ideasDir, exactName) };
3107
+ }
3108
+ const byId = /^I\d{3,}$/.test(trimmedRef) ? files.filter((name) => name.startsWith(`${trimmedRef}-`)) : [];
3109
+ if (byId.length === 1) {
3110
+ return { path: path14.join(ideasDir, byId[0]) };
3111
+ }
3112
+ if (byId.length > 1) {
3113
+ throw createCliError(
3114
+ "INVALID_ARGUMENT",
3115
+ tr(lang, "cli", "feature.ideaAmbiguous", { ref: trimmedRef })
3116
+ );
3117
+ }
3118
+ throw createCliError(
3119
+ "INVALID_ARGUMENT",
3120
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3121
+ );
3122
+ }
3123
+ async function readIdeaMetadataValue(ideaPath, label) {
3124
+ const content = await fs.readFile(ideaPath, "utf-8");
3125
+ const pattern = new RegExp(`^- \\*\\*${escapeRegExp(label)}\\*\\*:\\s*(.+)$`, "m");
3126
+ const match = content.match(pattern);
3127
+ if (!match) return null;
3128
+ const value = match[1].trim();
3129
+ return value.length > 0 ? value : null;
3130
+ }
3131
+ async function deriveFeatureNameFromIdea(ideaPath) {
3132
+ const ideaName = await readIdeaMetadataValue(ideaPath, "Idea Name");
3133
+ if (ideaName && ideaName !== "-") return ideaName;
3134
+ const basename = path14.basename(ideaPath, ".md");
3135
+ return basename.replace(/^I\d{3,}-/, "");
3136
+ }
3137
+ function escapeRegExp(value) {
3138
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3028
3139
  }
3029
3140
 
3030
3141
  // src/commands/feature.ts
3031
3142
  function featureCommand(program2) {
3032
- program2.command("feature <name>").description("Create a new feature folder").option("--component <component>", "Component name (multi only)").option("--id <id>", "Feature ID (default: auto)").option("-d, --desc <description>", "Feature description for spec.md").option("--non-interactive", "Fail instead of prompting for input").option("--json", "Output in JSON format for agents").action(async (name, options) => {
3143
+ program2.command("feature <name>").description("Create a new feature folder").option("--component <component>", "Component name (multi only)").option("--id <id>", "Feature ID (default: auto)").option("-d, --desc <description>", "Feature description for spec.md").option("--idea <ref>", "Idea reference to promote (I001 | I001-slug | docs/ideas/...)").option("--non-interactive", "Fail instead of prompting for input").option("--json", "Output in JSON format for agents").action(async (name, options) => {
3033
3144
  try {
3034
3145
  const result = await runFeature(name, options);
3035
3146
  if (options.json) {
@@ -3062,7 +3173,7 @@ function featureCommand(program2) {
3062
3173
  );
3063
3174
  return;
3064
3175
  }
3065
- console.log(chalk8.yellow(`
3176
+ console.log(chalk9.yellow(`
3066
3177
  ${tr(lang2, "cli", "common.canceled")}`));
3067
3178
  return;
3068
3179
  }
@@ -3083,8 +3194,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
3083
3194
  return;
3084
3195
  }
3085
3196
  console.error(
3086
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
3087
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
3197
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
3198
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
3088
3199
  );
3089
3200
  printCliErrorSuggestions(suggestions, lang);
3090
3201
  process.exitCode = 1;
@@ -3110,6 +3221,7 @@ async function runFeature(name, options) {
3110
3221
  projectType,
3111
3222
  config.components
3112
3223
  );
3224
+ const linkedIdea = options.idea ? await resolveIdeaReference(docsDir, options.idea, lang) : null;
3113
3225
  assertValid(
3114
3226
  validateSafeNameWithLang(name, lang),
3115
3227
  tr(lang, "cli", "validation.context.featureName"),
@@ -3175,19 +3287,19 @@ async function runFeature(name, options) {
3175
3287
  }
3176
3288
  let featuresDir;
3177
3289
  if (projectType === "multi") {
3178
- featuresDir = path12.join(docsDir, "features", component);
3290
+ featuresDir = path14.join(docsDir, "features", component);
3179
3291
  } else {
3180
- featuresDir = path12.join(docsDir, "features");
3292
+ featuresDir = path14.join(docsDir, "features");
3181
3293
  }
3182
3294
  const featureFolderName = `${featureId}-${name}`;
3183
- const featureDir = path12.join(featuresDir, featureFolderName);
3295
+ const featureDir = path14.join(featuresDir, featureFolderName);
3184
3296
  if (await fs.pathExists(featureDir)) {
3185
3297
  throw createCliError(
3186
3298
  "INVALID_ARGUMENT",
3187
3299
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
3188
3300
  );
3189
3301
  }
3190
- const featureBasePath = path12.join(
3302
+ const featureBasePath = path14.join(
3191
3303
  getTemplatesDir(),
3192
3304
  lang,
3193
3305
  "common",
@@ -3232,23 +3344,30 @@ async function runFeature(name, options) {
3232
3344
  }
3233
3345
  const fsAdapter = new DefaultFileSystemAdapter();
3234
3346
  await replaceInFiles(fsAdapter, featureDir, replacements);
3347
+ if (linkedIdea) {
3348
+ await stampIdeaReferenceInSpec(
3349
+ path14.join(featureDir, "spec.md"),
3350
+ path14.relative(featureDir, linkedIdea.path)
3351
+ );
3352
+ await markIdeaAsFeatureized(linkedIdea.path, featureFolderName);
3353
+ }
3235
3354
  if (config.workflow?.mode === "local") {
3236
3355
  await applyLocalWorkflowTemplateToFeatureDir(featureDir, lang);
3237
3356
  }
3238
3357
  if (!options.json) {
3239
3358
  console.log();
3240
3359
  console.log(
3241
- chalk8.green(tr(lang, "cli", "feature.created", { path: featureDir }))
3360
+ chalk9.green(tr(lang, "cli", "feature.created", { path: featureDir }))
3242
3361
  );
3243
3362
  console.log();
3244
- console.log(chalk8.blue(tr(lang, "cli", "feature.nextStepsTitle")));
3363
+ console.log(chalk9.blue(tr(lang, "cli", "feature.nextStepsTitle")));
3245
3364
  console.log(
3246
- chalk8.gray(
3365
+ chalk9.gray(
3247
3366
  tr(lang, "cli", "feature.nextSteps1", { path: featureDir })
3248
3367
  )
3249
3368
  );
3250
- console.log(chalk8.gray(tr(lang, "cli", "feature.nextSteps2")));
3251
- console.log(chalk8.gray(tr(lang, "cli", "feature.nextSteps3")));
3369
+ console.log(chalk9.gray(tr(lang, "cli", "feature.nextSteps2")));
3370
+ console.log(chalk9.gray(tr(lang, "cli", "feature.nextSteps3")));
3252
3371
  console.log();
3253
3372
  }
3254
3373
  return {
@@ -3256,18 +3375,69 @@ async function runFeature(name, options) {
3256
3375
  featureName: name,
3257
3376
  component: projectType === "multi" ? component : void 0,
3258
3377
  featurePath: featureDir,
3259
- featurePathFromDocs: path12.relative(docsDir, featureDir)
3378
+ featurePathFromDocs: path14.relative(docsDir, featureDir)
3260
3379
  };
3261
3380
  },
3262
3381
  { owner: "feature" }
3263
3382
  );
3264
3383
  }
3384
+ async function stampIdeaReferenceInSpec(specPath, relativeIdeaPath) {
3385
+ const normalizedPath = relativeIdeaPath.replace(/\\/g, "/");
3386
+ const ideaLine = `- Idea: \`${normalizedPath}\``;
3387
+ let content = await fs.readFile(specPath, "utf-8");
3388
+ if (content.includes(ideaLine)) {
3389
+ return;
3390
+ }
3391
+ if (content.includes("## Related Documents")) {
3392
+ content = content.replace(
3393
+ "## Related Documents\n\n",
3394
+ `## Related Documents
3395
+
3396
+ ${ideaLine}
3397
+ `
3398
+ );
3399
+ } else {
3400
+ content = `${content.trimEnd()}
3401
+
3402
+ ${ideaLine}
3403
+ `;
3404
+ }
3405
+ await fs.writeFile(specPath, content, "utf-8");
3406
+ }
3407
+ async function markIdeaAsFeatureized(ideaPath, featureFolderName) {
3408
+ let content = await fs.readFile(ideaPath, "utf-8");
3409
+ content = replaceOrAppendIdeaMetadata(content, "Status", "Featureized");
3410
+ content = replaceOrAppendIdeaMetadata(content, "Feature", featureFolderName);
3411
+ await fs.writeFile(ideaPath, content, "utf-8");
3412
+ }
3413
+ function replaceOrAppendIdeaMetadata(content, label, value) {
3414
+ const pattern = new RegExp(`^- \\*\\*${escapeRegExp2(label)}\\*\\*:.*$`, "m");
3415
+ const line = `- **${label}**: ${value}`;
3416
+ if (pattern.test(content)) {
3417
+ return content.replace(pattern, line);
3418
+ }
3419
+ const heading = "## Promotion Tracking";
3420
+ if (content.includes(heading)) {
3421
+ return content.replace(heading, `${heading}
3422
+
3423
+ ${line}`);
3424
+ }
3425
+ return `${content.trimEnd()}
3426
+
3427
+ ${heading}
3428
+
3429
+ ${line}
3430
+ `;
3431
+ }
3432
+ function escapeRegExp2(value) {
3433
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3434
+ }
3265
3435
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3266
3436
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
3267
3437
  const candidates = [
3268
- ...explicitDocsDir ? [path12.resolve(explicitDocsDir)] : [],
3269
- path12.resolve(cwd, "docs"),
3270
- path12.resolve(cwd)
3438
+ ...explicitDocsDir ? [path14.resolve(explicitDocsDir)] : [],
3439
+ path14.resolve(cwd, "docs"),
3440
+ path14.resolve(cwd)
3271
3441
  ];
3272
3442
  const endAt = Date.now() + timeoutMs;
3273
3443
  while (Date.now() < endAt) {
@@ -3294,12 +3464,12 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3294
3464
  return getConfig(cwd);
3295
3465
  }
3296
3466
  async function getNextFeatureId(docsDir, projectType, components) {
3297
- const featuresDir = path12.join(docsDir, "features");
3467
+ const featuresDir = path14.join(docsDir, "features");
3298
3468
  let max = 0;
3299
3469
  const scanDirs = [];
3300
3470
  if (projectType === "multi") {
3301
3471
  scanDirs.push(
3302
- ...components.map((component) => path12.join(featuresDir, component))
3472
+ ...components.map((component) => path14.join(featuresDir, component))
3303
3473
  );
3304
3474
  } else {
3305
3475
  scanDirs.push(featuresDir);
@@ -3320,6 +3490,167 @@ async function getNextFeatureId(docsDir, projectType, components) {
3320
3490
  const width = Math.max(3, String(next).length);
3321
3491
  return `F${String(next).padStart(width, "0")}`;
3322
3492
  }
3493
+ function ideaCommand(program2) {
3494
+ program2.command("idea <name>").description("Create a new indexed idea document").option("--component <component>", "Component name (optional)").option("--id <id>", "Idea ID (default: auto)").option("-d, --desc <description>", "Idea description for the document").option("--non-interactive", "Reserved for parity with other generators").option("--json", "Output in JSON format for agents").action(async (name, options) => {
3495
+ try {
3496
+ const result = await runIdea(name, options);
3497
+ if (options.json) {
3498
+ console.log(
3499
+ JSON.stringify(
3500
+ {
3501
+ status: "ok",
3502
+ reasonCode: "IDEA_CREATED",
3503
+ ideaId: result.ideaId,
3504
+ ideaName: result.ideaName,
3505
+ component: result.component,
3506
+ ideaPath: result.ideaPath,
3507
+ ideaPathFromDocs: result.ideaPathFromDocs
3508
+ },
3509
+ null,
3510
+ 2
3511
+ )
3512
+ );
3513
+ }
3514
+ } catch (error) {
3515
+ const config = await getConfig(process.cwd());
3516
+ const lang = config?.lang ?? DEFAULT_LANG;
3517
+ const cliError = toCliError(error);
3518
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
3519
+ if (options.json) {
3520
+ console.log(
3521
+ JSON.stringify({
3522
+ status: "error",
3523
+ reasonCode: cliError.code,
3524
+ error: cliError.message,
3525
+ suggestions
3526
+ })
3527
+ );
3528
+ process.exitCode = 1;
3529
+ return;
3530
+ }
3531
+ console.error(
3532
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
3533
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
3534
+ );
3535
+ printCliErrorSuggestions(suggestions, lang);
3536
+ process.exitCode = 1;
3537
+ }
3538
+ });
3539
+ }
3540
+ async function runIdea(name, options) {
3541
+ const cwd = process.cwd();
3542
+ const config = await getConfig(cwd);
3543
+ if (!config) {
3544
+ throw createCliError(
3545
+ "DOCS_NOT_FOUND",
3546
+ tr(DEFAULT_LANG, "cli", "common.docsNotFound")
3547
+ );
3548
+ }
3549
+ const { docsDir, projectType, lang } = config;
3550
+ const configuredComponents = resolveProjectComponents(
3551
+ projectType,
3552
+ config.components
3553
+ );
3554
+ assertValid(
3555
+ validateSafeNameWithLang(name, lang),
3556
+ tr(lang, "cli", "validation.context.ideaName"),
3557
+ lang
3558
+ );
3559
+ let component = (options.component || "").trim().toLowerCase();
3560
+ if (component && projectType === "single") {
3561
+ throw createCliError(
3562
+ "INVALID_ARGUMENT",
3563
+ "`--component` can only be used in multi mode."
3564
+ );
3565
+ }
3566
+ if (projectType === "multi" && component) {
3567
+ assertAllowedComponent(component, configuredComponents);
3568
+ }
3569
+ return withFileLock(
3570
+ getDocsLockPath(docsDir),
3571
+ async () => {
3572
+ const ideaId = options.id ? validateProvidedIdeaId(options.id, lang) : await getNextIdeaId(docsDir);
3573
+ const ideasDir = path14.join(docsDir, "ideas");
3574
+ const ideaFileName = `${ideaId}-${name}.md`;
3575
+ const ideaPath = path14.join(ideasDir, ideaFileName);
3576
+ if (await fs.pathExists(ideaPath)) {
3577
+ throw createCliError(
3578
+ "INVALID_ARGUMENT",
3579
+ tr(lang, "cli", "idea.fileExists", { path: ideaPath })
3580
+ );
3581
+ }
3582
+ const templatePath = path14.join(
3583
+ getTemplatesDir(),
3584
+ lang,
3585
+ "common",
3586
+ "ideas",
3587
+ "idea.md"
3588
+ );
3589
+ if (!await fs.pathExists(templatePath)) {
3590
+ throw createCliError(
3591
+ "DOCS_NOT_FOUND",
3592
+ tr(lang, "cli", "idea.templateNotFound")
3593
+ );
3594
+ }
3595
+ await fs.mkdir(ideasDir, { recursive: true });
3596
+ const template = await fs.readFile(templatePath, "utf-8");
3597
+ const content = applyIdeaTemplate(template, {
3598
+ ideaId,
3599
+ name,
3600
+ description: options.desc || "",
3601
+ component: component || "-",
3602
+ created: getLocalDateString()
3603
+ });
3604
+ await fs.writeFile(ideaPath, content, "utf-8");
3605
+ if (!options.json) {
3606
+ console.log();
3607
+ console.log(chalk9.green(tr(lang, "cli", "idea.created", { path: ideaPath })));
3608
+ console.log();
3609
+ console.log(chalk9.blue(tr(lang, "cli", "idea.nextStepsTitle")));
3610
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps1")));
3611
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps2", { ideaId })));
3612
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps3")));
3613
+ console.log();
3614
+ }
3615
+ return {
3616
+ ideaId,
3617
+ ideaName: name,
3618
+ component: component || void 0,
3619
+ ideaPath,
3620
+ ideaPathFromDocs: path14.relative(docsDir, ideaPath)
3621
+ };
3622
+ },
3623
+ { owner: "idea" }
3624
+ );
3625
+ }
3626
+ function validateProvidedIdeaId(id, lang) {
3627
+ assertValid(
3628
+ validateIdeaIdWithLang(id, lang),
3629
+ tr(lang, "cli", "validation.context.ideaId"),
3630
+ lang
3631
+ );
3632
+ return id;
3633
+ }
3634
+ function applyIdeaTemplate(template, values) {
3635
+ return template.replaceAll("{idea-id}", values.ideaId).replaceAll("{idea-name}", values.name).replaceAll("{YYYY-MM-DD}", values.created).replaceAll("{{description}}", values.description).replaceAll("{component}", values.component);
3636
+ }
3637
+ async function getNextIdeaId(docsDir) {
3638
+ const ideasDir = path14.join(docsDir, "ideas");
3639
+ let max = 0;
3640
+ if (await fs.pathExists(ideasDir)) {
3641
+ const entries = await fs.readdir(ideasDir, { withFileTypes: true });
3642
+ for (const entry of entries) {
3643
+ if (!entry.isFile()) continue;
3644
+ const match = entry.name.match(/^I(\d+)-/);
3645
+ if (!match) continue;
3646
+ const num = parseInt(match[1], 10);
3647
+ if (num > max) max = num;
3648
+ }
3649
+ }
3650
+ const next = max + 1;
3651
+ const width = Math.max(3, String(next).length);
3652
+ return `I${String(next).padStart(width, "0")}`;
3653
+ }
3323
3654
  var DefaultCommandAdapter = class {
3324
3655
  execSync(command, options) {
3325
3656
  return execSync(command, options);
@@ -3579,15 +3910,15 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3579
3910
  2. \uB9AC\uBDF0 \uBC94\uC704\uB97C \uBD84\uB9AC\uD574 \uD655\uC778\uD558\uC138\uC694.
3580
3911
  - main \uAE30\uC900: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
3581
3912
  - worktree \uAE30\uC900: 'git diff --name-only', 'git diff --name-only --cached', 'git ls-files --others --exclude-standard'
3582
- 3. \uAD6C\uD604\uC774 feature \uC758\uB3C4\uC5D0 \uB9DE\uB294\uC9C0 \uD3C9\uAC00\uD558\uC138\uC694. \uD2B9\uD788 \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, \`residualRisks\`\uB97C \uAD6C\uCCB4\uC801\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694.
3583
- 4. \uD655\uC778\uB41C \uAC01 \uD30C\uC77C\uC5D0 \uB300\uD574 risk, security, perf, maintainability \uD3C9\uAC00\uC640 \uAD6C\uCCB4\uC801\uC778 fileLine \uC704\uCE58\uAC00 \uD3EC\uD568\uB41C 'review-trace.json' \uC99D\uAC70 \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC138\uC694.
3913
+ 3. \uAD6C\uD604\uC774 feature \uC758\uB3C4\uC5D0 \uB9DE\uB294\uC9C0 \uD3C9\uAC00\uD558\uC138\uC694. \uD2B9\uD788 \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, \`residualRisks\`, \`approvalRationale\`\uB97C \uAD6C\uCCB4\uC801\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694.
3914
+ 4. \uD655\uC778\uB41C \uAC01 \uD30C\uC77C\uC5D0 \uB300\uD574 risk, security, perf, maintainability \uD3C9\uAC00\uC640 \uAD6C\uCCB4\uC801\uC778 fileLine \uC704\uCE58\uAC00 \uD3EC\uD568\uB41C 'review-trace.json' \uC99D\uAC70 \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC138\uC694. \uC99D\uAC70\uC5D0\uB294 \uBC18\uB4DC\uC2DC \`baseSha\`, \`headSha\`, \`changedFiles\`, \`reviewedFiles\`, \`riskSummaries\`(blocking/important/minor)\uAC00 \uD3EC\uD568\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4.
3584
3915
  5. \uAE30\uBCF8 \uBCA0\uC774\uC2A4\uB77C\uC778\uC740 '${fallbackText}'\uC774\uBA70, 'create-pr' \uBB38\uC11C\uC758 'Pre-PR \uAE30\uBCF8 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8' \uC139\uC158\uC744 \uC218\uD589\uD558\uC138\uC694.
3585
3916
  6. \uC6B0\uC120\uC21C\uC704 \uC2A4\uD0AC: ${skills.length > 0 ? skills.join(", ") : "\uC5C6\uC74C"} \uB85C \uC2EC\uD654 \uAC80\uD1A0\uB97C \uC9C4\uD589\uD558\uC138\uC694.
3586
3917
  7. \uCD94\uAC00 \uAC80\uC99D\uC774 \uAF2D \uD544\uC694\uD560 \uB54C\uB9CC audit/\uD0C0\uAE43 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694. \uBCC4\uB3C4 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uCD94\uAC00 \uC0DD\uC131\uB3C4 \uAF2D \uD544\uC694\uD560 \uB54C\uB9CC \uD558\uC138\uC694.
3587
3918
  8. \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uB9AC\uBDF0\uB97C \uC774\uC5B4\uAC00\uACE0, \uC774\uBBF8 \uC218\uC9D1\uD55C \uACB0\uACFC\uB9CC \uC815\uB9AC\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3588
3919
  9. \uC2E4\uD589\uD55C \uBA85\uB839\uC774 \uC788\uC73C\uBA74 \`commandsExecuted\`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.
3589
- 10. \uC9C0\uC801\uC0AC\uD56D\uC774 \uB0A8\uC544 \uC788\uC73C\uBA74 \uBA3C\uC800 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' \uB85C \uAE30\uB85D\uD558\uC138\uC694. \uB2E8, \`workflow.prePrReview.evidenceMode=any\` \uC774\uACE0 \uC2E4\uD589 \uC99D\uAC70 \uAC15\uC81C\uAC00 \uC5C6\uC73C\uBA74 \`--evidence\` \uC5C6\uC774 \uC9C1\uC811 \uAE30\uB85D\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3590
- 11. \uC218\uC815/\uC7AC\uAC80\uC99D \uD6C4 \uCD5C\uC885 \uC2B9\uC778 \uC2DC\uC810\uC5D0 'npx lee-spec-kit pre-pr-review <feature> --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694. \`path_required\` \uC815\uCC45\uC77C \uB54C\uB9CC \`--evidence review-trace.json\` \uC744 \uD568\uAED8 \uBD99\uC774\uC138\uC694.`;
3920
+ 10. \uC9C0\uC801\uC0AC\uD56D\uC774 \uB0A8\uC544 \uC788\uC73C\uBA74 \uBA3C\uC800 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' \uB85C \uAE30\uB85D\uD558\uC138\uC694. \uB2E8, \`workflow.prePrReview.evidenceMode=any\` \uC774\uACE0 \uC2E4\uD589 \uC99D\uAC70 \uAC15\uC81C\uAC00 \uC5C6\uC73C\uBA74 \`--evidence\` \uC5C6\uC774 \uC9C1\uC811 \uAE30\uB85D\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3921
+ 11. \uC218\uC815/\uC7AC\uAC80\uC99D \uD6C4 \uCD5C\uC885 \uC2B9\uC778 \uC2DC\uC810\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uAD6C\uC870\uD654\uB41C evidence\uC640 \uD568\uAED8 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694. approve\uB294 evidence \uC5C6\uC774 \uAE30\uB85D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`;
3591
3922
  }
3592
3923
  return `Conduct a pre-PR code review.
3593
3924
  0. Reuse the existing helper/sub-agent for this feature review if one already exists. Default to a single helper agent.
@@ -3595,15 +3926,15 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3595
3926
  2. Split and check the review scope.
3596
3927
  - Main scope: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
3597
3928
  - Worktree scope: 'git diff --name-only', 'git diff --name-only --cached', 'git ls-files --others --exclude-standard'
3598
- 3. Evaluate whether the implementation actually fits the feature intent. Capture concrete \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, and \`residualRisks\`, and explicitly set \`specAlignmentChecked\`.
3599
- 4. Generate a 'review-trace.json' file for all changed files, including \`findingCount\`, \`blockingFindings\`, evaluations for risk, security, perf, maintainability, and specific fileLine locators.
3929
+ 3. Evaluate whether the implementation actually fits the feature intent. Capture concrete \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, \`residualRisks\`, and \`approvalRationale\`, and explicitly set \`specAlignmentChecked\`.
3930
+ 4. Generate a 'review-trace.json' file for all changed files, including \`baseSha\`, \`headSha\`, \`changedFiles\`, \`reviewedFiles\`, \`riskSummaries\` (blocking/important/minor), \`findingCount\`, \`blockingFindings\`, per-file risk/security/perf/maintainability evaluations, and specific \`fileLine\` locators.
3600
3931
  5. The baseline is '${fallbackText}'. Always perform the 'Pre-PR Core Checklist' section of the 'create-pr' document.
3601
3932
  6. Priority skills: ${skills.length > 0 ? skills.join(", ") : "None"} for deeper technical review.
3602
3933
  7. Run extra audit/targeted verification only when the review truly needs more evidence. Spawn additional helper agents only when necessary.
3603
3934
  8. If helper-agent quota is exhausted, continue the review in the main agent and just keep the evidence consistent.
3604
3935
  9. Record commands in \`commandsExecuted\` only when you actually ran them.
3605
- 10. If unresolved findings remain, first record them with 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested'. When \`workflow.prePrReview.evidenceMode=any\` and execution evidence is not enforced, direct record mode without \`--evidence\` is also valid.
3606
- 11. After fixes and re-validation, run 'npx lee-spec-kit pre-pr-review <feature> --decision approve' for final pre-PR approval. Add \`--evidence review-trace.json\` only when the active evidence policy requires a path.`;
3936
+ 10. If unresolved findings remain, first record them with 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested'. When \`workflow.prePrReview.evidenceMode=any\` and execution evidence is not enforced, direct record mode without \`--evidence\` is also valid.
3937
+ 11. After fixes and re-validation, run 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision approve' for final pre-PR approval. Approve never records without structured evidence.`;
3607
3938
  }
3608
3939
  function getCodeReviewPrompt(lang) {
3609
3940
  if (lang === "ko") {
@@ -3743,7 +4074,7 @@ function isNonNegativeIntegerValue(value) {
3743
4074
  }
3744
4075
  function isLikelyCurrentPrePrReviewEvidence(evidencePath, feature) {
3745
4076
  try {
3746
- const raw = fs9.readFileSync(evidencePath, "utf-8");
4077
+ const raw = fs11.readFileSync(evidencePath, "utf-8");
3747
4078
  const parsed = JSON.parse(raw);
3748
4079
  const evidenceFeature = (parsed.feature || "").toString().trim();
3749
4080
  if (evidenceFeature && evidenceFeature !== feature.folderName) {
@@ -3759,31 +4090,31 @@ function resolvePrePrReviewEvidencePath(feature) {
3759
4090
  const candidates = [];
3760
4091
  const explicit = (feature.prePrReview.evidence || "").trim();
3761
4092
  if (explicit && explicit !== "-") {
3762
- if (path12.isAbsolute(explicit)) {
4093
+ if (path14.isAbsolute(explicit)) {
3763
4094
  candidates.push(explicit);
3764
4095
  } else {
3765
- candidates.push(path12.resolve(feature.path, explicit));
3766
- candidates.push(path12.resolve(docsRoot, explicit));
4096
+ candidates.push(path14.resolve(feature.path, explicit));
4097
+ candidates.push(path14.resolve(docsRoot, explicit));
3767
4098
  const normalizedExplicit = explicit.replace(/\\/g, "/");
3768
4099
  if (normalizedExplicit.startsWith("docs/")) {
3769
4100
  const withoutDocsPrefix = normalizedExplicit.slice("docs/".length);
3770
4101
  if (withoutDocsPrefix) {
3771
- candidates.push(path12.resolve(docsRoot, withoutDocsPrefix));
4102
+ candidates.push(path14.resolve(docsRoot, withoutDocsPrefix));
3772
4103
  }
3773
4104
  }
3774
4105
  }
3775
4106
  }
3776
- candidates.push(path12.join(feature.path, "review-trace.json"));
3777
- candidates.push(path12.join(docsRoot, "review-trace.json"));
4107
+ candidates.push(path14.join(feature.path, "review-trace.json"));
4108
+ candidates.push(path14.join(docsRoot, "review-trace.json"));
3778
4109
  const seen = /* @__PURE__ */ new Set();
3779
4110
  for (const candidate of candidates) {
3780
- const abs = path12.resolve(candidate);
4111
+ const abs = path14.resolve(candidate);
3781
4112
  if (seen.has(abs)) continue;
3782
4113
  seen.add(abs);
3783
- if (!fs9.existsSync(abs)) continue;
4114
+ if (!fs11.existsSync(abs)) continue;
3784
4115
  if (!abs.toLowerCase().endsWith(".json")) continue;
3785
4116
  if (!isLikelyCurrentPrePrReviewEvidence(abs, feature)) continue;
3786
- const rel = path12.relative(docsRoot, abs).replace(/\\/g, "/");
4117
+ const rel = path14.relative(docsRoot, abs).replace(/\\/g, "/");
3787
4118
  if (rel && !rel.startsWith("../")) {
3788
4119
  return rel;
3789
4120
  }
@@ -3824,8 +4155,8 @@ function getReviewFixCommitGuidance(feature, lang, options) {
3824
4155
  }
3825
4156
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
3826
4157
  if (!projectGitCwd) return null;
3827
- const normalized = path12.resolve(projectGitCwd);
3828
- const marker = `${path12.sep}.worktrees${path12.sep}`;
4158
+ const normalized = path14.resolve(projectGitCwd);
4159
+ const marker = `${path14.sep}.worktrees${path14.sep}`;
3829
4160
  const markerIndex = normalized.lastIndexOf(marker);
3830
4161
  if (markerIndex <= 0) return null;
3831
4162
  const projectRoot = normalized.slice(0, markerIndex);
@@ -3868,7 +4199,7 @@ function toTaskKey(rawTitle) {
3868
4199
  function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3869
4200
  const docsGitCwd = feature.git.docsGitCwd;
3870
4201
  const tasksRelativePath = normalizeGitRelativePath(
3871
- path12.join(feature.docs.featurePathFromDocs, "tasks.md")
4202
+ path14.join(feature.docs.featurePathFromDocs, "tasks.md")
3872
4203
  );
3873
4204
  const diff = readGitText(ctx, docsGitCwd, [
3874
4205
  "diff",
@@ -3943,7 +4274,7 @@ function checkTaskCommitGate(ctx, feature) {
3943
4274
  return { pass: true };
3944
4275
  }
3945
4276
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
3946
- const relativeDocsDir = path12.relative(projectGitCwd, feature.git.docsGitCwd);
4277
+ const relativeDocsDir = path14.relative(projectGitCwd, feature.git.docsGitCwd);
3947
4278
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
3948
4279
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
3949
4280
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -4325,9 +4656,9 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4325
4656
  const isPrePrReviewCurrent = (f) => prePrReviewPolicy.enabled && workflowPolicy.requirePr && f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && !f.git.docsHasCommitRequiredChanges && !f.git.projectHasUncommittedChanges && (!isPrMetadataConfigured(f) || !f.pr.link) && !isPrePrReviewSatisfied(f, prePrReviewPolicy);
4326
4657
  const isPrePrReviewMetadataMissing = (f) => isPrePrReviewCurrent(f) && !f.docs.prePrReviewFieldExists;
4327
4658
  const isPrePrReviewFixRequired = (f) => isPrePrReviewCurrent(f) && !!f.prePrReview.decisionOutcome && f.prePrReview.decisionOutcome !== "approve";
4328
- const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status !== "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4329
- const isPrePrReviewRunning = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status === "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4330
- const isPrePrReviewRecord = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && (!!resolvePrePrReviewEvidencePath(f) || prePrReviewPolicy.evidenceMode === "any" && !prePrReviewPolicy.enforceExecutionEvidence);
4659
+ const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status !== "Running" && !resolvePrePrReviewEvidencePath(f);
4660
+ const isPrePrReviewRunning = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status === "Running" && !resolvePrePrReviewEvidencePath(f);
4661
+ const isPrePrReviewRecord = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && !!resolvePrePrReviewEvidencePath(f);
4331
4662
  const getPrePrReviewMetadataActions = () => [
4332
4663
  {
4333
4664
  type: "instruction",
@@ -4371,7 +4702,7 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4371
4702
  cmd: buildSelfCliCommand(buildPrePrReviewRunCommandArgs(f))
4372
4703
  }
4373
4704
  ];
4374
- const getPrePrReviewRunningActions = () => [
4705
+ const getPrePrReviewInProgressActions = () => [
4375
4706
  {
4376
4707
  type: "instruction",
4377
4708
  category: "pre_pr_review_run",
@@ -4909,15 +5240,15 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4909
5240
  actions: (f) => getPrePrReviewRunActions(f)
4910
5241
  },
4911
5242
  {
4912
- id: "pre_pr_review_running",
5243
+ id: "pre_pr_review_in_progress",
4913
5244
  phase: "running",
4914
5245
  owner: "subagent",
4915
5246
  category: "pre_pr_review_run",
4916
5247
  when: (f) => isPrePrReviewRunning(f),
4917
- actions: () => getPrePrReviewRunningActions()
5248
+ actions: () => getPrePrReviewInProgressActions()
4918
5249
  },
4919
5250
  {
4920
- id: "pre_pr_review_record",
5251
+ id: "pre_pr_review_record_pending",
4921
5252
  phase: "record",
4922
5253
  owner: "main",
4923
5254
  category: "pre_pr_review_record",
@@ -5559,17 +5890,17 @@ function isGitPathIgnored(ctx, cwd, relativePath) {
5559
5890
  }
5560
5891
  }
5561
5892
  var GIT_WORKTREE_CACHE = /* @__PURE__ */ new Map();
5562
- var WORKTREE_MARKER = `${path12.sep}.worktrees${path12.sep}`;
5893
+ var WORKTREE_MARKER = `${path14.sep}.worktrees${path14.sep}`;
5563
5894
  function resetContextGitCaches() {
5564
5895
  GIT_WORKTREE_CACHE.clear();
5565
5896
  }
5566
5897
  function isManagedWorktreePath(cwd) {
5567
5898
  if (!cwd) return false;
5568
- const normalized = path12.resolve(cwd);
5899
+ const normalized = path14.resolve(cwd);
5569
5900
  return normalized.includes(WORKTREE_MARKER);
5570
5901
  }
5571
5902
  function resolveProjectRootFromGitCwd(cwd) {
5572
- const normalized = path12.resolve(cwd);
5903
+ const normalized = path14.resolve(cwd);
5573
5904
  const markerIndex = normalized.lastIndexOf(WORKTREE_MARKER);
5574
5905
  if (markerIndex <= 0) return normalized;
5575
5906
  const projectRoot = normalized.slice(0, markerIndex);
@@ -5588,7 +5919,7 @@ function getGitTopLevel(ctx, cwd) {
5588
5919
  }
5589
5920
  function listGitWorktrees(ctx, cwd) {
5590
5921
  const topLevel = getGitTopLevel(ctx, cwd) || cwd;
5591
- const cacheKey = path12.resolve(topLevel);
5922
+ const cacheKey = path14.resolve(topLevel);
5592
5923
  const cached = GIT_WORKTREE_CACHE.get(cacheKey);
5593
5924
  if (cached) return cached;
5594
5925
  try {
@@ -5689,12 +6020,12 @@ function countDocumentLines(content) {
5689
6020
  if (lines[lines.length - 1] === "") return lines.length - 1;
5690
6021
  return lines.length;
5691
6022
  }
5692
- function escapeRegExp(value) {
6023
+ function escapeRegExp3(value) {
5693
6024
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5694
6025
  }
5695
6026
  function extractSpecValue(content, key) {
5696
6027
  const regex = new RegExp(
5697
- `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:\\s*(.*)$`,
6028
+ `^\\s*-\\s*\\*\\*${escapeRegExp3(key)}\\*\\*\\s*:\\s*(.*)$`,
5698
6029
  "m"
5699
6030
  );
5700
6031
  const match = content.match(regex);
@@ -5702,7 +6033,7 @@ function extractSpecValue(content, key) {
5702
6033
  }
5703
6034
  function hasSpecKey(content, key) {
5704
6035
  const regex = new RegExp(
5705
- `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:`,
6036
+ `^\\s*-\\s*\\*\\*${escapeRegExp3(key)}\\*\\*\\s*:`,
5706
6037
  "m"
5707
6038
  );
5708
6039
  return regex.test(content);
@@ -5894,7 +6225,7 @@ function splitReviewLogSections(content, headerRegex) {
5894
6225
  }
5895
6226
  function collectStructuredReviewEntries(section, keys) {
5896
6227
  const lines = section.split("\n");
5897
- const escaped = keys.map((key) => escapeRegExp(key));
6228
+ const escaped = keys.map((key) => escapeRegExp3(key));
5898
6229
  const fieldRegex = new RegExp(
5899
6230
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*(.*)$`,
5900
6231
  "i"
@@ -6076,17 +6407,17 @@ function resolveLocalEvidencePathCandidates(rawValue, context) {
6076
6407
  if (!evidencePath) return [];
6077
6408
  if (/^https?:\/\//i.test(evidencePath)) return [];
6078
6409
  const candidates = /* @__PURE__ */ new Set();
6079
- if (path12.isAbsolute(evidencePath)) {
6080
- candidates.add(path12.resolve(evidencePath));
6410
+ if (path14.isAbsolute(evidencePath)) {
6411
+ candidates.add(path14.resolve(evidencePath));
6081
6412
  } else {
6082
- candidates.add(path12.resolve(context.featurePath, evidencePath));
6083
- candidates.add(path12.resolve(context.docsDir, evidencePath));
6084
- candidates.add(path12.resolve(path12.dirname(context.docsDir), evidencePath));
6413
+ candidates.add(path14.resolve(context.featurePath, evidencePath));
6414
+ candidates.add(path14.resolve(context.docsDir, evidencePath));
6415
+ candidates.add(path14.resolve(path14.dirname(context.docsDir), evidencePath));
6085
6416
  const normalizedEvidencePath = evidencePath.replace(/\\/g, "/");
6086
6417
  if (normalizedEvidencePath.startsWith("docs/")) {
6087
6418
  const withoutDocsPrefix = normalizedEvidencePath.slice("docs/".length);
6088
6419
  if (withoutDocsPrefix) {
6089
- candidates.add(path12.resolve(context.docsDir, withoutDocsPrefix));
6420
+ candidates.add(path14.resolve(context.docsDir, withoutDocsPrefix));
6090
6421
  }
6091
6422
  }
6092
6423
  }
@@ -6148,13 +6479,13 @@ function parsePrLink(value) {
6148
6479
  return trimmed;
6149
6480
  }
6150
6481
  function normalizeGitPath(value) {
6151
- return value.split(path12.sep).join("/");
6482
+ return value.split(path14.sep).join("/");
6152
6483
  }
6153
6484
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
6154
- const relativeDocsDir = path12.relative(projectGitCwd, docsDir);
6485
+ const relativeDocsDir = path14.relative(projectGitCwd, docsDir);
6155
6486
  if (!relativeDocsDir) return [];
6156
- if (path12.isAbsolute(relativeDocsDir)) return [];
6157
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path12.sep}`)) {
6487
+ if (path14.isAbsolute(relativeDocsDir)) return [];
6488
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path14.sep}`)) {
6158
6489
  return [];
6159
6490
  }
6160
6491
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(
@@ -6192,7 +6523,7 @@ function getExpectedWorktreeCandidates(projectGitCwd, issueNumber, slug, folderN
6192
6523
  const seen = /* @__PURE__ */ new Set();
6193
6524
  const out = [];
6194
6525
  for (const name of names) {
6195
- const candidate = path12.resolve(projectRoot, ".worktrees", name);
6526
+ const candidate = path14.resolve(projectRoot, ".worktrees", name);
6196
6527
  if (seen.has(candidate)) continue;
6197
6528
  seen.add(candidate);
6198
6529
  out.push(candidate);
@@ -6206,7 +6537,7 @@ function resolveExistingExpectedWorktreePath(projectGitCwd, issueNumber, slug, f
6206
6537
  slug,
6207
6538
  folderName
6208
6539
  )) {
6209
- if (!fs9.existsSync(candidate)) continue;
6540
+ if (!fs11.existsSync(candidate)) continue;
6210
6541
  return candidate;
6211
6542
  }
6212
6543
  return void 0;
@@ -6236,7 +6567,7 @@ function resolveFeatureWorktreePath(ctx, projectGitCwd, issueNumber, slug, folde
6236
6567
  slug,
6237
6568
  folderName
6238
6569
  )) {
6239
- if (!fs9.existsSync(candidate)) continue;
6570
+ if (!fs11.existsSync(candidate)) continue;
6240
6571
  const branchName = getCurrentBranch(ctx, candidate);
6241
6572
  if (!expectedBranchesSet.has(branchName)) continue;
6242
6573
  return {
@@ -6377,10 +6708,10 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6377
6708
  const normalizedCandidates = uniqueNormalizedPaths(
6378
6709
  candidates.map((candidate) => {
6379
6710
  if (!candidate) return "";
6380
- if (!path12.isAbsolute(candidate)) return candidate;
6381
- const relative = path12.relative(projectGitCwd, candidate);
6711
+ if (!path14.isAbsolute(candidate)) return candidate;
6712
+ const relative = path14.relative(projectGitCwd, candidate);
6382
6713
  if (!relative) return "";
6383
- if (relative === ".." || relative.startsWith(`..${path12.sep}`))
6714
+ if (relative === ".." || relative.startsWith(`..${path14.sep}`))
6384
6715
  return "";
6385
6716
  return relative;
6386
6717
  }).filter(Boolean)
@@ -6394,7 +6725,7 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6394
6725
  if (cached) return [...cached];
6395
6726
  const existing = [];
6396
6727
  for (const candidate of normalizedCandidates) {
6397
- if (await ctx.fs.pathExists(path12.join(projectGitCwd, candidate))) {
6728
+ if (await ctx.fs.pathExists(path14.join(projectGitCwd, candidate))) {
6398
6729
  existing.push(candidate);
6399
6730
  }
6400
6731
  }
@@ -6482,16 +6813,16 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6482
6813
  const lang = options.lang;
6483
6814
  const workflowPolicy = resolveWorkflowPolicy(options.workflow);
6484
6815
  const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
6485
- const folderName = path12.basename(featurePath);
6816
+ const folderName = path14.basename(featurePath);
6486
6817
  const match = folderName.match(/^(F\d+)-(.+)$/);
6487
6818
  const id = match?.[1];
6488
6819
  const slug = match?.[2] || folderName;
6489
- const specPath = path12.join(featurePath, "spec.md");
6490
- const planPath = path12.join(featurePath, "plan.md");
6491
- const tasksPath = path12.join(featurePath, "tasks.md");
6492
- const decisionsPath = path12.join(featurePath, "decisions.md");
6493
- const issueDocPath = path12.join(featurePath, "issue.md");
6494
- const prDocPath = path12.join(featurePath, "pr.md");
6820
+ const specPath = path14.join(featurePath, "spec.md");
6821
+ const planPath = path14.join(featurePath, "plan.md");
6822
+ const tasksPath = path14.join(featurePath, "tasks.md");
6823
+ const decisionsPath = path14.join(featurePath, "decisions.md");
6824
+ const issueDocPath = path14.join(featurePath, "issue.md");
6825
+ const prDocPath = path14.join(featurePath, "pr.md");
6495
6826
  let specStatus;
6496
6827
  let issueNumber;
6497
6828
  const specExists = await ctx.fs.pathExists(specPath);
@@ -6753,7 +7084,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6753
7084
  } else if (workflowPolicy.requireWorktree && tasksSummary.total > tasksSummary.done && !projectInManagedWorktree) {
6754
7085
  warnings.push(tr(lang, "warnings", "workflowWorktreeRequired"));
6755
7086
  }
6756
- const relativeFeaturePathFromDocs = path12.relative(
7087
+ const relativeFeaturePathFromDocs = path14.relative(
6757
7088
  context.docsDir,
6758
7089
  featurePath
6759
7090
  );
@@ -7116,7 +7447,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7116
7447
  async function listFeatureDirs(ctx, rootDir) {
7117
7448
  const dirs = await listSubdirectories(ctx.fs, rootDir);
7118
7449
  return dirs.filter(
7119
- (value) => path12.basename(value).trim().toLowerCase() !== "feature-base"
7450
+ (value) => path14.basename(value).trim().toLowerCase() !== "feature-base"
7120
7451
  );
7121
7452
  }
7122
7453
  function normalizeRelPath(value) {
@@ -7257,7 +7588,7 @@ async function scanFeatures(ctx) {
7257
7588
  if (config.projectType === "single") {
7258
7589
  const featureDirs = await listFeatureDirs(
7259
7590
  ctx,
7260
- path12.join(config.docsDir, "features")
7591
+ path14.join(config.docsDir, "features")
7261
7592
  );
7262
7593
  componentFeatureDirs.set("single", featureDirs);
7263
7594
  allFeatureDirs.push(...featureDirs);
@@ -7269,14 +7600,14 @@ async function scanFeatures(ctx) {
7269
7600
  for (const component of components) {
7270
7601
  const componentDirs = await listFeatureDirs(
7271
7602
  ctx,
7272
- path12.join(config.docsDir, "features", component)
7603
+ path14.join(config.docsDir, "features", component)
7273
7604
  );
7274
7605
  componentFeatureDirs.set(component, componentDirs);
7275
7606
  allFeatureDirs.push(...componentDirs);
7276
7607
  }
7277
7608
  }
7278
7609
  const relativeFeaturePaths = allFeatureDirs.map(
7279
- (dir) => normalizeRelPath(path12.relative(config.docsDir, dir))
7610
+ (dir) => normalizeRelPath(path14.relative(config.docsDir, dir))
7280
7611
  );
7281
7612
  const docsGitMeta = buildDocsFeatureGitMeta(
7282
7613
  ctx,
@@ -7293,7 +7624,7 @@ async function scanFeatures(ctx) {
7293
7624
  const parsed = await Promise.all(
7294
7625
  target.dirs.map(async (dir) => {
7295
7626
  const relativeFeaturePathFromDocs = normalizeRelPath(
7296
- path12.relative(config.docsDir, dir)
7627
+ path14.relative(config.docsDir, dir)
7297
7628
  );
7298
7629
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
7299
7630
  return parseFeature(
@@ -7345,8 +7676,8 @@ function statusCommand(program2) {
7345
7676
  const cliError = toCliError(error);
7346
7677
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7347
7678
  console.error(
7348
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7349
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
7679
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
7680
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7350
7681
  );
7351
7682
  printCliErrorSuggestions(suggestions, lang);
7352
7683
  process.exitCode = 1;
@@ -7363,13 +7694,13 @@ async function runStatus(options) {
7363
7694
  );
7364
7695
  }
7365
7696
  const { docsDir, projectType, projectName, lang } = ctx.config;
7366
- const featuresDir = path12.join(docsDir, "features");
7697
+ const featuresDir = path14.join(docsDir, "features");
7367
7698
  const scan = await scanFeatures(ctx);
7368
7699
  const features = [];
7369
7700
  const idMap = /* @__PURE__ */ new Map();
7370
7701
  for (const f of scan.features) {
7371
7702
  const id = f.id || "UNKNOWN";
7372
- const relPath = path12.relative(docsDir, f.path);
7703
+ const relPath = path14.relative(docsDir, f.path);
7373
7704
  if (!idMap.has(id)) idMap.set(id, []);
7374
7705
  idMap.get(id).push(relPath);
7375
7706
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -7443,7 +7774,7 @@ async function runStatus(options) {
7443
7774
  return;
7444
7775
  }
7445
7776
  if (features.length === 0) {
7446
- console.log(chalk8.yellow(tr(lang, "cli", "status.noFeatures")));
7777
+ console.log(chalk9.yellow(tr(lang, "cli", "status.noFeatures")));
7447
7778
  return;
7448
7779
  }
7449
7780
  features.sort((a, b) => a.id.localeCompare(b.id));
@@ -7453,14 +7784,14 @@ async function runStatus(options) {
7453
7784
  console.log(header);
7454
7785
  console.log(separator);
7455
7786
  for (const f of features) {
7456
- const statusColor = f.status === "WORKFLOW_DONE" ? chalk8.green : f.status === "DONE" ? chalk8.cyan : f.status === "DOING" ? chalk8.yellow : chalk8.gray;
7787
+ const statusColor = f.status === "WORKFLOW_DONE" ? chalk9.green : f.status === "DONE" ? chalk9.cyan : f.status === "DOING" ? chalk9.yellow : chalk9.gray;
7457
7788
  console.log(
7458
7789
  `| ${f.id} | ${f.name} | ${f.repo} | ${f.issue} | ${statusColor(f.status)} | ${f.progress} | ${f.step} | ${f.substate} | ${f.path} |`
7459
7790
  );
7460
7791
  }
7461
7792
  console.log();
7462
7793
  if (options.write) {
7463
- const outputPath = path12.join(featuresDir, "status.md");
7794
+ const outputPath = path14.join(featuresDir, "status.md");
7464
7795
  const date = getLocalDateString();
7465
7796
  const content = [
7466
7797
  "# Feature Status",
@@ -7477,22 +7808,22 @@ async function runStatus(options) {
7477
7808
  ].join("\n");
7478
7809
  await ctx.fs.writeFile(outputPath, content, "utf-8");
7479
7810
  console.log(
7480
- chalk8.green(tr(lang, "cli", "status.wrote", { path: outputPath }))
7811
+ chalk9.green(tr(lang, "cli", "status.wrote", { path: outputPath }))
7481
7812
  );
7482
7813
  }
7483
7814
  }
7484
- function escapeRegExp2(value) {
7815
+ function escapeRegExp4(value) {
7485
7816
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7486
7817
  }
7487
7818
  async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallbackFolderName) {
7488
7819
  try {
7489
- const specPath = path12.join(featureDir, "spec.md");
7820
+ const specPath = path14.join(featureDir, "spec.md");
7490
7821
  if (!await fsAdapter.pathExists(specPath)) return fallbackSlug;
7491
7822
  const content = await fsAdapter.readFile(specPath, "utf-8");
7492
7823
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
7493
7824
  for (const key of keys) {
7494
7825
  const regex = new RegExp(
7495
- `^\\s*-\\s*\\*\\*${escapeRegExp2(key)}\\*\\*\\s*:\\s*(.*)$`,
7826
+ `^\\s*-\\s*\\*\\*${escapeRegExp4(key)}\\*\\*\\s*:\\s*(.*)$`,
7496
7827
  "m"
7497
7828
  );
7498
7829
  const match = content.match(regex);
@@ -7514,15 +7845,15 @@ function updateCommand(program2) {
7514
7845
  const config = await getConfig(process.cwd());
7515
7846
  const lang = config?.lang ?? DEFAULT_LANG;
7516
7847
  if (error instanceof Error && error.message === "canceled") {
7517
- console.log(chalk8.yellow(`
7848
+ console.log(chalk9.yellow(`
7518
7849
  ${tr(lang, "cli", "common.canceled")}`));
7519
7850
  return;
7520
7851
  }
7521
7852
  const cliError = toCliError(error);
7522
7853
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7523
7854
  console.error(
7524
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7525
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
7855
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
7856
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7526
7857
  );
7527
7858
  printCliErrorSuggestions(suggestions, lang);
7528
7859
  process.exitCode = 1;
@@ -7552,26 +7883,26 @@ async function runUpdate(options) {
7552
7883
  const updateAgentsMd = options.agentsMd || !hasExplicitSelection;
7553
7884
  const updateTemplates = options.templates || !hasExplicitSelection;
7554
7885
  const agentsMode = options.skills && !options.agents ? "skills" : "all";
7555
- console.log(chalk8.blue(tr(lang, "cli", "update.start")));
7556
- console.log(chalk8.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
7886
+ console.log(chalk9.blue(tr(lang, "cli", "update.start")));
7887
+ console.log(chalk9.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
7557
7888
  console.log(
7558
- chalk8.gray(` - ${tr(lang, "cli", "update.typeLabel")}: ${projectType}`)
7889
+ chalk9.gray(` - ${tr(lang, "cli", "update.typeLabel")}: ${projectType}`)
7559
7890
  );
7560
7891
  console.log();
7561
7892
  let updatedCount = 0;
7562
7893
  if (updateAgents) {
7563
7894
  if (agentsMode === "skills") {
7564
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingSkills")));
7895
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingSkills")));
7565
7896
  console.log(
7566
- chalk8.gray(tr(lang, "cli", "update.engineManagedSkillsBuiltin"))
7897
+ chalk9.gray(tr(lang, "cli", "update.engineManagedSkillsBuiltin"))
7567
7898
  );
7568
- console.log(chalk8.green(` \u2705 ${tr(lang, "cli", "update.skillsUpdated")}`));
7899
+ console.log(chalk9.green(` \u2705 ${tr(lang, "cli", "update.skillsUpdated")}`));
7569
7900
  } else {
7570
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingAgents")));
7901
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingAgents")));
7571
7902
  }
7572
7903
  if (agentsMode === "all") {
7573
- const commonAgentsBase = path12.join(templatesDir, lang, "common", "agents");
7574
- const targetAgentsBase = path12.join(docsDir, "agents");
7904
+ const commonAgentsBase = path14.join(templatesDir, lang, "common", "agents");
7905
+ const targetAgentsBase = path14.join(docsDir, "agents");
7575
7906
  const commonAgents = commonAgentsBase;
7576
7907
  const targetAgents = targetAgentsBase;
7577
7908
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
@@ -7599,7 +7930,7 @@ async function runUpdate(options) {
7599
7930
  updatedCount += count;
7600
7931
  }
7601
7932
  console.log(
7602
- chalk8.green(
7933
+ chalk9.green(
7603
7934
  ` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`
7604
7935
  )
7605
7936
  );
@@ -7618,13 +7949,13 @@ async function runUpdate(options) {
7618
7949
  }
7619
7950
  }
7620
7951
  if (updateTemplates) {
7621
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingFeatureBase")));
7622
- console.log(chalk8.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
7952
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingFeatureBase")));
7953
+ console.log(chalk9.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
7623
7954
  }
7624
7955
  const pruned = await pruneEngineManagedDocs(docsDir);
7625
7956
  if (pruned.length > 0) {
7626
7957
  console.log(
7627
- chalk8.gray(
7958
+ chalk9.gray(
7628
7959
  ` - ${tr(lang, "cli", "update.engineManagedPruned", {
7629
7960
  count: pruned.length
7630
7961
  })}`
@@ -7634,18 +7965,18 @@ async function runUpdate(options) {
7634
7965
  console.log();
7635
7966
  if (configBackfill.changed) {
7636
7967
  console.log(
7637
- chalk8.gray(
7968
+ chalk9.gray(
7638
7969
  ` - ${tr(lang, "cli", "update.fileUpdated", { file: ".lee-spec-kit.json" })}`
7639
7970
  )
7640
7971
  );
7641
7972
  console.log(
7642
- chalk8.gray(
7973
+ chalk9.gray(
7643
7974
  ` (${configBackfill.changedPaths.join(", ")})`
7644
7975
  )
7645
7976
  );
7646
7977
  }
7647
7978
  console.log(
7648
- chalk8.green(`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`)
7979
+ chalk9.green(`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`)
7649
7980
  );
7650
7981
  },
7651
7982
  { owner: "update" }
@@ -7669,21 +8000,21 @@ async function collectAgentsMdTargets(cwd, config) {
7669
8000
  const targets = /* @__PURE__ */ new Set();
7670
8001
  const docsRepo = config.docsRepo ?? "embedded";
7671
8002
  if (docsRepo === "embedded") {
7672
- const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path12.resolve(config.docsDir, "..");
7673
- targets.add(path12.join(repoRoot, "AGENTS.md"));
8003
+ const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path14.resolve(config.docsDir, "..");
8004
+ targets.add(path14.join(repoRoot, "AGENTS.md"));
7674
8005
  return [...targets];
7675
8006
  }
7676
- targets.add(path12.join(config.docsDir, "AGENTS.md"));
8007
+ targets.add(path14.join(config.docsDir, "AGENTS.md"));
7677
8008
  const baseDir = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || process.cwd();
7678
8009
  const rawRoots = typeof config.projectRoot === "string" ? [config.projectRoot] : config.projectRoot && typeof config.projectRoot === "object" ? Object.values(config.projectRoot) : [];
7679
8010
  for (const rawRoot of rawRoots) {
7680
8011
  const value = String(rawRoot || "").trim();
7681
8012
  if (!value) continue;
7682
- const resolved = path12.resolve(baseDir, value);
8013
+ const resolved = path14.resolve(baseDir, value);
7683
8014
  if (!await fs.pathExists(resolved)) continue;
7684
8015
  const stat = await fs.stat(resolved);
7685
8016
  if (!stat.isDirectory()) continue;
7686
- targets.add(path12.join(resolved, "AGENTS.md"));
8017
+ targets.add(path14.join(resolved, "AGENTS.md"));
7687
8018
  }
7688
8019
  return [...targets];
7689
8020
  }
@@ -7723,7 +8054,7 @@ function normalizeDecisionEnumList2(raw) {
7723
8054
  return [...deduped];
7724
8055
  }
7725
8056
  async function backfillMissingConfigDefaults(docsDir) {
7726
- const configPath = path12.join(docsDir, ".lee-spec-kit.json");
8057
+ const configPath = path14.join(docsDir, ".lee-spec-kit.json");
7727
8058
  if (!await fs.pathExists(configPath)) {
7728
8059
  return { changed: false, changedPaths: [] };
7729
8060
  }
@@ -7846,8 +8177,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7846
8177
  const files = await fs.readdir(sourceDir);
7847
8178
  let updatedCount = 0;
7848
8179
  for (const file of files) {
7849
- const sourcePath = path12.join(sourceDir, file);
7850
- const targetPath = path12.join(targetDir, file);
8180
+ const sourcePath = path14.join(sourceDir, file);
8181
+ const targetPath = path14.join(targetDir, file);
7851
8182
  const stat = await fs.stat(sourcePath);
7852
8183
  if (stat.isFile()) {
7853
8184
  if (protectedFiles.has(file)) {
@@ -7865,7 +8196,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7865
8196
  }
7866
8197
  if (!force) {
7867
8198
  console.log(
7868
- chalk8.yellow(
8199
+ chalk9.yellow(
7869
8200
  ` \u26A0\uFE0F ${file} - ${tr(lang, "cli", "update.changeDetected")}`
7870
8201
  )
7871
8202
  );
@@ -7875,7 +8206,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7875
8206
  if (shouldUpdate) {
7876
8207
  await fs.writeFile(targetPath, sourceContent);
7877
8208
  console.log(
7878
- chalk8.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
8209
+ chalk9.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
7879
8210
  );
7880
8211
  updatedCount++;
7881
8212
  }
@@ -7930,7 +8261,7 @@ function extractPorcelainPaths(line) {
7930
8261
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
7931
8262
  const top = getGitTopLevel2(docsDir);
7932
8263
  if (!top) return null;
7933
- const rel = path12.relative(top, docsDir) || ".";
8264
+ const rel = path14.relative(top, docsDir) || ".";
7934
8265
  try {
7935
8266
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
7936
8267
  cwd: top,
@@ -7942,7 +8273,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
7942
8273
  }
7943
8274
  const ignoredRelPaths = new Set(
7944
8275
  ignoredAbsPaths.map(
7945
- (absPath) => normalizeGitPath2(path12.relative(top, absPath) || ".")
8276
+ (absPath) => normalizeGitPath2(path14.relative(top, absPath) || ".")
7946
8277
  )
7947
8278
  );
7948
8279
  const filtered = output.split("\n").filter((line) => {
@@ -7980,7 +8311,7 @@ function configCommand(program2) {
7980
8311
  if (error instanceof Error && error.message === "canceled") {
7981
8312
  const config2 = await getConfig(process.cwd());
7982
8313
  const lang2 = config2?.lang ?? DEFAULT_LANG;
7983
- console.log(chalk8.yellow(`
8314
+ console.log(chalk9.yellow(`
7984
8315
  ${tr(lang2, "cli", "common.canceled")}`));
7985
8316
  return;
7986
8317
  }
@@ -7989,8 +8320,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
7989
8320
  const cliError = toCliError(error);
7990
8321
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7991
8322
  console.error(
7992
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7993
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
8323
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
8324
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7994
8325
  );
7995
8326
  printCliErrorSuggestions(suggestions, lang);
7996
8327
  process.exitCode = 1;
@@ -8000,7 +8331,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
8000
8331
  }
8001
8332
  async function runConfig(options) {
8002
8333
  const cwd = process.cwd();
8003
- const targetCwd = options.dir ? path12.resolve(cwd, options.dir) : cwd;
8334
+ const targetCwd = options.dir ? path14.resolve(cwd, options.dir) : cwd;
8004
8335
  const config = await getConfig(targetCwd);
8005
8336
  if (!config) {
8006
8337
  throw createCliError(
@@ -8008,13 +8339,13 @@ async function runConfig(options) {
8008
8339
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
8009
8340
  );
8010
8341
  }
8011
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
8342
+ const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
8012
8343
  if (!options.projectRoot) {
8013
8344
  console.log();
8014
- console.log(chalk8.blue(tr(config.lang, "cli", "config.currentTitle")));
8345
+ console.log(chalk9.blue(tr(config.lang, "cli", "config.currentTitle")));
8015
8346
  console.log();
8016
8347
  console.log(
8017
- chalk8.gray(
8348
+ chalk9.gray(
8018
8349
  ` ${tr(config.lang, "cli", "config.pathLabel")}: ${configPath}`
8019
8350
  )
8020
8351
  );
@@ -8031,7 +8362,7 @@ async function runConfig(options) {
8031
8362
  const configFile = await fs.readJson(configPath);
8032
8363
  if (configFile.docsRepo !== "standalone") {
8033
8364
  console.log(
8034
- chalk8.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
8365
+ chalk9.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
8035
8366
  );
8036
8367
  return;
8037
8368
  }
@@ -8081,7 +8412,7 @@ async function runConfig(options) {
8081
8412
  currentRoot[targetComponent] = projectRoot;
8082
8413
  configFile.projectRoot = currentRoot;
8083
8414
  console.log(
8084
- chalk8.green(
8415
+ chalk9.green(
8085
8416
  tr(config.lang, "cli", "config.projectRootSet", {
8086
8417
  repo: targetComponent.toUpperCase(),
8087
8418
  path: projectRoot
@@ -8097,7 +8428,7 @@ async function runConfig(options) {
8097
8428
  }
8098
8429
  configFile.projectRoot = projectRoot;
8099
8430
  console.log(
8100
- chalk8.green(
8431
+ chalk9.green(
8101
8432
  tr(config.lang, "cli", "config.projectRootSetSingle", {
8102
8433
  path: projectRoot
8103
8434
  })
@@ -8114,47 +8445,47 @@ var BUILTIN_DOC_DEFINITIONS = [
8114
8445
  {
8115
8446
  id: "agents",
8116
8447
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
8117
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "agents.md")
8448
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "agents.md")
8118
8449
  },
8119
8450
  {
8120
8451
  id: "git-workflow",
8121
8452
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
8122
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "git-workflow.md")
8453
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "git-workflow.md")
8123
8454
  },
8124
8455
  {
8125
8456
  id: "issue-doc",
8126
8457
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
8127
- relativePath: (_, lang) => path12.join(lang, "common", "features", "feature-base", "issue.md")
8458
+ relativePath: (_, lang) => path14.join(lang, "common", "features", "feature-base", "issue.md")
8128
8459
  },
8129
8460
  {
8130
8461
  id: "pr-doc",
8131
8462
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
8132
- relativePath: (_, lang) => path12.join(lang, "common", "features", "feature-base", "pr.md")
8463
+ relativePath: (_, lang) => path14.join(lang, "common", "features", "feature-base", "pr.md")
8133
8464
  },
8134
8465
  {
8135
8466
  id: "create-feature",
8136
8467
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
8137
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-feature.md")
8468
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-feature.md")
8138
8469
  },
8139
8470
  {
8140
8471
  id: "execute-task",
8141
8472
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
8142
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "execute-task.md")
8473
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "execute-task.md")
8143
8474
  },
8144
8475
  {
8145
8476
  id: "create-issue",
8146
8477
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
8147
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-issue.md")
8478
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-issue.md")
8148
8479
  },
8149
8480
  {
8150
8481
  id: "create-pr",
8151
8482
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
8152
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-pr.md")
8483
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-pr.md")
8153
8484
  },
8154
8485
  {
8155
8486
  id: "split-feature",
8156
8487
  title: { ko: "feature \uBD84\uD560 \uAC00\uC774\uB4DC", en: "feature split guide" },
8157
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "split-feature.md")
8488
+ relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "split-feature.md")
8158
8489
  }
8159
8490
  ];
8160
8491
  var DOC_FOLLOWUPS = {
@@ -8251,7 +8582,7 @@ function listBuiltinDocs(projectType, lang) {
8251
8582
  id: doc.id,
8252
8583
  title: doc.title[lang],
8253
8584
  relativePath,
8254
- absolutePath: path12.join(templatesDir, relativePath)
8585
+ absolutePath: path14.join(templatesDir, relativePath)
8255
8586
  };
8256
8587
  });
8257
8588
  }
@@ -8661,6 +8992,13 @@ async function resolveContextSelection(ctx, featureName, options) {
8661
8992
 
8662
8993
  // src/services/ContextPresenter.ts
8663
8994
  function getActionExecutionMetadata(action) {
8995
+ if (action.category === "task_execute" && action.taskExecutePhase === "start") {
8996
+ return {
8997
+ handoffOnly: true,
8998
+ advancesWorkflow: false,
8999
+ nextMainState: "task_complete"
9000
+ };
9001
+ }
8664
9002
  if (action.category === "code_review_run") {
8665
9003
  return {
8666
9004
  handoffOnly: true,
@@ -8672,7 +9010,7 @@ function getActionExecutionMetadata(action) {
8672
9010
  return {
8673
9011
  handoffOnly: true,
8674
9012
  advancesWorkflow: false,
8675
- nextMainState: "pre_pr_review_running"
9013
+ nextMainState: "pre_pr_review_in_progress"
8676
9014
  };
8677
9015
  }
8678
9016
  return null;
@@ -8745,6 +9083,7 @@ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable, autoRunC
8745
9083
  pauseAndReportWhen: [
8746
9084
  "approvalRequest.required=true",
8747
9085
  "AUTO_GATE_REACHED",
9086
+ "AUTO_DELEGATED_HANDOFF",
8748
9087
  "AUTO_MANUAL_REQUIRED",
8749
9088
  "command execution error"
8750
9089
  ],
@@ -8830,7 +9169,7 @@ function buildDelegatedActionContract(state) {
8830
9169
  const feature = state.matchedFeature;
8831
9170
  const substateId = feature?.currentSubstateId;
8832
9171
  if (!feature || !substateId) return null;
8833
- if (substateId === "pre_pr_review_run" || substateId === "pre_pr_review_running") {
9172
+ if (substateId === "pre_pr_review_run" || substateId === "pre_pr_review_in_progress") {
8834
9173
  return {
8835
9174
  required: true,
8836
9175
  mode: "command",
@@ -8839,8 +9178,8 @@ function buildDelegatedActionContract(state) {
8839
9178
  delegatedWorkRequired: true,
8840
9179
  handoffOnly: true,
8841
9180
  advancesWorkflow: false,
8842
- doNotReapproveSameLabel: substateId === "pre_pr_review_running",
8843
- nextMainState: "pre_pr_review_running",
9181
+ doNotReapproveSameLabel: substateId === "pre_pr_review_in_progress",
9182
+ nextMainState: "pre_pr_review_in_progress",
8844
9183
  reuseKey: `pre-pr:${feature.folderName}`,
8845
9184
  evidenceFile: "review-trace.json",
8846
9185
  nextStepRequirement: "generate_review_trace_then_record",
@@ -8851,7 +9190,7 @@ function buildDelegatedActionContract(state) {
8851
9190
  ),
8852
9191
  approve: buildPrePrRecordCommand(feature, "approve")
8853
9192
  },
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."
9193
+ guidance: substateId === "pre_pr_review_in_progress" ? "A pre-PR review is already in progress. Reuse or resume the delegated review, generate structured review evidence, 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
9194
  };
8856
9195
  }
8857
9196
  if (substateId === "code_review_run" || substateId === "code_review_running") {
@@ -9083,12 +9422,12 @@ function printSuggestionOptions(lang, suggestionOptions) {
9083
9422
  if (suggestionOptions.length === 0) return;
9084
9423
  const finalPrompt = buildSuggestionFinalPrompt(lang, suggestionOptions);
9085
9424
  console.log(
9086
- chalk8.green(chalk8.bold(`\u{1F449} ${tr(lang, "cli", "context.suggestionHeader")}`))
9425
+ chalk9.green(chalk9.bold(`\u{1F449} ${tr(lang, "cli", "context.suggestionHeader")}`))
9087
9426
  );
9088
9427
  suggestionOptions.forEach((option) => {
9089
9428
  console.log(` ${option.label}: ${option.summary}`);
9090
9429
  console.log(
9091
- chalk8.gray(
9430
+ chalk9.gray(
9092
9431
  ` \u21B3 ${tr(lang, "cli", "context.suggestionCommandHint", {
9093
9432
  command: option.command
9094
9433
  })}`
@@ -9096,7 +9435,7 @@ function printSuggestionOptions(lang, suggestionOptions) {
9096
9435
  );
9097
9436
  });
9098
9437
  if (finalPrompt) {
9099
- console.log(chalk8.cyan(` \u21B3 ${finalPrompt}`));
9438
+ console.log(chalk9.cyan(` \u21B3 ${finalPrompt}`));
9100
9439
  }
9101
9440
  }
9102
9441
  function buildRequiredDocHints(actionOptions) {
@@ -9288,7 +9627,7 @@ function getApprovalSessionId() {
9288
9627
  function getApprovalTicketPaths(config) {
9289
9628
  return {
9290
9629
  runtimePath: getApprovalTicketStorePath(config.docsDir),
9291
- legacyPath: path12.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9630
+ legacyPath: path14.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9292
9631
  };
9293
9632
  }
9294
9633
  async function loadApprovalTicketStore(storePath) {
@@ -9302,7 +9641,7 @@ async function loadApprovalTicketStore(storePath) {
9302
9641
  }
9303
9642
  }
9304
9643
  async function saveApprovalTicketStore(storePath, payload) {
9305
- await fs.ensureDir(path12.dirname(storePath));
9644
+ await fs.ensureDir(path14.dirname(storePath));
9306
9645
  await fs.writeJson(storePath, payload, { spaces: 2 });
9307
9646
  }
9308
9647
  function pruneApprovalTickets(tickets, nowMs) {
@@ -9488,6 +9827,14 @@ function getCommandExecutionLockPath(action, config) {
9488
9827
  return getProjectExecutionLockPath(action.cwd);
9489
9828
  }
9490
9829
  function buildApprovedHandoffMetadata(action, featureRef) {
9830
+ if (action.category === "task_execute" && action.taskExecutePhase === "start") {
9831
+ const taskId = action.cmd.match(/\b--task\s+([^\s]+)/)?.[1];
9832
+ return {
9833
+ delegatedWorkRequired: true,
9834
+ doNotReapproveSameLabel: true,
9835
+ reuseKey: taskId ? `task:${featureRef}:${taskId}` : `task:${featureRef}`
9836
+ };
9837
+ }
9491
9838
  if (action.category === "pre_pr_review_run") {
9492
9839
  return {
9493
9840
  delegatedWorkRequired: true,
@@ -9633,10 +9980,10 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9633
9980
  return;
9634
9981
  }
9635
9982
  console.log();
9636
- console.log(chalk8.green(`\u2705 Approved option: ${parsedLabel}`));
9637
- console.log(chalk8.gray(` - Action: ${freshSelected.detail}`));
9983
+ console.log(chalk9.green(`\u2705 Approved option: ${parsedLabel}`));
9984
+ console.log(chalk9.gray(` - Action: ${freshSelected.detail}`));
9638
9985
  if (userRequest) {
9639
- console.log(chalk8.gray(` - User request: ${userRequest}`));
9986
+ console.log(chalk9.gray(` - User request: ${userRequest}`));
9640
9987
  }
9641
9988
  if (selectedAction.type === "command") {
9642
9989
  const selectedComponent = selectionOptions.component || "";
@@ -9647,24 +9994,24 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9647
9994
  `--ticket ${ticket.token}`
9648
9995
  );
9649
9996
  console.log(
9650
- chalk8.gray(
9997
+ chalk9.gray(
9651
9998
  ` - Ticket: ${ticket.token} (expires: ${ticket.expiresAt})`
9652
9999
  )
9653
10000
  );
9654
10001
  } else {
9655
10002
  executeCommand = executeCommand.replace(" [--ticket <TICKET>]", "");
9656
10003
  }
9657
- console.log(chalk8.gray(` - Run with: ${executeCommand}`));
10004
+ console.log(chalk9.gray(` - Run with: ${executeCommand}`));
9658
10005
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9659
10006
  console.log(
9660
- chalk8.gray(
10007
+ chalk9.gray(
9661
10008
  " - This command prepares a handoff only; complete the delegated work and update workflow evidence before re-running context."
9662
10009
  )
9663
10010
  );
9664
10011
  }
9665
10012
  } else {
9666
10013
  console.log(
9667
- chalk8.gray(" - Instruction-only action (no command execution).")
10014
+ chalk9.gray(" - Instruction-only action (no command execution).")
9668
10015
  );
9669
10016
  }
9670
10017
  console.log();
@@ -9715,19 +10062,19 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9715
10062
  }
9716
10063
  console.log();
9717
10064
  console.log(
9718
- chalk8.yellow(`\u26A0\uFE0F Approved label ${parsedLabel} is instruction-only.`)
10065
+ chalk9.yellow(`\u26A0\uFE0F Approved label ${parsedLabel} is instruction-only.`)
9719
10066
  );
9720
10067
  if (userRequest) {
9721
- console.log(chalk8.gray(` User request: ${userRequest}`));
10068
+ console.log(chalk9.gray(` User request: ${userRequest}`));
9722
10069
  }
9723
- console.log(chalk8.gray(` ${selectedAction.message}`));
10070
+ console.log(chalk9.gray(` ${selectedAction.message}`));
9724
10071
  console.log();
9725
10072
  return;
9726
10073
  }
9727
10074
  if (!jsonMode) {
9728
10075
  console.log();
9729
- console.log(chalk8.blue(`\u25B6 Executing option ${parsedLabel}...`));
9730
- console.log(chalk8.gray(` ${selectedAction.cmd}`));
10076
+ console.log(chalk9.blue(`\u25B6 Executing option ${parsedLabel}...`));
10077
+ console.log(chalk9.gray(` ${selectedAction.cmd}`));
9731
10078
  console.log();
9732
10079
  }
9733
10080
  try {
@@ -9789,14 +10136,14 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9789
10136
  }
9790
10137
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9791
10138
  console.log(
9792
- chalk8.yellow(
10139
+ chalk9.yellow(
9793
10140
  "Prepared handoff only. Complete the delegated work and update workflow evidence before re-running context."
9794
10141
  )
9795
10142
  );
9796
10143
  console.log();
9797
10144
  return;
9798
10145
  }
9799
- console.log(chalk8.green(`\u2705 Executed option ${parsedLabel}.`));
10146
+ console.log(chalk9.green(`\u2705 Executed option ${parsedLabel}.`));
9800
10147
  console.log();
9801
10148
  } catch (error) {
9802
10149
  const message = error instanceof Error ? error.message : String(error);
@@ -9844,8 +10191,8 @@ function contextCommand(program2) {
9844
10191
  );
9845
10192
  } else {
9846
10193
  console.error(
9847
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
9848
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
10194
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
10195
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
9849
10196
  );
9850
10197
  printCliErrorSuggestions(suggestions, lang);
9851
10198
  }
@@ -10155,7 +10502,7 @@ async function runContext(featureName, options) {
10155
10502
  untilCategories: autoRunPlan.untilCategories,
10156
10503
  unknownCategories: autoRunPlan.unknownCategories,
10157
10504
  manualBoundary: autoRunPlan.manualBoundary,
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.'
10505
+ 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`, when auto mode reaches configured gate categories, or when a delegated handoff pause must be resumed.'
10159
10506
  },
10160
10507
  approvalRequest: {
10161
10508
  guidance: approvalGuidance.replace(
@@ -10214,11 +10561,11 @@ async function runContext(featureName, options) {
10214
10561
  return;
10215
10562
  }
10216
10563
  console.log();
10217
- console.log(chalk8.bold(tr(lang, "cli", "context.header")));
10564
+ console.log(chalk9.bold(tr(lang, "cli", "context.header")));
10218
10565
  if (config.projectType === "single") {
10219
10566
  if (state.branches.project.single) {
10220
10567
  console.log(
10221
- chalk8.gray(
10568
+ chalk9.gray(
10222
10569
  ` (Detected from Project Branch: ${state.branches.project.single})`
10223
10570
  )
10224
10571
  );
@@ -10227,7 +10574,7 @@ async function runContext(featureName, options) {
10227
10574
  const branchName = state.branches.project[selectedComponent] || "";
10228
10575
  if (branchName) {
10229
10576
  console.log(
10230
- chalk8.gray(
10577
+ chalk9.gray(
10231
10578
  ` (Detected from Project Branch: ${selectedComponent.toUpperCase()} ${branchName})`
10232
10579
  )
10233
10580
  );
@@ -10236,37 +10583,37 @@ async function runContext(featureName, options) {
10236
10583
  const parts = Object.entries(state.branches.project).filter(([key, value]) => key !== "single" && !!value).map(([key, value]) => `${key.toUpperCase()} ${value}`);
10237
10584
  if (parts.length > 0) {
10238
10585
  console.log(
10239
- chalk8.gray(` (Detected from Project Branch: ${parts.join(" / ")})`)
10586
+ chalk9.gray(` (Detected from Project Branch: ${parts.join(" / ")})`)
10240
10587
  );
10241
10588
  }
10242
10589
  }
10243
10590
  if (config.docsRepo === "standalone" && state.branches.docs) {
10244
- console.log(chalk8.gray(` (Docs Branch: ${state.branches.docs})`));
10591
+ console.log(chalk9.gray(` (Docs Branch: ${state.branches.docs})`));
10245
10592
  }
10246
- console.log(chalk8.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
10593
+ console.log(chalk9.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
10247
10594
  console.log();
10248
10595
  if (state.features.length === 0) {
10249
- console.log(chalk8.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10596
+ console.log(chalk9.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10250
10597
  console.log();
10251
10598
  printSuggestionOptions(lang, suggestionOptions);
10252
10599
  console.log();
10253
10600
  return;
10254
10601
  }
10255
10602
  if (state.warnings.length > 0) {
10256
- console.log(chalk8.yellow(tr(lang, "cli", "context.envWarnings")));
10257
- state.warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
10603
+ console.log(chalk9.yellow(tr(lang, "cli", "context.envWarnings")));
10604
+ state.warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
10258
10605
  console.log();
10259
10606
  }
10260
10607
  if (state.targetFeatures.length === 0) {
10261
- console.log(chalk8.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10608
+ console.log(chalk9.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10262
10609
  if (state.status === "no_open") {
10263
10610
  console.log(
10264
- chalk8.gray(
10611
+ chalk9.gray(
10265
10612
  ` $ npx lee-spec-kit context --done # ${tr(lang, "cli", "context.tipShowDone")}`
10266
10613
  )
10267
10614
  );
10268
10615
  console.log(
10269
- chalk8.gray(
10616
+ chalk9.gray(
10270
10617
  ` $ npx lee-spec-kit context --all # ${tr(lang, "cli", "context.tipShowAll")}`
10271
10618
  )
10272
10619
  );
@@ -10279,7 +10626,7 @@ async function runContext(featureName, options) {
10279
10626
  if (state.targetFeatures.length > 1) {
10280
10627
  if (state.selectionMode === "open") {
10281
10628
  console.log(
10282
- chalk8.gray(
10629
+ chalk9.gray(
10283
10630
  ` ${tr(lang, "cli", "context.openFallbackSummary", {
10284
10631
  inProgress: state.inProgressFeatures.length,
10285
10632
  readyToClose: state.readyToCloseFeatures.length,
@@ -10291,7 +10638,7 @@ async function runContext(featureName, options) {
10291
10638
  }
10292
10639
  if (state.selectionMode === "open") {
10293
10640
  console.log(
10294
- chalk8.blue(
10641
+ chalk9.blue(
10295
10642
  `\u{1F539} ${tr(lang, "cli", "context.sectionInProgress")} (${state.inProgressFeatures.length})`
10296
10643
  )
10297
10644
  );
@@ -10303,14 +10650,14 @@ async function runContext(featureName, options) {
10303
10650
  workflowPolicy,
10304
10651
  prePrReviewPolicy
10305
10652
  );
10306
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10653
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10307
10654
  console.log(
10308
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10655
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10309
10656
  );
10310
10657
  });
10311
10658
  console.log();
10312
10659
  console.log(
10313
- chalk8.blue(
10660
+ chalk9.blue(
10314
10661
  `\u{1F538} ${tr(lang, "cli", "context.sectionReadyToClose")} (${state.readyToCloseFeatures.length})`
10315
10662
  )
10316
10663
  );
@@ -10322,14 +10669,14 @@ async function runContext(featureName, options) {
10322
10669
  workflowPolicy,
10323
10670
  prePrReviewPolicy
10324
10671
  );
10325
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10672
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10326
10673
  console.log(
10327
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10674
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10328
10675
  );
10329
10676
  });
10330
10677
  } else {
10331
10678
  const title = state.selectionMode === "all" ? `\u{1F539} ${state.targetFeatures.length} Features:` : state.selectionMode === "done" ? `\u{1F539} ${state.targetFeatures.length} Done Features:` : `\u{1F539} ${state.targetFeatures.length} Features Detected:`;
10332
- console.log(chalk8.blue(title));
10679
+ console.log(chalk9.blue(title));
10333
10680
  console.log();
10334
10681
  state.targetFeatures.forEach((f2) => {
10335
10682
  const stepName2 = getListLabel(
@@ -10339,24 +10686,24 @@ async function runContext(featureName, options) {
10339
10686
  workflowPolicy,
10340
10687
  prePrReviewPolicy
10341
10688
  );
10342
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10689
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10343
10690
  console.log(
10344
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10691
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10345
10692
  );
10346
10693
  });
10347
10694
  }
10348
10695
  console.log();
10349
- console.log(chalk8.gray(tr(lang, "cli", "context.tipDetails")));
10696
+ console.log(chalk9.gray(tr(lang, "cli", "context.tipDetails")));
10350
10697
  const selectorTip = config.projectType === "multi" ? selectedComponent ? ` $ npx lee-spec-kit context <slug|F001|F001-slug> --component ${selectedComponent}` : " $ npx lee-spec-kit context <slug|F001|F001-slug> [--component <component>]" : " $ npx lee-spec-kit context <slug|F001|F001-slug>";
10351
- console.log(chalk8.gray(selectorTip));
10698
+ console.log(chalk9.gray(selectorTip));
10352
10699
  if (state.selectionMode === "open") {
10353
10700
  console.log(
10354
- chalk8.gray(
10701
+ chalk9.gray(
10355
10702
  ` $ npx lee-spec-kit context --all # ${tr(lang, "cli", "context.tipShowAll")}`
10356
10703
  )
10357
10704
  );
10358
10705
  console.log(
10359
- chalk8.gray(
10706
+ chalk9.gray(
10360
10707
  ` $ npx lee-spec-kit context --done # ${tr(lang, "cli", "context.tipShowDone")}`
10361
10708
  )
10362
10709
  );
@@ -10368,47 +10715,47 @@ async function runContext(featureName, options) {
10368
10715
  }
10369
10716
  const f = state.targetFeatures[0];
10370
10717
  const stepName = stepsMap[f.currentStep] || "Unknown";
10371
- const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk8.yellow(tr(lang, "cli", "context.checkRequired")) : "";
10718
+ const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk9.yellow(tr(lang, "cli", "context.checkRequired")) : "";
10372
10719
  const hasCheckAction = approvalRequired;
10373
10720
  console.log(
10374
- `\u{1F539} Feature: ${chalk8.bold(f.folderName)} ${config.projectType === "multi" ? chalk8.cyan(`(${f.type})`) : ""}`
10721
+ `\u{1F539} Feature: ${chalk9.bold(f.folderName)} ${config.projectType === "multi" ? chalk9.cyan(`(${f.type})`) : ""}`
10375
10722
  );
10376
10723
  console.log(
10377
- ` \u2022 Completion: ${f.completion.implementationDone ? chalk8.green("Implementation \u2705") : chalk8.gray("Implementation \u25EF")} / ${f.completion.workflowDone ? chalk8.green("Workflow \u2705") : chalk8.yellow("Workflow \u25EF")}`
10724
+ ` \u2022 Completion: ${f.completion.implementationDone ? chalk9.green("Implementation \u2705") : chalk9.gray("Implementation \u25EF")} / ${f.completion.workflowDone ? chalk9.green("Workflow \u2705") : chalk9.yellow("Workflow \u25EF")}`
10378
10725
  );
10379
10726
  if (f.issueNumber) {
10380
10727
  console.log(` \u2022 Issue: #${f.issueNumber}`);
10381
10728
  }
10382
- console.log(` \u2022 Path: ${path12.relative(cwd, f.path)}`);
10729
+ console.log(` \u2022 Path: ${path14.relative(cwd, f.path)}`);
10383
10730
  if (f.git.projectBranch) {
10384
10731
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
10385
10732
  }
10386
10733
  console.log();
10387
10734
  console.log(
10388
- `\u{1F539} Progress: ${chalk8.yellow(`Step ${f.currentStep}. ${stepName}`)}`
10735
+ `\u{1F539} Progress: ${chalk9.yellow(`Step ${f.currentStep}. ${stepName}`)}`
10389
10736
  );
10390
10737
  if (f.activeTask) {
10391
10738
  console.log(
10392
- ` \u2022 Active Task: ${chalk8.yellow(`[${f.activeTask.status}]`)} ${f.activeTask.title}`
10739
+ ` \u2022 Active Task: ${chalk9.yellow(`[${f.activeTask.status}]`)} ${f.activeTask.title}`
10393
10740
  );
10394
10741
  } else if (f.nextTodoTask && f.currentStep === 10) {
10395
10742
  console.log(
10396
- ` \u2022 Next TODO: ${chalk8.gray(`[${f.nextTodoTask.status}]`)} ${f.nextTodoTask.title}`
10743
+ ` \u2022 Next TODO: ${chalk9.gray(`[${f.nextTodoTask.status}]`)} ${f.nextTodoTask.title}`
10397
10744
  );
10398
10745
  }
10399
10746
  printChecklist(f, stepDefinitions);
10400
10747
  if (f.warnings.length > 0) {
10401
10748
  console.log();
10402
- console.log(chalk8.yellow("\u26A0\uFE0F Feature Warnings:"));
10403
- f.warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
10749
+ console.log(chalk9.yellow("\u26A0\uFE0F Feature Warnings:"));
10750
+ f.warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
10404
10751
  }
10405
10752
  console.log();
10406
- console.log(chalk8.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
10753
+ console.log(chalk9.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
10407
10754
  if (hasCheckAction) {
10408
- console.log(chalk8.gray(tr(lang, "cli", "context.checkPolicyHint")));
10755
+ console.log(chalk9.gray(tr(lang, "cli", "context.checkPolicyHint")));
10409
10756
  }
10410
10757
  if (!f.actions || f.actions.length === 0) {
10411
- console.log(`\u{1F449} Next Action: ${chalk8.green(chalk8.bold(f.nextAction))}`);
10758
+ console.log(`\u{1F449} Next Action: ${chalk9.green(chalk9.bold(f.nextAction))}`);
10412
10759
  console.log();
10413
10760
  return;
10414
10761
  }
@@ -10421,7 +10768,7 @@ async function runContext(featureName, options) {
10421
10768
  f.currentSubstateOwner
10422
10769
  );
10423
10770
  const showOptionLabels = hasCheckAction;
10424
- console.log(chalk8.green(chalk8.bold("\u{1F449} Next Options (Atomic):")));
10771
+ console.log(chalk9.green(chalk9.bold("\u{1F449} Next Options (Atomic):")));
10425
10772
  let hasDocsCommand = false;
10426
10773
  actionOptions.forEach((option) => {
10427
10774
  const requiresCheck = option.action.requiresUserCheck;
@@ -10434,26 +10781,26 @@ async function runContext(featureName, options) {
10434
10781
  });
10435
10782
  if (hasDocsCommand) {
10436
10783
  console.log(
10437
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`)
10784
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`)
10438
10785
  );
10439
10786
  }
10440
10787
  if (hasCommandOption && longRunningDelegation.shouldDelegate) {
10441
10788
  console.log(
10442
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`)
10789
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`)
10443
10790
  );
10444
10791
  }
10445
10792
  if (hasCheckAction) {
10446
10793
  console.log(
10447
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`)
10794
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`)
10448
10795
  );
10449
10796
  console.log(
10450
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`)
10797
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`)
10451
10798
  );
10452
10799
  }
10453
10800
  if (requiredDocs.length > 0) {
10454
10801
  for (const requiredDoc of requiredDocs) {
10455
10802
  console.log(
10456
- chalk8.gray(
10803
+ chalk9.gray(
10457
10804
  ` \u21B3 ${tr(lang, "cli", "context.readBuiltinDocFirst", {
10458
10805
  command: requiredDoc.command
10459
10806
  })}`
@@ -10462,9 +10809,9 @@ async function runContext(featureName, options) {
10462
10809
  }
10463
10810
  }
10464
10811
  if (autoRunPlan.available) {
10465
- console.log(chalk8.gray(` \u21B3 ${autoRunPlan.summary}`));
10812
+ console.log(chalk9.gray(` \u21B3 ${autoRunPlan.summary}`));
10466
10813
  console.log(
10467
- chalk8.gray(
10814
+ chalk9.gray(
10468
10815
  ` \u21B3 ${tr(lang, "cli", "context.autoRunCommandHint", {
10469
10816
  command: autoRunPlan.command
10470
10817
  })}`
@@ -10484,16 +10831,16 @@ async function runContext(featureName, options) {
10484
10831
  selectedComponent,
10485
10832
  true
10486
10833
  );
10487
- console.log(chalk8.cyan(` \u21B3 ${finalApprovalPrompt}`));
10834
+ console.log(chalk9.cyan(` \u21B3 ${finalApprovalPrompt}`));
10488
10835
  console.log(
10489
- chalk8.gray(
10836
+ chalk9.gray(
10490
10837
  ` \u21B3 ${tr(lang, "cli", "context.finalLabelCommandHint", {
10491
10838
  command: approveCommand
10492
10839
  })}`
10493
10840
  )
10494
10841
  );
10495
10842
  console.log(
10496
- chalk8.gray(
10843
+ chalk9.gray(
10497
10844
  ` \u21B3 ${tr(lang, "cli", "context.finalTicketCommandHint", {
10498
10845
  command: executeCommand
10499
10846
  })}`
@@ -10507,8 +10854,8 @@ function printChecklist(f, stepDefinitions) {
10507
10854
  checklistSteps.forEach((definition) => {
10508
10855
  const done = definition.checklist.done(f);
10509
10856
  const detail = definition.checklist.detail?.(f) ?? "";
10510
- const mark = done ? chalk8.green("\u2705") : chalk8.gray("\u25EF");
10511
- const label = definition.step === f.currentStep ? chalk8.bold(definition.name) : definition.name;
10857
+ const mark = done ? chalk9.green("\u2705") : chalk9.gray("\u25EF");
10858
+ const label = definition.step === f.currentStep ? chalk9.bold(definition.name) : definition.name;
10512
10859
  console.log(` ${mark} ${definition.step}. ${label} ${detail}`);
10513
10860
  });
10514
10861
  }
@@ -10531,7 +10878,7 @@ function extractTitleAfterId(line, id) {
10531
10878
  return cleaned ? cleaned : void 0;
10532
10879
  }
10533
10880
  async function scanPrdRequirements(fsAdapter, docsDir) {
10534
- const prdDir = path12.join(docsDir, "prd");
10881
+ const prdDir = path14.join(docsDir, "prd");
10535
10882
  const files = await walkFiles(fsAdapter, prdDir, {
10536
10883
  extensions: [".md"],
10537
10884
  ignoreDirs: [".git", "node_modules", "dist", "tmp"]
@@ -10539,7 +10886,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10539
10886
  const definitions = /* @__PURE__ */ new Map();
10540
10887
  const duplicates = [];
10541
10888
  for (const filePath of files) {
10542
- if (path12.basename(filePath).toLowerCase() === "readme.md") {
10889
+ if (path14.basename(filePath).toLowerCase() === "readme.md") {
10543
10890
  continue;
10544
10891
  }
10545
10892
  let content = "";
@@ -10548,7 +10895,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10548
10895
  } catch {
10549
10896
  continue;
10550
10897
  }
10551
- const relFile = normalizeRelPath2(path12.relative(docsDir, filePath));
10898
+ const relFile = normalizeRelPath2(path14.relative(docsDir, filePath));
10552
10899
  const lines = content.split(/\r?\n/);
10553
10900
  let inCodeBlock = false;
10554
10901
  for (let i = 0; i < lines.length; i += 1) {
@@ -10623,7 +10970,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
10623
10970
  ]);
10624
10971
  function formatPath(cwd, p) {
10625
10972
  if (!p) return "";
10626
- return path12.isAbsolute(p) ? path12.relative(cwd, p) : p;
10973
+ return path14.isAbsolute(p) ? path14.relative(cwd, p) : p;
10627
10974
  }
10628
10975
  function detectPlaceholders(content) {
10629
10976
  const patterns = [
@@ -10782,7 +11129,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
10782
11129
  const placeholderContext = {
10783
11130
  projectName: config.projectName,
10784
11131
  featureName: f.slug,
10785
- featurePath: f.docs.featurePathFromDocs || path12.relative(config.docsDir, f.path),
11132
+ featurePath: f.docs.featurePathFromDocs || path14.relative(config.docsDir, f.path),
10786
11133
  repoType: f.type,
10787
11134
  featureNumber
10788
11135
  };
@@ -10792,7 +11139,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
10792
11139
  "tasks.md"
10793
11140
  ];
10794
11141
  for (const file of files) {
10795
- const fullPath = path12.join(f.path, file);
11142
+ const fullPath = path14.join(f.path, file);
10796
11143
  if (!await fs.pathExists(fullPath)) continue;
10797
11144
  const original = await fs.readFile(fullPath, "utf-8");
10798
11145
  let next = original;
@@ -10845,7 +11192,7 @@ async function checkDocsStructure(config, cwd) {
10845
11192
  const issues = [];
10846
11193
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
10847
11194
  for (const dir of requiredDirs) {
10848
- const p = path12.join(config.docsDir, dir);
11195
+ const p = path14.join(config.docsDir, dir);
10849
11196
  if (!await fs.pathExists(p)) {
10850
11197
  issues.push({
10851
11198
  level: "error",
@@ -10857,7 +11204,7 @@ async function checkDocsStructure(config, cwd) {
10857
11204
  });
10858
11205
  }
10859
11206
  }
10860
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
11207
+ const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
10861
11208
  if (!await fs.pathExists(configPath)) {
10862
11209
  issues.push({
10863
11210
  level: "warn",
@@ -10885,7 +11232,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10885
11232
  }
10886
11233
  const idMap = /* @__PURE__ */ new Map();
10887
11234
  for (const f of features) {
10888
- const rel = f.docs.featurePathFromDocs || path12.relative(config.docsDir, f.path);
11235
+ const rel = f.docs.featurePathFromDocs || path14.relative(config.docsDir, f.path);
10889
11236
  const id = f.id || "UNKNOWN";
10890
11237
  if (!idMap.has(id)) idMap.set(id, []);
10891
11238
  idMap.get(id).push(rel);
@@ -10893,7 +11240,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10893
11240
  if (!isInitialTemplateState) {
10894
11241
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
10895
11242
  for (const file of featureDocs) {
10896
- const p = path12.join(f.path, file);
11243
+ const p = path14.join(f.path, file);
10897
11244
  if (!await fs.pathExists(p)) continue;
10898
11245
  const content = await fs.readFile(p, "utf-8");
10899
11246
  const placeholders = detectPlaceholders(content);
@@ -10908,7 +11255,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10908
11255
  });
10909
11256
  }
10910
11257
  if (decisionsPlaceholderMode !== "off") {
10911
- const decisionsPath = path12.join(f.path, "decisions.md");
11258
+ const decisionsPath = path14.join(f.path, "decisions.md");
10912
11259
  if (await fs.pathExists(decisionsPath)) {
10913
11260
  const content = await fs.readFile(decisionsPath, "utf-8");
10914
11261
  const placeholders = detectPlaceholders(content);
@@ -10937,7 +11284,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10937
11284
  level: "warn",
10938
11285
  code: "spec_status_unset",
10939
11286
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
10940
- path: formatPath(cwd, path12.join(f.path, "spec.md"))
11287
+ path: formatPath(cwd, path14.join(f.path, "spec.md"))
10941
11288
  });
10942
11289
  }
10943
11290
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -10945,7 +11292,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10945
11292
  level: "warn",
10946
11293
  code: "plan_status_unset",
10947
11294
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
10948
- path: formatPath(cwd, path12.join(f.path, "plan.md"))
11295
+ path: formatPath(cwd, path14.join(f.path, "plan.md"))
10949
11296
  });
10950
11297
  }
10951
11298
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -10953,11 +11300,11 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10953
11300
  level: "warn",
10954
11301
  code: "tasks_empty",
10955
11302
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
10956
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11303
+ path: formatPath(cwd, path14.join(f.path, "tasks.md"))
10957
11304
  });
10958
11305
  }
10959
11306
  if (f.docs.tasksExists) {
10960
- const tasksPath = path12.join(f.path, "tasks.md");
11307
+ const tasksPath = path14.join(f.path, "tasks.md");
10961
11308
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
10962
11309
  const unknownPrdTags = [...new Set(
10963
11310
  parseTaskLines(tasksContent).flatMap((task) => task.tags).filter((tag) => isPrdRequirementId(tag)).map((tag) => tag.trim().toUpperCase()).filter((tag) => !prdDefinitions.has(tag))
@@ -10979,7 +11326,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10979
11326
  level: "warn",
10980
11327
  code: "tasks_doc_status_missing",
10981
11328
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
10982
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11329
+ path: formatPath(cwd, path14.join(f.path, "tasks.md"))
10983
11330
  });
10984
11331
  }
10985
11332
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -10987,7 +11334,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10987
11334
  level: "warn",
10988
11335
  code: "tasks_doc_status_unset",
10989
11336
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
10990
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11337
+ path: formatPath(cwd, path14.join(f.path, "tasks.md"))
10991
11338
  });
10992
11339
  }
10993
11340
  }
@@ -11011,7 +11358,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11011
11358
  level: "warn",
11012
11359
  code: "missing_feature_id",
11013
11360
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
11014
- path: formatPath(cwd, path12.join(config.docsDir, p))
11361
+ path: formatPath(cwd, path14.join(config.docsDir, p))
11015
11362
  });
11016
11363
  }
11017
11364
  return issues;
@@ -11141,25 +11488,25 @@ function doctorCommand(program2) {
11141
11488
  return;
11142
11489
  }
11143
11490
  console.log();
11144
- console.log(chalk8.bold(tr(lang, "cli", "doctor.title")));
11145
- console.log(chalk8.gray(`- Docs: ${path12.relative(cwd, docsDir)}`));
11146
- console.log(chalk8.gray(`- Type: ${projectType}`));
11147
- console.log(chalk8.gray(`- Lang: ${lang}`));
11491
+ console.log(chalk9.bold(tr(lang, "cli", "doctor.title")));
11492
+ console.log(chalk9.gray(`- Docs: ${path14.relative(cwd, docsDir)}`));
11493
+ console.log(chalk9.gray(`- Type: ${projectType}`));
11494
+ console.log(chalk9.gray(`- Lang: ${lang}`));
11148
11495
  console.log();
11149
11496
  if (warnings.length > 0) {
11150
- console.log(chalk8.yellow(tr(lang, "cli", "doctor.envWarnings")));
11151
- warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
11497
+ console.log(chalk9.yellow(tr(lang, "cli", "doctor.envWarnings")));
11498
+ warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
11152
11499
  console.log();
11153
11500
  }
11154
11501
  if (fixResult) {
11155
11502
  const label = fixResult.dryRun ? "\u{1F9EA} Doctor Fix (dry-run)" : "\u{1F6E0}\uFE0F Doctor Fix";
11156
11503
  console.log(
11157
- chalk8.blue(`${label}: ${fixResult.changedFiles} file(s)`)
11504
+ chalk9.blue(`${label}: ${fixResult.changedFiles} file(s)`)
11158
11505
  );
11159
11506
  if (fixResult.changedFiles > 0) {
11160
11507
  fixResult.entries.forEach(
11161
11508
  (entry) => console.log(
11162
- chalk8.gray(` - ${entry.path}: ${entry.changes.join(", ")}`)
11509
+ chalk9.gray(` - ${entry.path}: ${entry.changes.join(", ")}`)
11163
11510
  )
11164
11511
  );
11165
11512
  }
@@ -11169,51 +11516,51 @@ function doctorCommand(program2) {
11169
11516
  const warns = issues.filter((i) => i.level === "warn");
11170
11517
  const infos = issues.filter((i) => i.level === "info");
11171
11518
  if (!hasIssues && infos.length === 0) {
11172
- console.log(chalk8.green(tr(lang, "cli", "doctor.noIssues")));
11519
+ console.log(chalk9.green(tr(lang, "cli", "doctor.noIssues")));
11173
11520
  console.log();
11174
11521
  return;
11175
11522
  }
11176
11523
  if (!hasIssues && infos.length > 0) {
11177
- console.log(chalk8.green(tr(lang, "cli", "doctor.noIssues")));
11524
+ console.log(chalk9.green(tr(lang, "cli", "doctor.noIssues")));
11178
11525
  console.log();
11179
11526
  }
11180
11527
  if (errors.length > 0) {
11181
11528
  console.log(
11182
- chalk8.red(
11529
+ chalk9.red(
11183
11530
  `\u274C ${tr(lang, "cli", "doctor.errorsTitle")} (${errors.length})`
11184
11531
  )
11185
11532
  );
11186
11533
  errors.forEach(
11187
11534
  (i) => console.log(
11188
- chalk8.red(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11535
+ chalk9.red(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11189
11536
  )
11190
11537
  );
11191
11538
  console.log();
11192
11539
  }
11193
11540
  if (warns.length > 0) {
11194
11541
  console.log(
11195
- chalk8.yellow(
11542
+ chalk9.yellow(
11196
11543
  `\u26A0\uFE0F ${tr(lang, "cli", "doctor.warningsTitle")} (${warns.length})`
11197
11544
  )
11198
11545
  );
11199
11546
  warns.forEach(
11200
11547
  (i) => console.log(
11201
- chalk8.yellow(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11548
+ chalk9.yellow(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11202
11549
  )
11203
11550
  );
11204
11551
  console.log();
11205
11552
  }
11206
11553
  if (infos.length > 0) {
11207
- console.log(chalk8.blue(`\u2139\uFE0F Info (${infos.length})`));
11554
+ console.log(chalk9.blue(`\u2139\uFE0F Info (${infos.length})`));
11208
11555
  infos.forEach(
11209
11556
  (i) => console.log(
11210
- chalk8.blue(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11557
+ chalk9.blue(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11211
11558
  )
11212
11559
  );
11213
11560
  console.log();
11214
11561
  }
11215
11562
  console.log(
11216
- chalk8.gray(
11563
+ chalk9.gray(
11217
11564
  tr(lang, "cli", "doctor.tipJson", {
11218
11565
  strictFlag: options.strict ? " --strict" : ""
11219
11566
  })
@@ -11242,8 +11589,8 @@ function doctorCommand(program2) {
11242
11589
  );
11243
11590
  } else {
11244
11591
  console.error(
11245
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
11246
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
11592
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
11593
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
11247
11594
  );
11248
11595
  printCliErrorSuggestions(suggestions, lang);
11249
11596
  }
@@ -11272,8 +11619,8 @@ function viewCommand(program2) {
11272
11619
  );
11273
11620
  } else {
11274
11621
  console.error(
11275
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
11276
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
11622
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
11623
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
11277
11624
  );
11278
11625
  printCliErrorSuggestions(suggestions, lang);
11279
11626
  }
@@ -11322,49 +11669,49 @@ async function runView(featureName, options) {
11322
11669
  return;
11323
11670
  }
11324
11671
  console.log();
11325
- console.log(chalk8.bold("\u{1F4CA} Workflow View"));
11326
- console.log(chalk8.gray(`- Docs: ${path12.relative(cwd, config.docsDir)}`));
11672
+ console.log(chalk9.bold("\u{1F4CA} Workflow View"));
11673
+ console.log(chalk9.gray(`- Docs: ${path14.relative(cwd, config.docsDir)}`));
11327
11674
  console.log(
11328
- chalk8.gray(
11675
+ chalk9.gray(
11329
11676
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
11330
11677
  )
11331
11678
  );
11332
11679
  console.log(
11333
- chalk8.gray(
11680
+ chalk9.gray(
11334
11681
  `- In Progress: ${state.inProgressFeatures.length}, Ready To Close: ${state.readyToCloseFeatures.length}`
11335
11682
  )
11336
11683
  );
11337
11684
  if (state.warnings.length > 0) {
11338
11685
  console.log();
11339
- console.log(chalk8.yellow("\u26A0\uFE0F Environment warnings:"));
11686
+ console.log(chalk9.yellow("\u26A0\uFE0F Environment warnings:"));
11340
11687
  for (const warning of state.warnings) {
11341
- console.log(chalk8.yellow(` - ${warning}`));
11688
+ console.log(chalk9.yellow(` - ${warning}`));
11342
11689
  }
11343
11690
  }
11344
11691
  if (state.features.length === 0) {
11345
11692
  console.log();
11346
- console.log(chalk8.yellow("No features found."));
11347
- console.log(chalk8.gray("Try: npx lee-spec-kit feature <name>"));
11693
+ console.log(chalk9.yellow("No features found."));
11694
+ console.log(chalk9.gray("Try: npx lee-spec-kit feature <name>"));
11348
11695
  console.log();
11349
11696
  return;
11350
11697
  }
11351
11698
  if (!state.matchedFeature) {
11352
11699
  console.log();
11353
11700
  console.log(
11354
- chalk8.blue(`Selection: ${state.status} (${toReasonCode(state.status)})`)
11701
+ chalk9.blue(`Selection: ${state.status} (${toReasonCode(state.status)})`)
11355
11702
  );
11356
11703
  const rows = state.targetFeatures.length > 0 ? state.targetFeatures : state.features;
11357
11704
  for (const f2 of rows) {
11358
- const statusText = f2.completion.workflowDone ? chalk8.green("WORKFLOW_DONE") : f2.completion.implementationDone ? chalk8.cyan("DONE") : chalk8.yellow("IN_PROGRESS");
11705
+ const statusText = f2.completion.workflowDone ? chalk9.green("WORKFLOW_DONE") : f2.completion.implementationDone ? chalk9.cyan("DONE") : chalk9.yellow("IN_PROGRESS");
11359
11706
  const substateText = f2.currentSubstateId ? `${f2.currentSubstateId} (${f2.currentSubstateOwner ?? "-"} / ${f2.currentSubstatePhase ?? "-"})` : "-";
11360
11707
  console.log(
11361
11708
  `- ${f2.folderName} (${f2.tasks.done}/${f2.tasks.total}) ${statusText} step:${f2.currentStep} substate:${substateText}`
11362
11709
  );
11363
- console.log(chalk8.gray(` next: ${f2.nextAction}`));
11710
+ console.log(chalk9.gray(` next: ${f2.nextAction}`));
11364
11711
  }
11365
11712
  console.log();
11366
11713
  const selectorTip = config.projectType === "multi" ? selectedComponent ? `Tip: npx lee-spec-kit view <slug|F001|F001-slug> --component ${selectedComponent}` : "Tip: npx lee-spec-kit view <slug|F001|F001-slug> [--component <component>]" : "Tip: npx lee-spec-kit view <slug|F001|F001-slug>";
11367
- console.log(chalk8.gray(selectorTip));
11714
+ console.log(chalk9.gray(selectorTip));
11368
11715
  console.log();
11369
11716
  return;
11370
11717
  }
@@ -11372,7 +11719,7 @@ async function runView(featureName, options) {
11372
11719
  const completion = `${f.tasks.done}/${f.tasks.total}`;
11373
11720
  const stateLabel = f.completion.workflowDone ? "WORKFLOW_DONE" : f.completion.implementationDone ? "DONE" : "IN_PROGRESS";
11374
11721
  console.log();
11375
- console.log(chalk8.blue(`Feature: ${chalk8.bold(f.folderName)}`));
11722
+ console.log(chalk9.blue(`Feature: ${chalk9.bold(f.folderName)}`));
11376
11723
  console.log(`- State: ${stateLabel}`);
11377
11724
  console.log(`- Progress: ${completion}`);
11378
11725
  console.log(`- Step: ${f.currentStep}`);
@@ -11383,14 +11730,14 @@ async function runView(featureName, options) {
11383
11730
  console.log(`- Next: ${nextSummary}`);
11384
11731
  if (state.actionOptions.length > 0) {
11385
11732
  console.log();
11386
- console.log(chalk8.green("Atomic options:"));
11733
+ console.log(chalk9.green("Atomic options:"));
11387
11734
  for (const option of state.actionOptions) {
11388
11735
  const lang = config.lang ?? DEFAULT_LANG;
11389
11736
  const requiresCheck = option.action.requiresUserCheck ? tr(lang, "cli", "context.checkRequired") : "";
11390
11737
  console.log(` ${option.label}. ${requiresCheck}${option.detail}`);
11391
11738
  }
11392
11739
  console.log(
11393
- chalk8.gray(
11740
+ chalk9.gray(
11394
11741
  `Approve with: npx lee-spec-kit context ${f.folderName} --approve <LABEL>`
11395
11742
  )
11396
11743
  );
@@ -11408,13 +11755,13 @@ function normalizeRunId(raw) {
11408
11755
  return value;
11409
11756
  }
11410
11757
  function getFlowRunBaseDir(cwd) {
11411
- return path12.join(getRuntimeStateDir(cwd), "flow-runs");
11758
+ return path14.join(getRuntimeStateDir(cwd), "flow-runs");
11412
11759
  }
11413
11760
  function getFlowRunPath(cwd, runId) {
11414
- return path12.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11761
+ return path14.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11415
11762
  }
11416
11763
  function getFlowRunLockPath(cwd, runId) {
11417
- return path12.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11764
+ return path14.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11418
11765
  }
11419
11766
  async function readFlowRunRecordUnsafe(cwd, runId) {
11420
11767
  const normalized = normalizeRunId(runId);
@@ -11440,7 +11787,7 @@ async function readFlowRunRecordUnsafe(cwd, runId) {
11440
11787
  }
11441
11788
  async function writeFlowRunRecord(cwd, record) {
11442
11789
  const filePath = getFlowRunPath(cwd, record.runId);
11443
- await fs.ensureDir(path12.dirname(filePath));
11790
+ await fs.ensureDir(path14.dirname(filePath));
11444
11791
  await fs.writeJson(filePath, record, { spaces: 2 });
11445
11792
  }
11446
11793
  async function createFlowRunRecord(cwd, input) {
@@ -11590,6 +11937,7 @@ function toCompactAutoRun(autoRun) {
11590
11937
  iterations: autoRun.iterations,
11591
11938
  executionCount: autoRun.executions.length,
11592
11939
  lastExecution,
11940
+ delegated: autoRun.delegated ?? null,
11593
11941
  gate: autoRun.gate ?? null,
11594
11942
  manual: autoRun.manual ?? null,
11595
11943
  resume: autoRun.resume,
@@ -11634,6 +11982,7 @@ function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
11634
11982
  pauseAndReportWhen: [
11635
11983
  "approvalRequest.required=true",
11636
11984
  "AUTO_GATE_REACHED",
11985
+ "AUTO_DELEGATED_HANDOFF",
11637
11986
  "AUTO_MANUAL_REQUIRED",
11638
11987
  "command execution error"
11639
11988
  ],
@@ -11859,6 +12208,7 @@ function resolveAutoMode(config, options, requestText) {
11859
12208
  }
11860
12209
  function toAutoReasonCode(status) {
11861
12210
  const map = {
12211
+ delegated_handoff: "AUTO_DELEGATED_HANDOFF",
11862
12212
  gate_reached: "AUTO_GATE_REACHED",
11863
12213
  manual_required: "AUTO_MANUAL_REQUIRED",
11864
12214
  no_action_options: "AUTO_NO_ACTION_OPTIONS",
@@ -11882,6 +12232,7 @@ function isAutoRunFailureStatus(status) {
11882
12232
  }
11883
12233
  function toFlowRunStatus(status) {
11884
12234
  switch (status) {
12235
+ case "delegated_handoff":
11885
12236
  case "gate_reached":
11886
12237
  case "manual_required":
11887
12238
  return "paused";
@@ -11896,6 +12247,17 @@ function toFlowRunStatus(status) {
11896
12247
  return "failed";
11897
12248
  }
11898
12249
  }
12250
+ function isTaskCommitCheckpointOption(option, state) {
12251
+ if (!option || state.status !== "single_matched" || !state.matchedFeature) {
12252
+ return false;
12253
+ }
12254
+ if (state.matchedFeature.currentSubstateId !== "task_commit_pending") {
12255
+ return false;
12256
+ }
12257
+ if (option.action.type !== "command") return false;
12258
+ if (option.action.category === "docs_commit") return true;
12259
+ return option.action.category === "task_execute" && option.action.scope === "project" && /\bgit\s+commit\b/i.test(option.action.cmd);
12260
+ }
11899
12261
  async function runAutoUntilCategory(config, featureName, selectionOptions, untilCategories, requestText, metadata) {
11900
12262
  const contextArgs = [
11901
12263
  "context",
@@ -12073,6 +12435,29 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
12073
12435
  manual: null
12074
12436
  };
12075
12437
  }
12438
+ const taskCommitCheckpoint = actionOptions.find(
12439
+ (option) => isTaskCommitCheckpointOption(option, state)
12440
+ );
12441
+ if (taskCommitCheckpoint) {
12442
+ return {
12443
+ enabled: true,
12444
+ untilCategories,
12445
+ request: requestText,
12446
+ preset: metadata?.preset ?? null,
12447
+ source: metadata?.source ?? null,
12448
+ resume,
12449
+ status: "manual_required",
12450
+ reasonCode: toAutoReasonCode("manual_required"),
12451
+ iterations,
12452
+ executions,
12453
+ gate: null,
12454
+ manual: {
12455
+ label: taskCommitCheckpoint.label,
12456
+ category: taskCommitCheckpoint.action.category,
12457
+ detail: taskCommitCheckpoint.detail
12458
+ }
12459
+ };
12460
+ }
12076
12461
  const executable = actionOptions.find(
12077
12462
  (option) => option.action.type === "command"
12078
12463
  );
@@ -12158,16 +12543,18 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
12158
12543
  preset: metadata?.preset ?? null,
12159
12544
  source: metadata?.source ?? null,
12160
12545
  resume,
12161
- status: "manual_required",
12162
- reasonCode: toAutoReasonCode("manual_required"),
12546
+ status: "delegated_handoff",
12547
+ reasonCode: toAutoReasonCode("delegated_handoff"),
12163
12548
  iterations,
12164
12549
  executions,
12165
- gate: null,
12166
- manual: {
12550
+ delegated: {
12167
12551
  label: executable.label,
12168
12552
  category: executable.action.category,
12169
- detail: typeof executeResult?.nextMainState === "string" ? `Complete the delegated handoff work, then continue from ${executeResult.nextMainState}.` : executable.detail
12170
- }
12553
+ detail: typeof executeResult?.nextMainState === "string" ? `Complete the delegated handoff work, then continue from ${executeResult.nextMainState}.` : executable.detail,
12554
+ nextMainState: executeResult?.nextMainState
12555
+ },
12556
+ gate: null,
12557
+ manual: null
12171
12558
  };
12172
12559
  }
12173
12560
  if (executeResult?.status !== "approved_executed") {
@@ -12235,8 +12622,8 @@ function flowCommand(program2) {
12235
12622
  );
12236
12623
  } else {
12237
12624
  console.error(
12238
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
12239
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
12625
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
12626
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
12240
12627
  );
12241
12628
  printCliErrorSuggestions(suggestions, lang);
12242
12629
  }
@@ -12358,6 +12745,20 @@ async function runFlow(featureName, options) {
12358
12745
  "`--request` requires auto mode. Use `--auto-until-category`, `--auto-preset`, or configure `workflow.auto.defaultPreset`."
12359
12746
  );
12360
12747
  }
12748
+ if (autoMode && !resolvedFeatureName && requestText) {
12749
+ const bootstrapped = await bootstrapFeatureFromIdeaRequest(
12750
+ config,
12751
+ requestText,
12752
+ selectedComponent
12753
+ );
12754
+ if (bootstrapped) {
12755
+ resolvedFeatureName = bootstrapped.featureRef;
12756
+ if (!selectedComponent && bootstrapped.component) {
12757
+ selectedComponent = bootstrapped.component;
12758
+ selectionOptions.component = bootstrapped.component;
12759
+ }
12760
+ }
12761
+ }
12361
12762
  if (autoMode && !featureName) {
12362
12763
  if (!resolvedFeatureName) {
12363
12764
  throw createCliError(
@@ -12452,7 +12853,8 @@ async function runFlow(featureName, options) {
12452
12853
  },
12453
12854
  lastAutoStatus: autoRun.status,
12454
12855
  lastReasonCode: autoRun.reasonCode,
12455
- lastError: autoRun.error
12856
+ lastError: autoRun.error,
12857
+ lastDelegatedHandoff: autoRun.delegated ?? null
12456
12858
  })
12457
12859
  );
12458
12860
  autoRun.run = {
@@ -12555,16 +12957,16 @@ async function runFlow(featureName, options) {
12555
12957
  return;
12556
12958
  }
12557
12959
  console.log();
12558
- console.log(chalk8.bold("\u{1F501} Flow Summary"));
12960
+ console.log(chalk9.bold("\u{1F501} Flow Summary"));
12559
12961
  console.log(
12560
- chalk8.gray(
12962
+ chalk9.gray(
12561
12963
  `- Before: ${before.status} (${toReasonCode(before.status)}) / After: ${after.status} (${toReasonCode(after.status)})`
12562
12964
  )
12563
12965
  );
12564
12966
  if (approvalResult && typeof approvalResult === "object") {
12565
12967
  const result = approvalResult;
12566
12968
  console.log(
12567
- chalk8.gray(
12969
+ chalk9.gray(
12568
12970
  `- Approval: ${result.status ?? "unknown"} (${result.reasonCode ?? "-"})`
12569
12971
  )
12570
12972
  );
@@ -12572,18 +12974,18 @@ async function runFlow(featureName, options) {
12572
12974
  if (autoRun) {
12573
12975
  const presetSuffix = autoRun.preset ? `, preset ${autoRun.preset}` : "";
12574
12976
  console.log(
12575
- chalk8.gray(
12977
+ chalk9.gray(
12576
12978
  `- Auto: ${autoRun.status} (${autoRun.reasonCode}), iterations ${autoRun.iterations}, executions ${autoRun.executions.length}${presetSuffix}`
12577
12979
  )
12578
12980
  );
12579
- console.log(chalk8.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
12981
+ console.log(chalk9.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
12580
12982
  if (autoRun.run) {
12581
12983
  console.log(
12582
- chalk8.gray(
12984
+ chalk9.gray(
12583
12985
  `- Auto run: ${autoRun.run.mode} ${autoRun.run.runId} (${autoRun.run.status})`
12584
12986
  )
12585
12987
  );
12586
- console.log(chalk8.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
12988
+ console.log(chalk9.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
12587
12989
  }
12588
12990
  }
12589
12991
  const agentOrchestration = buildAgentOrchestrationPolicy2(
@@ -12591,21 +12993,21 @@ async function runFlow(featureName, options) {
12591
12993
  resolvedFeatureName || null
12592
12994
  );
12593
12995
  console.log(
12594
- chalk8.gray(
12996
+ chalk9.gray(
12595
12997
  `- Orchestration: ${agentOrchestration.mode}, prefer substate-owner delegation and fall back to legacy long-running categories`
12596
12998
  )
12597
12999
  );
12598
13000
  const statusCounts = statusReport.counts;
12599
13001
  const doctorCounts = doctorReport.counts;
12600
- console.log(chalk8.gray(`- Status features: ${statusCounts?.features ?? 0}`));
13002
+ console.log(chalk9.gray(`- Status features: ${statusCounts?.features ?? 0}`));
12601
13003
  console.log(
12602
- chalk8.gray(
13004
+ chalk9.gray(
12603
13005
  `- Doctor issues: ${doctorCounts?.issues ?? 0} (errors ${doctorCounts?.errors ?? 0}, warnings ${doctorCounts?.warnings ?? 0})`
12604
13006
  )
12605
13007
  );
12606
13008
  if (strictChecks) {
12607
- const strictLabel = strictChecks.statusStrictOk && strictChecks.doctorStrictOk ? chalk8.green("PASS") : chalk8.red("FAIL");
12608
- console.log(chalk8.gray(`- Strict checks: ${strictLabel}`));
13009
+ const strictLabel = strictChecks.statusStrictOk && strictChecks.doctorStrictOk ? chalk9.green("PASS") : chalk9.red("FAIL");
13010
+ console.log(chalk9.gray(`- Strict checks: ${strictLabel}`));
12609
13011
  }
12610
13012
  console.log();
12611
13013
  if (autoRun?.status === "gate_reached" && autoRun.gate?.userFacingLines?.length) {
@@ -12613,7 +13015,7 @@ async function runFlow(featureName, options) {
12613
13015
  console.log(line);
12614
13016
  }
12615
13017
  console.log(
12616
- chalk8.gray(
13018
+ chalk9.gray(
12617
13019
  "Auto gate reached. Reply with one of the labels shown above (example: A OK)."
12618
13020
  )
12619
13021
  );
@@ -12624,20 +13026,59 @@ async function runFlow(featureName, options) {
12624
13026
  }
12625
13027
  if (after.matchedFeature) {
12626
13028
  console.log(
12627
- chalk8.blue(
13029
+ chalk9.blue(
12628
13030
  `Next: npx lee-spec-kit context ${after.matchedFeature.folderName}${componentHint}`
12629
13031
  )
12630
13032
  );
12631
13033
  } else {
12632
- console.log(chalk8.blue(`Next: npx lee-spec-kit context${componentHint}`));
13034
+ console.log(chalk9.blue(`Next: npx lee-spec-kit context${componentHint}`));
12633
13035
  }
12634
13036
  console.log(
12635
- chalk8.gray(
13037
+ chalk9.gray(
12636
13038
  "Tip: add --approve <LABEL> [--execute] to run the selected atomic action."
12637
13039
  )
12638
13040
  );
12639
13041
  console.log();
12640
13042
  }
13043
+ async function bootstrapFeatureFromIdeaRequest(config, requestText, selectedComponent) {
13044
+ const ideaRef = extractExplicitIdeaRef(requestText);
13045
+ if (!ideaRef) return null;
13046
+ const resolvedIdea = await resolveIdeaReference(
13047
+ config.docsDir,
13048
+ ideaRef,
13049
+ config.lang
13050
+ );
13051
+ const existingFeatureRef = await readIdeaMetadataValue(
13052
+ resolvedIdea.path,
13053
+ "Feature"
13054
+ );
13055
+ if (existingFeatureRef && existingFeatureRef !== "-") {
13056
+ return { featureRef: existingFeatureRef, component: selectedComponent };
13057
+ }
13058
+ let component = selectedComponent;
13059
+ if (config.projectType === "multi" && !component) {
13060
+ const ideaComponent = await readIdeaMetadataValue(
13061
+ resolvedIdea.path,
13062
+ "Component"
13063
+ );
13064
+ if (ideaComponent && ideaComponent !== "-" && ideaComponent !== "all") {
13065
+ component = ideaComponent.toLowerCase();
13066
+ } else {
13067
+ return null;
13068
+ }
13069
+ }
13070
+ const featureName = await deriveFeatureNameFromIdea(resolvedIdea.path);
13071
+ const result = await runFeature(featureName, {
13072
+ component,
13073
+ idea: ideaRef,
13074
+ nonInteractive: true,
13075
+ json: true
13076
+ });
13077
+ return {
13078
+ featureRef: `${result.featureId}-${result.featureName}`,
13079
+ component
13080
+ };
13081
+ }
12641
13082
  function runProcess(bin, args, cwd) {
12642
13083
  const result = spawnSync(bin, args, {
12643
13084
  cwd,
@@ -12751,27 +13192,27 @@ function tg(lang, key, vars = {}) {
12751
13192
  function detectGithubCliLangSync(cwd) {
12752
13193
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
12753
13194
  const startDirs = [
12754
- explicitDocsDir ? path12.resolve(explicitDocsDir) : "",
12755
- path12.resolve(cwd)
13195
+ explicitDocsDir ? path14.resolve(explicitDocsDir) : "",
13196
+ path14.resolve(cwd)
12756
13197
  ].filter(Boolean);
12757
13198
  const scanOrder = [];
12758
13199
  const seen = /* @__PURE__ */ new Set();
12759
13200
  for (const start of startDirs) {
12760
13201
  let current = start;
12761
13202
  while (true) {
12762
- const abs = path12.resolve(current);
13203
+ const abs = path14.resolve(current);
12763
13204
  if (!seen.has(abs)) {
12764
13205
  scanOrder.push(abs);
12765
13206
  seen.add(abs);
12766
13207
  }
12767
- const parent = path12.dirname(abs);
13208
+ const parent = path14.dirname(abs);
12768
13209
  if (parent === abs) break;
12769
13210
  current = parent;
12770
13211
  }
12771
13212
  }
12772
13213
  for (const base of scanOrder) {
12773
- for (const docsDir of [path12.join(base, "docs"), base]) {
12774
- const configPath = path12.join(docsDir, ".lee-spec-kit.json");
13214
+ for (const docsDir of [path14.join(base, "docs"), base]) {
13215
+ const configPath = path14.join(docsDir, ".lee-spec-kit.json");
12775
13216
  if (fs.existsSync(configPath)) {
12776
13217
  try {
12777
13218
  const parsed = fs.readJsonSync(configPath);
@@ -12780,11 +13221,11 @@ function detectGithubCliLangSync(cwd) {
12780
13221
  } catch {
12781
13222
  }
12782
13223
  }
12783
- const agentsPath = path12.join(docsDir, "agents");
12784
- const featuresPath = path12.join(docsDir, "features");
13224
+ const agentsPath = path14.join(docsDir, "agents");
13225
+ const featuresPath = path14.join(docsDir, "features");
12785
13226
  if (!fs.existsSync(agentsPath) || !fs.existsSync(featuresPath)) continue;
12786
13227
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
12787
- const file = path12.join(agentsPath, probe);
13228
+ const file = path14.join(agentsPath, probe);
12788
13229
  if (!fs.existsSync(file)) continue;
12789
13230
  try {
12790
13231
  const content = fs.readFileSync(file, "utf-8");
@@ -12804,13 +13245,13 @@ function parseLabels(raw, lang) {
12804
13245
  }
12805
13246
  return [...new Set(labels)];
12806
13247
  }
12807
- function escapeRegExp3(value) {
13248
+ function escapeRegExp5(value) {
12808
13249
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12809
13250
  }
12810
13251
  function extractDraftMetadataValue(content, keys) {
12811
13252
  for (const key of keys) {
12812
13253
  const re = new RegExp(
12813
- `^\\s*-\\s*\\*\\*${escapeRegExp3(key)}\\*\\*\\s*:\\s*(.*?)\\s*$`,
13254
+ `^\\s*-\\s*\\*\\*${escapeRegExp5(key)}\\*\\*\\s*:\\s*(.*?)\\s*$`,
12814
13255
  "mi"
12815
13256
  );
12816
13257
  const match = content.match(re);
@@ -12889,7 +13330,7 @@ async function prepareGithubBody(params) {
12889
13330
  };
12890
13331
  }
12891
13332
  }
12892
- await fs.ensureDir(path12.dirname(defaultBodyFile));
13333
+ await fs.ensureDir(path14.dirname(defaultBodyFile));
12893
13334
  await fs.writeFile(defaultBodyFile, generatedBody, "utf-8");
12894
13335
  return {
12895
13336
  body: generatedBody,
@@ -12929,7 +13370,7 @@ function ensureSections(body, sections, kind, lang) {
12929
13370
  };
12930
13371
  const hasMetadataField = (field) => {
12931
13372
  const re = new RegExp(
12932
- `^\\s*-\\s*\\*\\*${escapeRegExp3(field)}\\*\\*\\s*:`,
13373
+ `^\\s*-\\s*\\*\\*${escapeRegExp5(field)}\\*\\*\\s*:`,
12933
13374
  "m"
12934
13375
  );
12935
13376
  return re.test(body);
@@ -12959,7 +13400,7 @@ function ensureSections(body, sections, kind, lang) {
12959
13400
  }
12960
13401
  function ensureDocsExist(docsDir, relativePaths, lang) {
12961
13402
  const missing = relativePaths.filter(
12962
- (relativePath) => !fs.existsSync(path12.join(docsDir, relativePath))
13403
+ (relativePath) => !fs.existsSync(path14.join(docsDir, relativePath))
12963
13404
  );
12964
13405
  if (missing.length > 0) {
12965
13406
  throw createCliError(
@@ -12969,18 +13410,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
12969
13410
  }
12970
13411
  }
12971
13412
  function buildDefaultBodyFileName(kind, docsDir, component) {
12972
- const key = `${path12.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13413
+ const key = `${path14.resolve(docsDir)}::${component.trim().toLowerCase()}`;
12973
13414
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
12974
13415
  return `lee-spec-kit.${digest}.${kind}.md`;
12975
13416
  }
12976
13417
  function toBodyFilePath(raw, kind, docsDir, component, lang) {
12977
- const selected = raw?.trim() || path12.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13418
+ const selected = raw?.trim() || path14.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
12978
13419
  assertValid(
12979
13420
  validatePathWithLang(selected, lang),
12980
13421
  `github.${kind}.bodyFile`,
12981
13422
  lang
12982
13423
  );
12983
- return path12.resolve(selected);
13424
+ return path14.resolve(selected);
12984
13425
  }
12985
13426
  function toProjectRootDocsPath(relativePathFromDocs) {
12986
13427
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -13817,7 +14258,7 @@ function stripMarkdownCodeContexts(body) {
13817
14258
  function hasIssueClosingKeyword(body, issueNumber) {
13818
14259
  if (!issueNumber) return false;
13819
14260
  const cleaned = stripMarkdownCodeContexts(body);
13820
- const issue = escapeRegExp3(issueNumber);
14261
+ const issue = escapeRegExp5(issueNumber);
13821
14262
  const closeKeywordRegex = new RegExp(
13822
14263
  `\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\b\\s*(?:[a-zA-Z0-9_.-]+\\/)?#\\s*${issue}\\b`,
13823
14264
  "i"
@@ -14035,7 +14476,7 @@ function ensureCleanWorktree(cwd, lang) {
14035
14476
  function commitAndPushPaths(cwd, absPaths, message, lang, options) {
14036
14477
  const uniqueRelativePaths = [
14037
14478
  ...new Set(
14038
- absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path12.relative(cwd, absPath) || absPath)
14479
+ absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path14.relative(cwd, absPath) || absPath)
14039
14480
  )
14040
14481
  ];
14041
14482
  if (uniqueRelativePaths.length === 0) return;
@@ -14231,15 +14672,15 @@ function githubCommand(program2) {
14231
14672
  config.lang
14232
14673
  );
14233
14674
  const specContent = await fs.readFile(
14234
- path12.join(config.docsDir, paths.specPath),
14675
+ path14.join(config.docsDir, paths.specPath),
14235
14676
  "utf-8"
14236
14677
  );
14237
14678
  const planContent = await fs.readFile(
14238
- path12.join(config.docsDir, paths.planPath),
14679
+ path14.join(config.docsDir, paths.planPath),
14239
14680
  "utf-8"
14240
14681
  );
14241
14682
  const tasksContent = await fs.readFile(
14242
- path12.join(config.docsDir, paths.tasksPath),
14683
+ path14.join(config.docsDir, paths.tasksPath),
14243
14684
  "utf-8"
14244
14685
  );
14245
14686
  const overview = resolveOverviewFromSpec(
@@ -14282,7 +14723,7 @@ function githubCommand(program2) {
14282
14723
  create: options.create,
14283
14724
  explicitBodyFile,
14284
14725
  defaultBodyFile,
14285
- workflowDraftPath: path12.join(config.docsDir, paths.issuePath),
14726
+ workflowDraftPath: path14.join(config.docsDir, paths.issuePath),
14286
14727
  generatedBody,
14287
14728
  requiredSections: getRequiredIssueSections(config.lang),
14288
14729
  kindLabel: tg(config.lang, "kindIssue"),
@@ -14298,7 +14739,7 @@ function githubCommand(program2) {
14298
14739
  `${feature.type}-issue-sanitized`,
14299
14740
  config.lang
14300
14741
  );
14301
- await fs.ensureDir(path12.dirname(sanitizedBodyFile));
14742
+ await fs.ensureDir(path14.dirname(sanitizedBodyFile));
14302
14743
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14303
14744
  bodyFile = sanitizedBodyFile;
14304
14745
  }
@@ -14347,12 +14788,12 @@ function githubCommand(program2) {
14347
14788
  const syncedIssueNumber = extractIssueNumberFromUrl(issueUrl);
14348
14789
  if (syncedIssueNumber) {
14349
14790
  const synced = syncTasksIssueMetadata(
14350
- path12.join(config.docsDir, paths.tasksPath),
14791
+ path14.join(config.docsDir, paths.tasksPath),
14351
14792
  syncedIssueNumber,
14352
14793
  config.lang
14353
14794
  );
14354
14795
  const draftSynced = syncIssueDraftMetadata(
14355
- path12.join(config.docsDir, paths.issuePath),
14796
+ path14.join(config.docsDir, paths.issuePath),
14356
14797
  syncedIssueNumber
14357
14798
  );
14358
14799
  syncChanged = synced.changed || draftSynced.changed;
@@ -14380,31 +14821,31 @@ function githubCommand(program2) {
14380
14821
  return;
14381
14822
  }
14382
14823
  console.log();
14383
- console.log(chalk8.bold(tg(config.lang, "issueHeader")));
14824
+ console.log(chalk9.bold(tg(config.lang, "issueHeader")));
14384
14825
  console.log(
14385
- chalk8.gray(
14826
+ chalk9.gray(
14386
14827
  `- ${tg(config.lang, "labelFeature")}: ${feature.folderName}`
14387
14828
  )
14388
14829
  );
14389
14830
  console.log(
14390
- chalk8.gray(
14831
+ chalk9.gray(
14391
14832
  `- ${tg(config.lang, "labelBodyFile")}: ${bodyFile}`
14392
14833
  )
14393
14834
  );
14394
14835
  console.log(
14395
- chalk8.gray(
14836
+ chalk9.gray(
14396
14837
  `- ${tg(config.lang, "labelLabels")}: ${labels.join(", ")}`
14397
14838
  )
14398
14839
  );
14399
14840
  if (issueUrl) {
14400
14841
  console.log(
14401
- chalk8.green(
14842
+ chalk9.green(
14402
14843
  tg(config.lang, "issueCreated", { url: issueUrl })
14403
14844
  )
14404
14845
  );
14405
14846
  } else {
14406
14847
  console.log(
14407
- chalk8.blue(tg(config.lang, "issueTemplateGenerated"))
14848
+ chalk9.blue(tg(config.lang, "issueTemplateGenerated"))
14408
14849
  );
14409
14850
  }
14410
14851
  console.log();
@@ -14423,8 +14864,8 @@ function githubCommand(program2) {
14423
14864
  );
14424
14865
  } else {
14425
14866
  console.error(
14426
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14427
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
14867
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
14868
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14428
14869
  );
14429
14870
  printCliErrorSuggestions(suggestions, lang);
14430
14871
  }
@@ -14463,13 +14904,13 @@ function githubCommand(program2) {
14463
14904
  config.lang
14464
14905
  );
14465
14906
  const specContent = await fs.readFile(
14466
- path12.join(config.docsDir, paths.specPath),
14907
+ path14.join(config.docsDir, paths.specPath),
14467
14908
  "utf-8"
14468
14909
  );
14469
- const planPath = path12.join(config.docsDir, paths.planPath);
14910
+ const planPath = path14.join(config.docsDir, paths.planPath);
14470
14911
  const planContent = await fs.pathExists(planPath) ? await fs.readFile(planPath, "utf-8") : "";
14471
14912
  const tasksContent = await fs.readFile(
14472
- path12.join(config.docsDir, paths.tasksPath),
14913
+ path14.join(config.docsDir, paths.tasksPath),
14473
14914
  "utf-8"
14474
14915
  );
14475
14916
  const overview = resolveOverviewFromSpec(
@@ -14517,7 +14958,7 @@ function githubCommand(program2) {
14517
14958
  create: options.create,
14518
14959
  explicitBodyFile,
14519
14960
  defaultBodyFile,
14520
- workflowDraftPath: path12.join(config.docsDir, paths.prPath),
14961
+ workflowDraftPath: path14.join(config.docsDir, paths.prPath),
14521
14962
  generatedBody,
14522
14963
  requiredSections: getRequiredPrSections(config.lang),
14523
14964
  kindLabel: tg(config.lang, "kindPr"),
@@ -14535,7 +14976,7 @@ function githubCommand(program2) {
14535
14976
  `${feature.type}-pr-sanitized`,
14536
14977
  config.lang
14537
14978
  );
14538
- await fs.ensureDir(path12.dirname(sanitizedBodyFile));
14979
+ await fs.ensureDir(path14.dirname(sanitizedBodyFile));
14539
14980
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14540
14981
  bodyFile = sanitizedBodyFile;
14541
14982
  }
@@ -14563,7 +15004,7 @@ function githubCommand(program2) {
14563
15004
  if (preparedBody.source === "generated") {
14564
15005
  await fs.writeFile(bodyFile, body, "utf-8");
14565
15006
  } else {
14566
- await fs.ensureDir(path12.dirname(fallbackBodyFile));
15007
+ await fs.ensureDir(path14.dirname(fallbackBodyFile));
14567
15008
  await fs.writeFile(fallbackBodyFile, body, "utf-8");
14568
15009
  bodyFile = fallbackBodyFile;
14569
15010
  }
@@ -14624,13 +15065,13 @@ function githubCommand(program2) {
14624
15065
  }
14625
15066
  if (prUrl && options.syncTasks !== false) {
14626
15067
  const syncedTasks = syncTasksPrMetadata(
14627
- path12.join(config.docsDir, paths.tasksPath),
15068
+ path14.join(config.docsDir, paths.tasksPath),
14628
15069
  prUrl,
14629
15070
  "Review",
14630
15071
  config.lang
14631
15072
  );
14632
15073
  const syncedDraft = syncPrDraftMetadata(
14633
- path12.join(config.docsDir, paths.prPath),
15074
+ path14.join(config.docsDir, paths.prPath),
14634
15075
  prUrl,
14635
15076
  "Review"
14636
15077
  );
@@ -14671,13 +15112,13 @@ function githubCommand(program2) {
14671
15112
  mergeAlreadyMerged = merged.alreadyMerged;
14672
15113
  if (prUrl && options.syncTasks !== false) {
14673
15114
  const mergedTasksSync = syncTasksPrMetadata(
14674
- path12.join(config.docsDir, paths.tasksPath),
15115
+ path14.join(config.docsDir, paths.tasksPath),
14675
15116
  prUrl,
14676
15117
  "Approved",
14677
15118
  config.lang
14678
15119
  );
14679
15120
  const mergedDraftSync = syncPrDraftMetadata(
14680
- path12.join(config.docsDir, paths.prPath),
15121
+ path14.join(config.docsDir, paths.prPath),
14681
15122
  prUrl,
14682
15123
  "Approved"
14683
15124
  );
@@ -14761,35 +15202,35 @@ function githubCommand(program2) {
14761
15202
  return;
14762
15203
  }
14763
15204
  console.log();
14764
- console.log(chalk8.bold(tg(config.lang, "prHeader")));
15205
+ console.log(chalk9.bold(tg(config.lang, "prHeader")));
14765
15206
  console.log(
14766
- chalk8.gray(
15207
+ chalk9.gray(
14767
15208
  `- ${tg(config.lang, "labelFeature")}: ${feature.folderName}`
14768
15209
  )
14769
15210
  );
14770
15211
  console.log(
14771
- chalk8.gray(
15212
+ chalk9.gray(
14772
15213
  `- ${tg(config.lang, "labelBodyFile")}: ${bodyFile}`
14773
15214
  )
14774
15215
  );
14775
15216
  console.log(
14776
- chalk8.gray(
15217
+ chalk9.gray(
14777
15218
  `- ${tg(config.lang, "labelLabels")}: ${labels.join(", ")}`
14778
15219
  )
14779
15220
  );
14780
15221
  if (prUrl) {
14781
15222
  console.log(
14782
- chalk8.gray(`- ${tg(config.lang, "labelPr")}: ${prUrl}`)
15223
+ chalk9.gray(`- ${tg(config.lang, "labelPr")}: ${prUrl}`)
14783
15224
  );
14784
15225
  }
14785
15226
  if (syncChanged) {
14786
15227
  console.log(
14787
- chalk8.green(tg(config.lang, "prTasksSynced"))
15228
+ chalk9.green(tg(config.lang, "prTasksSynced"))
14788
15229
  );
14789
15230
  }
14790
15231
  if (options.merge) {
14791
15232
  console.log(
14792
- chalk8.green(
15233
+ chalk9.green(
14793
15234
  tg(config.lang, "prMerged", {
14794
15235
  attempts: mergedAttempts ?? 1
14795
15236
  })
@@ -14797,15 +15238,15 @@ function githubCommand(program2) {
14797
15238
  );
14798
15239
  if (mergeAlreadyMerged) {
14799
15240
  console.log(
14800
- chalk8.yellow(tg(config.lang, "prAlreadyMergedNotice"))
15241
+ chalk9.yellow(tg(config.lang, "prAlreadyMergedNotice"))
14801
15242
  );
14802
15243
  }
14803
15244
  for (const warning of postMergeWarnings) {
14804
- console.log(chalk8.yellow(`\u26A0\uFE0F ${warning}`));
15245
+ console.log(chalk9.yellow(`\u26A0\uFE0F ${warning}`));
14805
15246
  }
14806
15247
  } else if (!options.create) {
14807
15248
  console.log(
14808
- chalk8.blue(tg(config.lang, "prTemplateGenerated"))
15249
+ chalk9.blue(tg(config.lang, "prTemplateGenerated"))
14809
15250
  );
14810
15251
  }
14811
15252
  console.log();
@@ -14824,8 +15265,8 @@ function githubCommand(program2) {
14824
15265
  );
14825
15266
  } else {
14826
15267
  console.error(
14827
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14828
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15268
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15269
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14829
15270
  );
14830
15271
  printCliErrorSuggestions(suggestions, lang);
14831
15272
  }
@@ -14883,10 +15324,10 @@ function docsCommand(program2) {
14883
15324
  return;
14884
15325
  }
14885
15326
  console.log();
14886
- console.log(chalk8.bold(tr(config.lang, "cli", "docs.listHeader")));
15327
+ console.log(chalk9.bold(tr(config.lang, "cli", "docs.listHeader")));
14887
15328
  for (const doc of docsList) {
14888
15329
  console.log(`- ${doc.id}: ${doc.title}`);
14889
- console.log(chalk8.gray(` ${doc.command}`));
15330
+ console.log(chalk9.gray(` ${doc.command}`));
14890
15331
  }
14891
15332
  console.log();
14892
15333
  } catch (error) {
@@ -14905,8 +15346,8 @@ function docsCommand(program2) {
14905
15346
  );
14906
15347
  } else {
14907
15348
  console.error(
14908
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14909
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15349
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15350
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14910
15351
  );
14911
15352
  printCliErrorSuggestions(suggestions, lang);
14912
15353
  }
@@ -14948,25 +15389,25 @@ function docsCommand(program2) {
14948
15389
  );
14949
15390
  return;
14950
15391
  }
14951
- const relativeFromCwd = path12.relative(process.cwd(), loaded.entry.absolutePath);
15392
+ const relativeFromCwd = path14.relative(process.cwd(), loaded.entry.absolutePath);
14952
15393
  console.log();
14953
- console.log(chalk8.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
15394
+ console.log(chalk9.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
14954
15395
  console.log(
14955
- chalk8.gray(
15396
+ chalk9.gray(
14956
15397
  `${tr(config.lang, "cli", "docs.sourceLabel")}: ${relativeFromCwd || loaded.entry.absolutePath}`
14957
15398
  )
14958
15399
  );
14959
15400
  console.log(
14960
- chalk8.gray(`${tr(config.lang, "cli", "docs.hashLabel")}: ${loaded.hash}`)
15401
+ chalk9.gray(`${tr(config.lang, "cli", "docs.hashLabel")}: ${loaded.hash}`)
14961
15402
  );
14962
15403
  console.log();
14963
15404
  process.stdout.write(loaded.content.endsWith("\n") ? loaded.content : `${loaded.content}
14964
15405
  `);
14965
15406
  if (followups.length > 0) {
14966
15407
  console.log();
14967
- console.log(chalk8.blue(`${tr(config.lang, "cli", "docs.nextDocs")}:`));
15408
+ console.log(chalk9.blue(`${tr(config.lang, "cli", "docs.nextDocs")}:`));
14968
15409
  for (const followup of followups) {
14969
- console.log(chalk8.gray(`- ${followup.command}`));
15410
+ console.log(chalk9.gray(`- ${followup.command}`));
14970
15411
  }
14971
15412
  console.log();
14972
15413
  }
@@ -14986,8 +15427,8 @@ function docsCommand(program2) {
14986
15427
  );
14987
15428
  } else {
14988
15429
  console.error(
14989
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14990
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15430
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15431
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14991
15432
  );
14992
15433
  printCliErrorSuggestions(suggestions, lang);
14993
15434
  }
@@ -15016,8 +15457,8 @@ function detectCommand(program2) {
15016
15457
  );
15017
15458
  } else {
15018
15459
  console.error(
15019
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
15020
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15460
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15461
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
15021
15462
  );
15022
15463
  printCliErrorSuggestions(suggestions, lang);
15023
15464
  }
@@ -15028,7 +15469,7 @@ function detectCommand(program2) {
15028
15469
  }
15029
15470
  async function runDetect(options) {
15030
15471
  const cwd = process.cwd();
15031
- const targetCwd = options.dir ? path12.resolve(cwd, options.dir) : cwd;
15472
+ const targetCwd = options.dir ? path14.resolve(cwd, options.dir) : cwd;
15032
15473
  const config = await getConfig(targetCwd);
15033
15474
  const detected = !!config;
15034
15475
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
@@ -15055,7 +15496,7 @@ async function runDetect(options) {
15055
15496
  );
15056
15497
  return;
15057
15498
  }
15058
- const configPath2 = path12.join(config.docsDir, ".lee-spec-kit.json");
15499
+ const configPath2 = path14.join(config.docsDir, ".lee-spec-kit.json");
15059
15500
  const configFilePresent2 = await fs.pathExists(configPath2);
15060
15501
  const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
15061
15502
  console.log(
@@ -15081,26 +15522,26 @@ async function runDetect(options) {
15081
15522
  }
15082
15523
  const lang = config?.lang ?? DEFAULT_LANG;
15083
15524
  console.log();
15084
- console.log(chalk8.blue(tr(lang, "cli", "detect.header")));
15085
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelTarget")}: ${targetCwd}`));
15525
+ console.log(chalk9.blue(tr(lang, "cli", "detect.header")));
15526
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelTarget")}: ${targetCwd}`));
15086
15527
  if (!config) {
15087
- console.log(chalk8.yellow(`- ${tr(lang, "cli", "detect.resultNotDetected")}`));
15088
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.notDetectedHint")}`));
15528
+ console.log(chalk9.yellow(`- ${tr(lang, "cli", "detect.resultNotDetected")}`));
15529
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.notDetectedHint")}`));
15089
15530
  console.log();
15090
15531
  return;
15091
15532
  }
15092
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
15533
+ const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
15093
15534
  const configFilePresent = await fs.pathExists(configPath);
15094
15535
  const detectionSource = configFilePresent ? "config" : "heuristic";
15095
- console.log(chalk8.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
15096
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
15536
+ console.log(chalk9.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
15537
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
15097
15538
  console.log(
15098
- chalk8.gray(
15539
+ chalk9.gray(
15099
15540
  `- ${tr(lang, "cli", "detect.labelConfigPath")}: ${configFilePresent ? configPath : "-"}`
15100
15541
  )
15101
15542
  );
15102
15543
  console.log(
15103
- chalk8.gray(
15544
+ chalk9.gray(
15104
15545
  `- ${tr(lang, "cli", "detect.labelSource")}: ${tr(
15105
15546
  lang,
15106
15547
  "cli",
@@ -15109,14 +15550,14 @@ async function runDetect(options) {
15109
15550
  )
15110
15551
  );
15111
15552
  console.log(
15112
- chalk8.gray(
15553
+ chalk9.gray(
15113
15554
  `- ${tr(lang, "cli", "detect.labelProjectType")}: ${config.projectType}`
15114
15555
  )
15115
15556
  );
15116
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelLang")}: ${config.lang}`));
15557
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelLang")}: ${config.lang}`));
15117
15558
  if (config.projectName) {
15118
15559
  console.log(
15119
- chalk8.gray(
15560
+ chalk9.gray(
15120
15561
  `- ${tr(lang, "cli", "detect.labelProjectName")}: ${config.projectName}`
15121
15562
  )
15122
15563
  );
@@ -15166,19 +15607,19 @@ function hasTemplateMarkers(content) {
15166
15607
  return patterns.some((pattern) => pattern.test(content));
15167
15608
  }
15168
15609
  async function countFeatureDirs(ctx, docsDir, projectType) {
15169
- const featuresRoot = path12.join(docsDir, "features");
15610
+ const featuresRoot = path14.join(docsDir, "features");
15170
15611
  if (projectType === "single") {
15171
15612
  const dirs = await listSubdirectories(ctx.fs, featuresRoot);
15172
- return dirs.filter((value) => path12.basename(value) !== "feature-base").length;
15613
+ return dirs.filter((value) => path14.basename(value) !== "feature-base").length;
15173
15614
  }
15174
15615
  const components = await listSubdirectories(ctx.fs, featuresRoot);
15175
15616
  let total = 0;
15176
15617
  for (const componentDir of components) {
15177
- const componentName = path12.basename(componentDir).trim().toLowerCase();
15618
+ const componentName = path14.basename(componentDir).trim().toLowerCase();
15178
15619
  if (!componentName || componentName === "feature-base") continue;
15179
15620
  const dirs = await listSubdirectories(ctx.fs, componentDir);
15180
15621
  total += dirs.filter(
15181
- (value) => path12.basename(value) !== "feature-base"
15622
+ (value) => path14.basename(value) !== "feature-base"
15182
15623
  ).length;
15183
15624
  }
15184
15625
  return total;
@@ -15190,7 +15631,7 @@ async function hasUserPrdFile(ctx, prdDir) {
15190
15631
  ignoreDirs: ["node_modules"]
15191
15632
  });
15192
15633
  return files.some(
15193
- (absolutePath) => path12.basename(absolutePath).toLowerCase() !== "readme.md"
15634
+ (absolutePath) => path14.basename(absolutePath).toLowerCase() !== "readme.md"
15194
15635
  );
15195
15636
  }
15196
15637
  function finalizeChecks(checks) {
@@ -15207,17 +15648,17 @@ function finalizeChecks(checks) {
15207
15648
  function printOnboardResult(lang, result) {
15208
15649
  console.log();
15209
15650
  console.log(
15210
- chalk8.bold(t(lang, "\u{1F9ED} Onboarding \uC810\uAC80", "\u{1F9ED} Onboarding Checks"))
15651
+ chalk9.bold(t(lang, "\u{1F9ED} Onboarding \uC810\uAC80", "\u{1F9ED} Onboarding Checks"))
15211
15652
  );
15212
15653
  for (const check of result.checks) {
15213
- const mark = check.status === "ok" ? chalk8.green("\u2705") : check.status === "warn" ? chalk8.yellow("\u26A0\uFE0F") : chalk8.red("\u274C");
15214
- const level = check.status === "ok" ? chalk8.green("OK") : check.status === "warn" ? chalk8.yellow("WARN") : chalk8.red("BLOCK");
15654
+ const mark = check.status === "ok" ? chalk9.green("\u2705") : check.status === "warn" ? chalk9.yellow("\u26A0\uFE0F") : chalk9.red("\u274C");
15655
+ const level = check.status === "ok" ? chalk9.green("OK") : check.status === "warn" ? chalk9.yellow("WARN") : chalk9.red("BLOCK");
15215
15656
  console.log(`${mark} [${level}] ${check.title}`);
15216
15657
  console.log(` ${check.message}`);
15217
- if (check.path) console.log(chalk8.gray(` path: ${check.path}`));
15658
+ if (check.path) console.log(chalk9.gray(` path: ${check.path}`));
15218
15659
  if (check.suggestedCommand) {
15219
15660
  console.log(
15220
- chalk8.gray(
15661
+ chalk9.gray(
15221
15662
  ` ${t(lang, "\uB2E4\uC74C \uBA85\uB839", "next")}: ${check.suggestedCommand}`
15222
15663
  )
15223
15664
  );
@@ -15225,7 +15666,7 @@ function printOnboardResult(lang, result) {
15225
15666
  }
15226
15667
  console.log();
15227
15668
  console.log(
15228
- chalk8.bold(
15669
+ chalk9.bold(
15229
15670
  t(
15230
15671
  lang,
15231
15672
  `\uC694\uC57D: OK ${result.summary.ok}, WARN ${result.summary.warn}, BLOCK ${result.summary.block}`,
@@ -15235,13 +15676,13 @@ function printOnboardResult(lang, result) {
15235
15676
  );
15236
15677
  if (result.status === "ready") {
15237
15678
  console.log(
15238
- chalk8.green(
15679
+ chalk9.green(
15239
15680
  t(lang, "\uC628\uBCF4\uB529 \uC900\uBE44\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Onboarding checks passed.")
15240
15681
  )
15241
15682
  );
15242
15683
  } else if (result.status === "needs_action") {
15243
15684
  console.log(
15244
- chalk8.yellow(
15685
+ chalk9.yellow(
15245
15686
  t(
15246
15687
  lang,
15247
15688
  "\uCD94\uAC00 \uC815\uB9AC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
@@ -15251,7 +15692,7 @@ function printOnboardResult(lang, result) {
15251
15692
  );
15252
15693
  } else {
15253
15694
  console.log(
15254
- chalk8.red(
15695
+ chalk9.red(
15255
15696
  t(
15256
15697
  lang,
15257
15698
  "\uC628\uBCF4\uB529 \uC120\uD589 \uC791\uC5C5\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
@@ -15359,7 +15800,7 @@ async function runOnboardChecks(ctx) {
15359
15800
  });
15360
15801
  }
15361
15802
  }
15362
- const constitutionPath = path12.join(docsDir, "agents", "constitution.md");
15803
+ const constitutionPath = path14.join(docsDir, "agents", "constitution.md");
15363
15804
  if (!await fs.pathExists(constitutionPath)) {
15364
15805
  checks.push({
15365
15806
  id: "constitution_exists",
@@ -15401,7 +15842,7 @@ async function runOnboardChecks(ctx) {
15401
15842
  });
15402
15843
  }
15403
15844
  }
15404
- const customPath = path12.join(docsDir, "agents", "custom.md");
15845
+ const customPath = path14.join(docsDir, "agents", "custom.md");
15405
15846
  if (await fs.pathExists(customPath)) {
15406
15847
  const content = await fs.readFile(customPath, "utf-8");
15407
15848
  if (hasTemplateMarkers(content)) {
@@ -15430,7 +15871,7 @@ async function runOnboardChecks(ctx) {
15430
15871
  });
15431
15872
  }
15432
15873
  }
15433
- const prdDir = path12.join(docsDir, "prd");
15874
+ const prdDir = path14.join(docsDir, "prd");
15434
15875
  const featureCount = await countFeatureDirs(ctx, docsDir, config.projectType);
15435
15876
  const prdReady = await hasUserPrdFile(ctx, prdDir);
15436
15877
  if (!prdReady) {
@@ -15448,7 +15889,7 @@ async function runOnboardChecks(ctx) {
15448
15889
  "PRD is empty. If features already exist, fill PRD as soon as possible."
15449
15890
  ),
15450
15891
  path: prdDir,
15451
- suggestedCommand: `touch ${quotePath(path12.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
15892
+ suggestedCommand: `touch ${quotePath(path14.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
15452
15893
  });
15453
15894
  } else {
15454
15895
  checks.push({
@@ -15589,8 +16030,8 @@ function onboardCommand(program2) {
15589
16030
  );
15590
16031
  } else {
15591
16032
  console.error(
15592
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
15593
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
16033
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
16034
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
15594
16035
  );
15595
16036
  printCliErrorSuggestions(suggestions, lang);
15596
16037
  }
@@ -15633,6 +16074,14 @@ function asNonEmptyString(value, fallback) {
15633
16074
  const trimmed = value.trim();
15634
16075
  return trimmed || fallback;
15635
16076
  }
16077
+ function asRequiredNonEmptyString(value, field) {
16078
+ const normalized = asNonEmptyString(value, "");
16079
+ if (normalized) return normalized;
16080
+ throw createCliError(
16081
+ "VALIDATION_FAILED",
16082
+ `Evidence JSON ${field} is required.`
16083
+ );
16084
+ }
15636
16085
  function asRequiredBoolean(value, field) {
15637
16086
  if (typeof value === "boolean") return value;
15638
16087
  throw createCliError(
@@ -15711,6 +16160,61 @@ function normalizeCommandsExecuted(value) {
15711
16160
  return entry.trim();
15712
16161
  });
15713
16162
  }
16163
+ function normalizeRequiredPathList(value, field) {
16164
+ if (!Array.isArray(value)) {
16165
+ throw createCliError(
16166
+ "VALIDATION_FAILED",
16167
+ `Evidence JSON ${field} must be a non-empty array of repo-relative paths.`
16168
+ );
16169
+ }
16170
+ const out = value.map((entry, index) => {
16171
+ if (typeof entry !== "string" || !entry.trim()) {
16172
+ throw createCliError(
16173
+ "VALIDATION_FAILED",
16174
+ `Evidence JSON ${field}[${index}] must be a non-empty repo-relative path string.`
16175
+ );
16176
+ }
16177
+ return normalizeGitPath3(entry);
16178
+ }).filter(Boolean);
16179
+ if (out.length === 0) {
16180
+ throw createCliError(
16181
+ "VALIDATION_FAILED",
16182
+ `Evidence JSON ${field} must include at least one repo-relative path.`
16183
+ );
16184
+ }
16185
+ return uniquePaths(out);
16186
+ }
16187
+ function normalizeRiskSummaries(value) {
16188
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16189
+ throw createCliError(
16190
+ "VALIDATION_FAILED",
16191
+ 'Evidence JSON "riskSummaries" is required and must include blocking, important, and minor fields.'
16192
+ );
16193
+ }
16194
+ const riskSummaries = value;
16195
+ return {
16196
+ blocking: asRequiredReviewField(
16197
+ riskSummaries.blocking,
16198
+ '"riskSummaries.blocking"',
16199
+ { allowExplicitNone: true }
16200
+ ),
16201
+ important: asRequiredReviewField(
16202
+ riskSummaries.important,
16203
+ '"riskSummaries.important"',
16204
+ { allowExplicitNone: true }
16205
+ ),
16206
+ minor: asRequiredReviewField(
16207
+ riskSummaries.minor,
16208
+ '"riskSummaries.minor"',
16209
+ { allowExplicitNone: true }
16210
+ )
16211
+ };
16212
+ }
16213
+ function formatMissingPathGuidance(field, missingFiles) {
16214
+ return `Evidence JSON ${field} is missing these repo-relative paths:
16215
+ ${missingFiles.map((file) => `- ${file}`).join("\n")}
16216
+ Use paths relative to the project git root, matching files.path entries.`;
16217
+ }
15714
16218
  function normalizeGitPath3(value) {
15715
16219
  return value.trim().replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
15716
16220
  }
@@ -15823,7 +16327,7 @@ var PrePrReviewValidator = class {
15823
16327
  return result.evidence;
15824
16328
  }
15825
16329
  async validateEvidenceWithScope(evidencePath, projectRoot) {
15826
- const fullPath = path12.resolve(evidencePath);
16330
+ const fullPath = path14.resolve(evidencePath);
15827
16331
  if (!await fs.pathExists(fullPath)) {
15828
16332
  throw createCliError(
15829
16333
  "INVALID_ARGUMENT",
@@ -15866,6 +16370,27 @@ var PrePrReviewValidator = class {
15866
16370
  evidence.blockingFindings,
15867
16371
  '"blockingFindings"'
15868
16372
  ),
16373
+ baseSha: asRequiredNonEmptyString(evidence.baseSha, '"baseSha"'),
16374
+ headSha: asRequiredNonEmptyString(evidence.headSha, '"headSha"'),
16375
+ changedFiles: normalizeRequiredPathList(
16376
+ evidence.changedFiles,
16377
+ '"changedFiles"'
16378
+ ),
16379
+ reviewedFiles: normalizeRequiredPathList(
16380
+ evidence.reviewedFiles,
16381
+ '"reviewedFiles"'
16382
+ ),
16383
+ riskSummaries: normalizeRiskSummaries(evidence.riskSummaries),
16384
+ approvalRationale: asRequiredReviewField(
16385
+ evidence.approvalRationale,
16386
+ '"approvalRationale"'
16387
+ ),
16388
+ coverage: {
16389
+ changedFileCount: 0,
16390
+ reviewedFileCount: 0,
16391
+ reviewCoverageRatio: 0,
16392
+ missingFiles: []
16393
+ },
15869
16394
  files: normalizeEvidenceFiles(evidence.files),
15870
16395
  residualRisks: normalizeResidualRisks(evidence.residualRisks),
15871
16396
  commandsExecuted: normalizeCommandsExecuted(evidence.commandsExecuted)
@@ -15900,9 +16425,9 @@ var PrePrReviewValidator = class {
15900
16425
  ]);
15901
16426
  const reviewedFiles = new Set(
15902
16427
  normalizedEvidence.files.map(
15903
- (f) => path12.relative(
16428
+ (f) => path14.relative(
15904
16429
  projectRoot,
15905
- path12.resolve(projectRoot, f.path)
16430
+ path14.resolve(projectRoot, f.path)
15906
16431
  )
15907
16432
  ).map((entry) => normalizeGitPath3(entry)).filter(Boolean)
15908
16433
  );
@@ -15914,6 +16439,36 @@ var PrePrReviewValidator = class {
15914
16439
  ${missingFiles.map((f) => `- ${f}`).join("\n")}`
15915
16440
  );
15916
16441
  }
16442
+ const changedFilesInEvidence = new Set(
16443
+ normalizedEvidence.changedFiles.map((entry) => normalizeGitPath3(entry))
16444
+ );
16445
+ const missingChangedFiles = changedFiles.filter(
16446
+ (file) => !changedFilesInEvidence.has(file)
16447
+ );
16448
+ if (missingChangedFiles.length > 0) {
16449
+ throw createCliError(
16450
+ "VALIDATION_FAILED",
16451
+ formatMissingPathGuidance('"changedFiles"', missingChangedFiles)
16452
+ );
16453
+ }
16454
+ const reviewedFilesInEvidence = new Set(
16455
+ normalizedEvidence.reviewedFiles.map((entry) => normalizeGitPath3(entry))
16456
+ );
16457
+ const missingReviewedFiles = changedFiles.filter(
16458
+ (file) => !reviewedFilesInEvidence.has(file)
16459
+ );
16460
+ if (missingReviewedFiles.length > 0) {
16461
+ throw createCliError(
16462
+ "VALIDATION_FAILED",
16463
+ formatMissingPathGuidance('"reviewedFiles"', missingReviewedFiles)
16464
+ );
16465
+ }
16466
+ normalizedEvidence.coverage = {
16467
+ changedFileCount: changedFiles.length,
16468
+ reviewedFileCount: normalizedEvidence.reviewedFiles.length,
16469
+ reviewCoverageRatio: changedFiles.length === 0 ? 1 : normalizedEvidence.reviewedFiles.length / changedFiles.length,
16470
+ missingFiles: missingReviewedFiles
16471
+ };
15917
16472
  return {
15918
16473
  evidence: normalizedEvidence,
15919
16474
  scope
@@ -16048,11 +16603,27 @@ var DEFAULT_EVIDENCE_FOR_ANY_MODE = {
16048
16603
  specAlignmentChecked: true,
16049
16604
  findingCount: 0,
16050
16605
  blockingFindings: 0,
16606
+ baseSha: "unrecorded",
16607
+ headSha: "unrecorded",
16608
+ changedFiles: [],
16609
+ reviewedFiles: [],
16610
+ riskSummaries: {
16611
+ blocking: "none",
16612
+ important: "manual direct record without structured evidence",
16613
+ minor: "manual direct record without structured evidence"
16614
+ },
16615
+ approvalRationale: "manual direct record without structured evidence; not valid for approve",
16616
+ coverage: {
16617
+ changedFileCount: 0,
16618
+ reviewedFileCount: 0,
16619
+ reviewCoverageRatio: 0,
16620
+ missingFiles: []
16621
+ },
16051
16622
  files: [],
16052
16623
  residualRisks: ["none"],
16053
16624
  commandsExecuted: []
16054
16625
  };
16055
- function escapeRegExp4(value) {
16626
+ function escapeRegExp6(value) {
16056
16627
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16057
16628
  }
16058
16629
  function normalizeDecision(raw) {
@@ -16066,14 +16637,14 @@ function normalizeDecision(raw) {
16066
16637
  return null;
16067
16638
  }
16068
16639
  function findSpecLineIndex(lines, keys) {
16069
- const escaped = keys.map((key) => escapeRegExp4(key));
16640
+ const escaped = keys.map((key) => escapeRegExp6(key));
16070
16641
  const re = new RegExp(
16071
16642
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
16072
16643
  );
16073
16644
  return lines.findIndex((line) => re.test(line));
16074
16645
  }
16075
16646
  function replaceSpecLine(line, keys, preferredKey, value) {
16076
- const escaped = keys.map((key) => escapeRegExp4(key));
16647
+ const escaped = keys.map((key) => escapeRegExp6(key));
16077
16648
  const re = new RegExp(
16078
16649
  `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
16079
16650
  );
@@ -16163,11 +16734,28 @@ ${normalizedCommands.map((c) => ` - \`${c}\``).join("\n")}
16163
16734
  - **Spec Alignment Checked**: ${input.evidence.specAlignmentChecked ? "yes" : "no"}
16164
16735
  - **Finding Count**: ${input.evidence.findingCount}
16165
16736
  - **Blocking Findings**: ${input.evidence.blockingFindings}
16737
+ - **Base SHA**: ${input.evidence.baseSha}
16738
+ - **Head SHA**: ${input.evidence.headSha}
16739
+ - **Approval Rationale**: ${input.evidence.approvalRationale}
16166
16740
  ${commandsRun}
16167
16741
 
16168
16742
  - **Residual Risks**:
16169
16743
  ${residualRisksSection}
16170
16744
 
16745
+ - **Risk Summaries**:
16746
+ - Blocking: ${input.evidence.riskSummaries.blocking}
16747
+ - Important: ${input.evidence.riskSummaries.important}
16748
+ - Minor: ${input.evidence.riskSummaries.minor}
16749
+
16750
+ - **Evidence Coverage**:
16751
+ - Changed File Count: ${input.evidence.coverage.changedFileCount}
16752
+ - Reviewed File Count: ${input.evidence.coverage.reviewedFileCount}
16753
+ - Review Coverage Ratio: ${input.evidence.coverage.reviewCoverageRatio.toFixed(2)}
16754
+ - Reviewed Files:
16755
+ ${input.evidence.reviewedFiles.length > 0 ? input.evidence.reviewedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)"}
16756
+ - Evidence Changed Files:
16757
+ ${input.evidence.changedFiles.length > 0 ? input.evidence.changedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)"}
16758
+
16171
16759
  - **Review Scope**:
16172
16760
  - **Main Base Ref**: ${input.scope.baseRef}
16173
16761
  - **Main Merge Base**: ${input.scope.mergeBase ?? "unresolved"}
@@ -16221,7 +16809,7 @@ function prePrReviewCommand(program2) {
16221
16809
  })
16222
16810
  );
16223
16811
  } else {
16224
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
16812
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16225
16813
  printCliErrorSuggestions(suggestions, lang);
16226
16814
  }
16227
16815
  process.exitCode = 1;
@@ -16233,7 +16821,7 @@ function prePrReviewCommand(program2) {
16233
16821
  "Decision outcome: approve | changes_requested | blocked"
16234
16822
  ).option("--note <text>", "Decision note text").option(
16235
16823
  "--evidence <path>",
16236
- "Optional review evidence path (for example review-trace.json); required only when policy demands it"
16824
+ "Structured review evidence path (for example review-trace.json); required for approve and whenever policy demands it"
16237
16825
  ).option("--json", "Output JSON").action(
16238
16826
  async (featureName, options) => {
16239
16827
  try {
@@ -16253,7 +16841,7 @@ function prePrReviewCommand(program2) {
16253
16841
  })
16254
16842
  );
16255
16843
  } else {
16256
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
16844
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16257
16845
  printCliErrorSuggestions(suggestions, lang);
16258
16846
  }
16259
16847
  process.exitCode = 1;
@@ -16300,7 +16888,7 @@ async function runPrePrReviewRun(featureName, options) {
16300
16888
  );
16301
16889
  const policy = resolvePrePrReviewPolicy(config.workflow);
16302
16890
  const preferred = getPreferredKeys(config.lang);
16303
- const tasksPath = path12.join(feature.path, "tasks.md");
16891
+ const tasksPath = path14.join(feature.path, "tasks.md");
16304
16892
  let tasksUpdated = false;
16305
16893
  if (await fs.pathExists(tasksPath)) {
16306
16894
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -16347,11 +16935,9 @@ async function runPrePrReviewRun(featureName, options) {
16347
16935
  handoffOnly: true,
16348
16936
  advancesWorkflow: false,
16349
16937
  reuseKey: `pre-pr:${featureRef}`,
16350
- suggestedParallelism: 1,
16351
- fallbackToMainAgentWhenQuotaExceeded: true,
16352
16938
  nextStepRequirement: "generate_review_trace_then_record",
16353
16939
  delegatedWorkRequired: true,
16354
- nextMainState: "pre_pr_review_running",
16940
+ nextMainState: "pre_pr_review_in_progress",
16355
16941
  evidenceFile: "review-trace.json",
16356
16942
  tasksUpdated,
16357
16943
  tasksPath,
@@ -16370,26 +16956,26 @@ async function runPrePrReviewRun(featureName, options) {
16370
16956
  console.log(prompt);
16371
16957
  console.log();
16372
16958
  console.log(
16373
- chalk8.yellow(
16374
- config.lang === "ko" ? "\uC774 \uBA85\uB839\uC740 \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. review-trace.json\uC744 \uC9C1\uC811 \uC0DD\uC131\uD558\uAC70\uB098 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC0C1\uD0DC\uB97C \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "This command only prepares the review handoff. It does not generate review-trace.json or advance workflow state by itself."
16959
+ chalk9.yellow(
16960
+ config.lang === "ko" ? "\uC774 \uBA85\uB839\uC740 \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. review-trace.json\uC744 \uC9C1\uC811 \uC0DD\uC131\uD558\uAC70\uB098 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC0C1\uD0DC\uB97C \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "This command only prepares the review handoff. It does not complete the review or advance workflow state by itself."
16375
16961
  )
16376
16962
  );
16377
16963
  console.log(
16378
- chalk8.gray(
16964
+ chalk9.gray(
16379
16965
  config.lang === "ko" ? "- \uAE30\uC874 pre-PR \uB9AC\uBDF0 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC788\uC73C\uBA74 \uC7AC\uC0AC\uC6A9\uD558\uACE0, \uAE30\uBCF8\uC740 1\uAC1C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694." : "- Reuse the existing pre-PR helper agent if one already exists; default to a single helper agent."
16380
16966
  )
16381
16967
  );
16382
16968
  console.log(
16383
- chalk8.gray(
16969
+ chalk9.gray(
16384
16970
  config.lang === "ko" ? "- \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uB9AC\uBDF0\uB97C \uC774\uC5B4\uAC00\uC138\uC694." : "- If helper-agent quota is exhausted, continue the review in the main agent."
16385
16971
  )
16386
16972
  );
16387
16973
  console.log(`Reuse key: pre-pr:${featureRef}`);
16388
16974
  console.log(`Suggested parallelism: 1`);
16389
- console.log(`Next main state: pre_pr_review_running`);
16975
+ console.log(`Next main state: pre_pr_review_in_progress`);
16390
16976
  console.log(`Evidence file: review-trace.json`);
16391
16977
  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"
16978
+ config.lang === "ko" ? "Next required: delegated review\uB97C \uC774\uC5B4\uC11C \uC218\uD589\uD558\uACE0 \uAD6C\uC870\uD654\uB41C review-trace.json\uC744 \uB9CC\uB4E0 \uB4A4 pre-pr-review\uB85C \uAE30\uB85D" : "Next required: continue the delegated review, generate structured review-trace.json, then record the result with pre-pr-review"
16393
16979
  );
16394
16980
  if (tasksUpdated) {
16395
16981
  console.log(`tasks.md updated: ${tasksPath}`);
@@ -16411,7 +16997,7 @@ async function runPrePrReview(featureName, options) {
16411
16997
  `tasks.md not found for feature: ${feature.folderName}`
16412
16998
  );
16413
16999
  }
16414
- const tasksPath = path12.join(feature.path, "tasks.md");
17000
+ const tasksPath = path14.join(feature.path, "tasks.md");
16415
17001
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
16416
17002
  const policy = resolvePrePrReviewPolicy(config.workflow);
16417
17003
  const preferred = getPreferredKeys(config.lang);
@@ -16444,6 +17030,12 @@ async function runPrePrReview(featureName, options) {
16444
17030
  "`--evidence <path>` is required when workflow.prePrReview.enforceExecutionEvidence=true."
16445
17031
  );
16446
17032
  }
17033
+ if (decision === "approve" && !options.evidence) {
17034
+ throw createCliError(
17035
+ "INVALID_ARGUMENT",
17036
+ "`--evidence <path>` is required for approve decisions. Generate structured review evidence first, for example `--evidence review-trace.json`."
17037
+ );
17038
+ }
16447
17039
  if (options.evidence) {
16448
17040
  const validator = new PrePrReviewValidator(ctx);
16449
17041
  const validationResult = await validator.validateEvidenceWithScope(
@@ -16503,7 +17095,7 @@ async function runPrePrReview(featureName, options) {
16503
17095
  }
16504
17096
  }
16505
17097
  }
16506
- const decisionsPath = path12.join(feature.path, "decisions.md");
17098
+ const decisionsPath = path14.join(feature.path, "decisions.md");
16507
17099
  const decisionLogEntry = buildReportContent({
16508
17100
  folderName: feature.folderName,
16509
17101
  date,
@@ -16519,9 +17111,9 @@ async function runPrePrReview(featureName, options) {
16519
17111
  await fs.writeFile(decisionsPath, nextDecisions, "utf-8");
16520
17112
  }
16521
17113
  const decisionsPathFromDocs = normalizePathForDoc(
16522
- path12.join(feature.docs.featurePathFromDocs, "decisions.md")
17114
+ path14.join(feature.docs.featurePathFromDocs, "decisions.md")
16523
17115
  );
16524
- const evidencePath = path12.basename(config.docsDir) === "docs" ? normalizePathForDoc(path12.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
17116
+ const evidencePath = path14.basename(config.docsDir) === "docs" ? normalizePathForDoc(path14.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
16525
17117
  let nextTasks = tasksContent;
16526
17118
  nextTasks = upsertSpecLine(
16527
17119
  nextTasks,
@@ -16568,26 +17160,26 @@ async function runPrePrReview(featureName, options) {
16568
17160
  return;
16569
17161
  }
16570
17162
  console.log();
16571
- console.log(chalk8.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
16572
- console.log(chalk8.gray(`- Decision: ${decision}`));
16573
- console.log(chalk8.gray(`- Decisions log: ${decisionsPath}`));
17163
+ console.log(chalk9.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
17164
+ console.log(chalk9.gray(`- Decision: ${decision}`));
17165
+ console.log(chalk9.gray(`- Decisions log: ${decisionsPath}`));
16574
17166
  if (nextTasks !== tasksContent) {
16575
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17167
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
16576
17168
  }
16577
17169
  console.log();
16578
17170
  }
16579
- function escapeRegExp5(value) {
17171
+ function escapeRegExp7(value) {
16580
17172
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16581
17173
  }
16582
17174
  function findSpecLineIndex2(lines, keys) {
16583
- const escaped = keys.map((key) => escapeRegExp5(key));
17175
+ const escaped = keys.map((key) => escapeRegExp7(key));
16584
17176
  const re = new RegExp(
16585
17177
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
16586
17178
  );
16587
17179
  return lines.findIndex((line) => re.test(line));
16588
17180
  }
16589
17181
  function replaceSpecLine2(line, keys, preferredKey, value) {
16590
- const escaped = keys.map((key) => escapeRegExp5(key));
17182
+ const escaped = keys.map((key) => escapeRegExp7(key));
16591
17183
  const re = new RegExp(
16592
17184
  `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
16593
17185
  );
@@ -16662,7 +17254,7 @@ async function runCodeReviewRun(featureName, options) {
16662
17254
  );
16663
17255
  }
16664
17256
  const feature = state.matchedFeature;
16665
- const tasksPath = path12.join(feature.path, "tasks.md");
17257
+ const tasksPath = path14.join(feature.path, "tasks.md");
16666
17258
  let tasksUpdated = false;
16667
17259
  if (await fs.pathExists(tasksPath)) {
16668
17260
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -16692,12 +17284,10 @@ async function runCodeReviewRun(featureName, options) {
16692
17284
  handoffOnly: true,
16693
17285
  advancesWorkflow: false,
16694
17286
  reuseKey: `code-review:${feature.folderName}`,
16695
- suggestedParallelism: 1,
16696
- fallbackToMainAgentWhenQuotaExceeded: true,
16697
17287
  nextMainState: "code_review_running",
16698
17288
  tasksUpdated,
16699
17289
  tasksPath,
16700
- decisionsPath: path12.join(feature.path, "decisions.md"),
17290
+ decisionsPath: path14.join(feature.path, "decisions.md"),
16701
17291
  prompt,
16702
17292
  recordedAt: getLocalDateString()
16703
17293
  };
@@ -16708,30 +17298,24 @@ async function runCodeReviewRun(featureName, options) {
16708
17298
  console.log(prompt);
16709
17299
  console.log();
16710
17300
  console.log(
16711
- chalk8.yellow(
17301
+ chalk9.yellow(
16712
17302
  config.lang === "ko" ? "\uC774 \uBA85\uB839\uC740 PR \uB9AC\uBDF0 \uCF54\uBA58\uD2B8 \uB300\uC751\uC6A9 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. \uCF54\uBA58\uD2B8\uB97C \uC9C1\uC811 \uC77D\uC5B4\uC624\uAC70\uB098 evidence/decision\uC744 \uC790\uB3D9 \uAE30\uB85D\uD558\uC9C0 \uC54A\uC73C\uBA70, \uC0C1\uD0DC\uB3C4 \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "This command only prepares a handoff for addressing PR review comments. It does not fetch comments automatically, record review evidence/decision, or advance workflow state by itself."
16713
17303
  )
16714
17304
  );
16715
- console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16716
- console.log(chalk8.gray(`- owner: ${payload.owner}`));
16717
- console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16718
- console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16719
- console.log(
16720
- chalk8.gray(
16721
- `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16722
- )
16723
- );
16724
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17305
+ console.log(chalk9.gray(`- substate: ${payload.substateId}`));
17306
+ console.log(chalk9.gray(`- owner: ${payload.owner}`));
17307
+ console.log(chalk9.gray(`- reuse key: ${payload.reuseKey}`));
17308
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
16725
17309
  if (tasksUpdated) {
16726
- console.log(chalk8.gray(`- tasks.md updated: ${payload.tasksPath}`));
17310
+ console.log(chalk9.gray(`- tasks.md updated: ${payload.tasksPath}`));
16727
17311
  console.log(
16728
- chalk8.gray(
17312
+ chalk9.gray(
16729
17313
  config.lang === "ko" ? "- PR \uB9AC\uBDF0 \uC0C1\uD0DC: Running" : "- PR Review status: Running"
16730
17314
  )
16731
17315
  );
16732
17316
  }
16733
- console.log(chalk8.gray(`- tasks.md: ${payload.tasksPath}`));
16734
- console.log(chalk8.gray(`- decisions.md: ${payload.decisionsPath}`));
17317
+ console.log(chalk9.gray(`- tasks.md: ${payload.tasksPath}`));
17318
+ console.log(chalk9.gray(`- decisions.md: ${payload.decisionsPath}`));
16735
17319
  }
16736
17320
  function codeReviewRunCommand(program2) {
16737
17321
  program2.command("code-review-run [feature-name]").description("Prepare a PR review execution handoff for sub-agent work").option("--component <component>", "Component name for multi projects").option("--json", "Output JSON").action(
@@ -16753,7 +17337,7 @@ function codeReviewRunCommand(program2) {
16753
17337
  })
16754
17338
  );
16755
17339
  } else {
16756
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17340
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16757
17341
  printCliErrorSuggestions(suggestions, lang);
16758
17342
  }
16759
17343
  process.exitCode = 1;
@@ -16788,8 +17372,8 @@ function requirementsCommand(program2) {
16788
17372
  const lang = ctx?.config?.lang ?? DEFAULT_LANG;
16789
17373
  const cliError = toCliError(error);
16790
17374
  console.error(
16791
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
16792
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
17375
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
17376
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
16793
17377
  );
16794
17378
  process.exitCode = 1;
16795
17379
  }
@@ -16822,7 +17406,7 @@ async function runRequirements(options) {
16822
17406
  }
16823
17407
  for (const feature of scan.features) {
16824
17408
  if (!feature.docs.tasksExists) continue;
16825
- const tasksPath = path12.join(feature.path, "tasks.md");
17409
+ const tasksPath = path14.join(feature.path, "tasks.md");
16826
17410
  let tasksContent = "";
16827
17411
  try {
16828
17412
  tasksContent = await ctx.fs.readFile(tasksPath, "utf-8");
@@ -16956,10 +17540,10 @@ async function runRequirements(options) {
16956
17540
  process.stdout.write(`${lines.join("\n")}
16957
17541
  `);
16958
17542
  if (options.write) {
16959
- const outputPath = path12.join(docsDir, "prd", "status.md");
17543
+ const outputPath = path14.join(docsDir, "prd", "status.md");
16960
17544
  await ctx.fs.writeFile(outputPath, `${lines.join("\n")}
16961
17545
  `, "utf-8");
16962
- console.log(chalk8.green(`\u2705 wrote: ${outputPath}`));
17546
+ console.log(chalk9.green(`\u2705 wrote: ${outputPath}`));
16963
17547
  }
16964
17548
  if (options.strict && issuesFound) process.exitCode = 1;
16965
17549
  }
@@ -17041,7 +17625,7 @@ async function resolveTaskRunContext(featureName, options) {
17041
17625
  }
17042
17626
  async function runTaskRun(featureName, options) {
17043
17627
  const { config, feature } = await resolveTaskRunContext(featureName, options);
17044
- const tasksPath = path12.join(feature.path, "tasks.md");
17628
+ const tasksPath = path14.join(feature.path, "tasks.md");
17045
17629
  if (!await fs.pathExists(tasksPath)) {
17046
17630
  throw createCliError(
17047
17631
  "PRECONDITION_FAILED",
@@ -17098,8 +17682,6 @@ async function runTaskRun(featureName, options) {
17098
17682
  owner: "subagent",
17099
17683
  handoffOnly: true,
17100
17684
  reuseKey: `task:${feature.folderName}:${resolvedTask.taskId}`,
17101
- suggestedParallelism: 1,
17102
- fallbackToMainAgentWhenQuotaExceeded: true,
17103
17685
  nextMainState: "task_complete",
17104
17686
  tasksUpdated,
17105
17687
  tasksPath,
@@ -17112,20 +17694,14 @@ async function runTaskRun(featureName, options) {
17112
17694
  }
17113
17695
  console.log(prompt);
17114
17696
  console.log();
17115
- console.log(chalk8.gray(`- substate: ${payload.substateId}`));
17116
- console.log(chalk8.gray(`- owner: ${payload.owner}`));
17117
- console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
17118
- console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
17119
- console.log(
17120
- chalk8.gray(
17121
- `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
17122
- )
17123
- );
17124
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17697
+ console.log(chalk9.gray(`- substate: ${payload.substateId}`));
17698
+ console.log(chalk9.gray(`- owner: ${payload.owner}`));
17699
+ console.log(chalk9.gray(`- reuse key: ${payload.reuseKey}`));
17700
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
17125
17701
  if (tasksUpdated) {
17126
17702
  console.log();
17127
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17128
- console.log(chalk8.gray(`- task status: TODO -> DOING`));
17703
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
17704
+ console.log(chalk9.gray(`- task status: TODO -> DOING`));
17129
17705
  }
17130
17706
  }
17131
17707
  function taskRunCommand(program2) {
@@ -17149,7 +17725,7 @@ function taskRunCommand(program2) {
17149
17725
  })
17150
17726
  );
17151
17727
  } else {
17152
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17728
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
17153
17729
  printCliErrorSuggestions(suggestions, lang);
17154
17730
  }
17155
17731
  process.exitCode = 1;
@@ -17200,7 +17776,7 @@ async function resolveTaskCompleteContext(featureName, options) {
17200
17776
  }
17201
17777
  async function runTaskComplete(featureName, options) {
17202
17778
  const { feature } = await resolveTaskCompleteContext(featureName, options);
17203
- const tasksPath = path12.join(feature.path, "tasks.md");
17779
+ const tasksPath = path14.join(feature.path, "tasks.md");
17204
17780
  if (!await fs.pathExists(tasksPath)) {
17205
17781
  throw createCliError(
17206
17782
  "PRECONDITION_FAILED",
@@ -17260,13 +17836,13 @@ async function runTaskComplete(featureName, options) {
17260
17836
  return;
17261
17837
  }
17262
17838
  console.log(
17263
- chalk8.green(
17839
+ chalk9.green(
17264
17840
  `Marked task ${resolvedTask.taskId} as DONE for ${feature.folderName}.`
17265
17841
  )
17266
17842
  );
17267
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17268
- console.log(chalk8.gray(`- status: ${resolvedTask.status} -> DONE`));
17269
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17843
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
17844
+ console.log(chalk9.gray(`- status: ${resolvedTask.status} -> DONE`));
17845
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
17270
17846
  }
17271
17847
  function taskCompleteCommand(program2) {
17272
17848
  program2.command("task-complete [feature-name]").description("Mark the active DOING/REVIEW task as DONE").option("--component <component>", "Component name for multi projects").option("--task <task-id>", "Explicit task id to mark DONE").option("--json", "Output JSON").action(
@@ -17288,7 +17864,7 @@ function taskCompleteCommand(program2) {
17288
17864
  })
17289
17865
  );
17290
17866
  } else {
17291
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17867
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
17292
17868
  printCliErrorSuggestions(suggestions, lang);
17293
17869
  }
17294
17870
  process.exitCode = 1;
@@ -17335,15 +17911,15 @@ function getBanner(opts) {
17335
17911
  ${version}
17336
17912
  ` : "\n";
17337
17913
  if (process.stdout.isTTY) {
17338
- return `${chalk8.cyan(ascii)}${chalk8.gray(footer)}`;
17914
+ return `${chalk9.cyan(ascii)}${chalk9.gray(footer)}`;
17339
17915
  }
17340
17916
  return `${ascii}${footer}`;
17341
17917
  }
17342
- var CACHE_FILE = path12.join(os.homedir(), ".lee-spec-kit-version-cache.json");
17918
+ var CACHE_FILE = path14.join(os.homedir(), ".lee-spec-kit-version-cache.json");
17343
17919
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
17344
17920
  function getCurrentVersion() {
17345
17921
  try {
17346
- const packageJsonPath = path12.join(__dirname$1, "..", "package.json");
17922
+ const packageJsonPath = path14.join(__dirname$1, "..", "package.json");
17347
17923
  if (fs.existsSync(packageJsonPath)) {
17348
17924
  const pkg = fs.readJsonSync(packageJsonPath);
17349
17925
  return pkg.version;
@@ -17377,8 +17953,8 @@ function resolveUpdateNoticeLang() {
17377
17953
  }
17378
17954
  function printUpdateNotice(current, latest, lang) {
17379
17955
  console.log();
17380
- console.log(chalk8.yellow(tr(lang, "cli", "versionCheck.noticeAvailable", { latest, current })));
17381
- console.log(chalk8.gray(tr(lang, "cli", "versionCheck.updateCommand")));
17956
+ console.log(chalk9.yellow(tr(lang, "cli", "versionCheck.noticeAvailable", { latest, current })));
17957
+ console.log(chalk9.gray(tr(lang, "cli", "versionCheck.updateCommand")));
17382
17958
  console.log();
17383
17959
  }
17384
17960
  function spawnBackgroundVersionCheck() {
@@ -17447,7 +18023,7 @@ function shouldCheckForUpdates() {
17447
18023
  if (shouldCheckForUpdates()) checkForUpdates();
17448
18024
  function getCliVersion() {
17449
18025
  try {
17450
- const packageJsonPath = path12.join(__dirname$1, "..", "package.json");
18026
+ const packageJsonPath = path14.join(__dirname$1, "..", "package.json");
17451
18027
  if (fs.existsSync(packageJsonPath)) {
17452
18028
  const pkg = fs.readJsonSync(packageJsonPath);
17453
18029
  if (pkg?.version) return String(pkg.version);
@@ -17465,6 +18041,7 @@ if (shouldShowBanner()) {
17465
18041
  }
17466
18042
  initCommand(program);
17467
18043
  featureCommand(program);
18044
+ ideaCommand(program);
17468
18045
  statusCommand(program);
17469
18046
  updateCommand(program);
17470
18047
  configCommand(program);