lee-spec-kit 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import path12 from 'path';
2
+ import path13 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 fs10 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 = () => path13.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 = path13.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 = path13.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 = path13.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 = path13.dirname(__filename2);
155
155
  function getTemplatesDir() {
156
- const rootDir = path12.resolve(__dirname2, "..");
157
- return path12.join(rootDir, "templates");
156
+ const rootDir = path13.resolve(__dirname2, "..");
157
+ return path13.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 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. delegated review\uB97C \uB9C8\uCE58\uACE0 `review-trace.json`\uC744 \uB9CC\uB4E0 \uB4A4 `pre-pr-review`\uB85C \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
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 in progress. Finish the delegated review, generate `review-trace.json`, then record the result with `pre-pr-review`. (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(path13.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 path13.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 path13.isAbsolute(out) ? out : path13.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 = path13.resolve(cwd);
1729
1768
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1730
1769
  }
1731
1770
  function getDocsLockPath(docsDir) {
1732
- return path12.join(
1771
+ return path13.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 path13.join(
1779
+ getRuntimeStateDir(path13.dirname(path13.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 path13.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 path13.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(path13.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 = path13.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 = path13.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(path13.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 = path13.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(path13.relative(docsDir, target));
1889
1928
  }
1890
1929
  }
1891
- const featureBasePath = path12.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1930
+ const featureBasePath = path13.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(path13.relative(docsDir, featureBasePath));
1895
1934
  }
1896
1935
  return removed;
1897
1936
  }
@@ -1990,6 +2029,7 @@ In approval-waiting state:
1990
2029
  3. Do not paraphrase or omit these lines.
1991
2030
  4. Prefer \`approvalRequest.userFacingLines\` as the source for user-facing approval text.
1992
2031
  5. Prefer \`matchedFeature.currentSubstateOwner\` plus \`agentOrchestration.subAgentHandoff\` as the delegation SSOT. Treat \`currentActionShouldDelegate\` as a compatibility mirror for older consumers.
2032
+ 6. When \`matchedFeature.currentSubstateOwner="subagent"\` and \`agentOrchestration.subAgentHandoff.required=true\` with \`mode="command"\`, call \`spawn_agent\` first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, continue the delegated work immediately and do not re-open the same approval label.
1993
2033
 
1994
2034
  In non-approval state (progress updates, analysis, tool execution logs, unrelated Q&A):
1995
2035
 
@@ -2004,23 +2044,35 @@ If approval is still pending after answering an unrelated question:
2004
2044
  - \`actionOptions[*].approvalPrompt\` (label meaning included), and
2005
2045
  - \`approvalRequest.finalPrompt\` (format line).
2006
2046
  - Never output \`finalPrompt\` alone without the matching \`A: ...\` prompt.`;
2007
- function renderManagedBlock(lang, docsRepo) {
2047
+ function renderManagedSegment(lang, docsRepo) {
2008
2048
  return `${LEE_SPEC_KIT_AGENTS_BEGIN}
2009
2049
  ${CANONICAL_LEE_SPEC_KIT_AGENTS_TEXT}
2010
- ${LEE_SPEC_KIT_AGENTS_END}
2050
+ ${LEE_SPEC_KIT_AGENTS_END}`;
2051
+ }
2052
+ function renderManagedBlock(lang, docsRepo) {
2053
+ return `${renderManagedSegment()}
2011
2054
 
2012
2055
  `;
2013
2056
  }
2014
2057
  async function upsertLeeSpecKitAgentsMd(filePath, options) {
2015
2058
  const block = renderManagedBlock(options.lang, options.docsRepo);
2059
+ const segment = renderManagedSegment(options.lang, options.docsRepo);
2016
2060
  const exists = await fs.pathExists(filePath);
2017
2061
  if (!exists) {
2018
2062
  await fs.writeFile(filePath, block, "utf-8");
2019
2063
  return { changed: true, action: "created" };
2020
2064
  }
2021
2065
  const current = await fs.readFile(filePath, "utf-8");
2022
- if (current.includes(LEE_SPEC_KIT_AGENTS_BEGIN)) {
2023
- return { changed: false, action: "noop" };
2066
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_AGENTS_BEGIN);
2067
+ const endIndex = current.indexOf(LEE_SPEC_KIT_AGENTS_END);
2068
+ if (beginIndex !== -1 && endIndex !== -1 && beginIndex <= endIndex) {
2069
+ const replaceEnd = endIndex + LEE_SPEC_KIT_AGENTS_END.length;
2070
+ const next2 = `${current.slice(0, beginIndex)}${segment}${current.slice(replaceEnd)}`;
2071
+ if (next2 === current) {
2072
+ return { changed: false, action: "noop" };
2073
+ }
2074
+ await fs.writeFile(filePath, next2, "utf-8");
2075
+ return { changed: true, action: "updated" };
2024
2076
  }
2025
2077
  let next = current;
2026
2078
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
@@ -2158,7 +2210,7 @@ function initCommand(program2) {
2158
2210
  } catch (error) {
2159
2211
  if (error instanceof Error && error.message === "canceled") {
2160
2212
  const lang2 = options.lang ?? DEFAULT_LANG;
2161
- console.log(chalk8.yellow(`
2213
+ console.log(chalk9.yellow(`
2162
2214
  ${tr(lang2, "cli", "common.canceled")}`));
2163
2215
  return;
2164
2216
  }
@@ -2166,8 +2218,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
2166
2218
  const cliError = toCliError(error);
2167
2219
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
2168
2220
  console.error(
2169
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
2170
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
2221
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
2222
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
2171
2223
  );
2172
2224
  printCliErrorSuggestions(suggestions, lang);
2173
2225
  process.exitCode = 1;
@@ -2177,7 +2229,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
2177
2229
  }
2178
2230
  async function runInit(options) {
2179
2231
  const cwd = process.cwd();
2180
- const defaultName = path12.basename(cwd);
2232
+ const defaultName = path13.basename(cwd);
2181
2233
  let projectName = options.name || defaultName;
2182
2234
  let projectType = options.type;
2183
2235
  let components = parseComponentsOption(options.components);
@@ -2188,7 +2240,7 @@ async function runInit(options) {
2188
2240
  let docsRemote = options.docsRemote;
2189
2241
  let projectRoot;
2190
2242
  const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
2191
- const targetDir = path12.resolve(cwd, options.dir || "./docs");
2243
+ const targetDir = path13.resolve(cwd, options.dir || "./docs");
2192
2244
  const skipPrompts = !!options.yes || !!options.nonInteractive;
2193
2245
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
2194
2246
  throw createCliError(
@@ -2228,18 +2280,18 @@ async function runInit(options) {
2228
2280
  }
2229
2281
  console.log();
2230
2282
  console.log(
2231
- chalk8.blue(`${tr(lang, "cli", "init.currentDirectoryLabel")}: ${cwd}`)
2283
+ chalk9.blue(`${tr(lang, "cli", "init.currentDirectoryLabel")}: ${cwd}`)
2232
2284
  );
2233
2285
  if (isInsideGitRepo) {
2234
- console.log(chalk8.green(tr(lang, "cli", "init.gitDetected")));
2286
+ console.log(chalk9.green(tr(lang, "cli", "init.gitDetected")));
2235
2287
  console.log();
2236
- console.log(chalk8.gray(tr(lang, "cli", "init.insideProjectRoot")));
2237
- console.log(chalk8.gray(tr(lang, "cli", "init.modeEmbeddedDesc")));
2238
- console.log(chalk8.gray(tr(lang, "cli", "init.modeStandaloneDesc")));
2239
- 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")));
2240
2292
  } else {
2241
- console.log(chalk8.yellow(tr(lang, "cli", "init.gitNotDetected")));
2242
- 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")));
2243
2295
  }
2244
2296
  console.log();
2245
2297
  const response = await prompts(
@@ -2561,31 +2613,31 @@ async function runInit(options) {
2561
2613
  initial: false
2562
2614
  });
2563
2615
  if (!overwrite) {
2564
- console.log(chalk8.yellow(tr(lang, "cli", "common.canceled")));
2616
+ console.log(chalk9.yellow(tr(lang, "cli", "common.canceled")));
2565
2617
  return;
2566
2618
  }
2567
2619
  }
2568
2620
  }
2569
2621
  }
2570
2622
  console.log();
2571
- console.log(chalk8.blue(tr(lang, "cli", "init.log.creatingDocs")));
2623
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.creatingDocs")));
2572
2624
  console.log(
2573
- chalk8.gray(
2625
+ chalk9.gray(
2574
2626
  ` ${tr(lang, "cli", "init.log.projectLabel")}: ${projectName}`
2575
2627
  )
2576
2628
  );
2577
2629
  console.log(
2578
- chalk8.gray(` ${tr(lang, "cli", "init.log.typeLabel")}: ${projectType}`)
2630
+ chalk9.gray(` ${tr(lang, "cli", "init.log.typeLabel")}: ${projectType}`)
2579
2631
  );
2580
2632
  console.log(
2581
- chalk8.gray(` ${tr(lang, "cli", "init.log.langLabel")}: ${lang}`)
2633
+ chalk9.gray(` ${tr(lang, "cli", "init.log.langLabel")}: ${lang}`)
2582
2634
  );
2583
2635
  console.log(
2584
- chalk8.gray(` ${tr(lang, "cli", "init.log.pathLabel")}: ${targetDir}`)
2636
+ chalk9.gray(` ${tr(lang, "cli", "init.log.pathLabel")}: ${targetDir}`)
2585
2637
  );
2586
2638
  console.log();
2587
2639
  const templatesDir = getTemplatesDir();
2588
- const commonPath = path12.join(templatesDir, lang, "common");
2640
+ const commonPath = path13.join(templatesDir, lang, "common");
2589
2641
  if (!await fs.pathExists(commonPath)) {
2590
2642
  throw new Error(
2591
2643
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
@@ -2594,11 +2646,11 @@ async function runInit(options) {
2594
2646
  const fsAdapter = new DefaultFileSystemAdapter();
2595
2647
  await copyTemplates(fsAdapter, commonPath, targetDir);
2596
2648
  if (projectType === "multi") {
2597
- const featuresRoot = path12.join(targetDir, "features");
2649
+ const featuresRoot = path13.join(targetDir, "features");
2598
2650
  for (const component of components) {
2599
- const componentDir = path12.join(featuresRoot, component);
2651
+ const componentDir = path13.join(featuresRoot, component);
2600
2652
  await fs.ensureDir(componentDir);
2601
- const readmePath = path12.join(componentDir, "README.md");
2653
+ const readmePath = path13.join(componentDir, "README.md");
2602
2654
  if (!await fs.pathExists(readmePath)) {
2603
2655
  await fs.writeFile(
2604
2656
  readmePath,
@@ -2659,20 +2711,20 @@ async function runInit(options) {
2659
2711
  config.projectRoot = projectRoot;
2660
2712
  }
2661
2713
  }
2662
- const configPath = path12.join(targetDir, ".lee-spec-kit.json");
2714
+ const configPath = path13.join(targetDir, ".lee-spec-kit.json");
2663
2715
  await fs.writeJson(configPath, config, { spaces: 2 });
2664
2716
  const extraCommitPathsAbs = [];
2665
2717
  try {
2666
2718
  if (docsRepo === "embedded") {
2667
2719
  const repoRoot = getGitTopLevelOrNull(cwd) || cwd;
2668
- const agentsMdPath = path12.join(repoRoot, "AGENTS.md");
2720
+ const agentsMdPath = path13.join(repoRoot, "AGENTS.md");
2669
2721
  const result = await upsertLeeSpecKitAgentsMd(agentsMdPath, {
2670
2722
  lang,
2671
2723
  docsRepo
2672
2724
  });
2673
2725
  if (result.changed) extraCommitPathsAbs.push(agentsMdPath);
2674
2726
  } else {
2675
- await upsertLeeSpecKitAgentsMd(path12.join(targetDir, "AGENTS.md"), {
2727
+ await upsertLeeSpecKitAgentsMd(path13.join(targetDir, "AGENTS.md"), {
2676
2728
  lang,
2677
2729
  docsRepo
2678
2730
  });
@@ -2682,16 +2734,16 @@ async function runInit(options) {
2682
2734
  } else if (projectRoot && typeof projectRoot === "object") {
2683
2735
  roots.push(...Object.values(projectRoot));
2684
2736
  }
2685
- const resolvedCwd = path12.resolve(cwd);
2737
+ const resolvedCwd = path13.resolve(cwd);
2686
2738
  for (const raw of roots) {
2687
2739
  const value = String(raw || "").trim();
2688
2740
  if (!value) continue;
2689
- const abs = path12.resolve(cwd, value);
2690
- if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path12.sep}`)) {
2741
+ const abs = path13.resolve(cwd, value);
2742
+ if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path13.sep}`)) {
2691
2743
  if (await fs.pathExists(abs)) {
2692
2744
  const stat = await fs.stat(abs);
2693
2745
  if (stat.isDirectory()) {
2694
- await upsertLeeSpecKitAgentsMd(path12.join(abs, "AGENTS.md"), {
2746
+ await upsertLeeSpecKitAgentsMd(path13.join(abs, "AGENTS.md"), {
2695
2747
  lang,
2696
2748
  docsRepo
2697
2749
  });
@@ -2702,7 +2754,7 @@ async function runInit(options) {
2702
2754
  }
2703
2755
  } catch {
2704
2756
  }
2705
- console.log(chalk8.green(tr(lang, "cli", "init.log.docsCreated")));
2757
+ console.log(chalk9.green(tr(lang, "cli", "init.log.docsCreated")));
2706
2758
  console.log();
2707
2759
  await initGit(
2708
2760
  cwd,
@@ -2713,14 +2765,14 @@ async function runInit(options) {
2713
2765
  docsRemote,
2714
2766
  extraCommitPathsAbs
2715
2767
  );
2716
- console.log(chalk8.blue(tr(lang, "cli", "init.log.nextStepsTitle")));
2768
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.nextStepsTitle")));
2717
2769
  console.log(
2718
- chalk8.gray(
2770
+ chalk9.gray(
2719
2771
  tr(lang, "cli", "init.log.nextSteps1", { docsDir: targetDir })
2720
2772
  )
2721
2773
  );
2722
- console.log(chalk8.gray(tr(lang, "cli", "init.log.nextSteps2")));
2723
- 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")));
2724
2776
  console.log();
2725
2777
  },
2726
2778
  { owner: "init" }
@@ -2775,31 +2827,31 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2775
2827
  try {
2776
2828
  runGitOrThrow(["rev-parse", "--is-inside-work-tree"], gitWorkdir);
2777
2829
  console.log(
2778
- chalk8.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit"))
2830
+ chalk9.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit"))
2779
2831
  );
2780
2832
  } catch {
2781
- console.log(chalk8.blue(tr(lang, "cli", "init.log.gitInit")));
2833
+ console.log(chalk9.blue(tr(lang, "cli", "init.log.gitInit")));
2782
2834
  runGitOrThrow(["init"], gitWorkdir);
2783
2835
  }
2784
- const relativePath = docsRepo === "standalone" ? "." : path12.relative(gitWorkdir, targetDir);
2836
+ const relativePath = docsRepo === "standalone" ? "." : path13.relative(gitWorkdir, targetDir);
2785
2837
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2786
2838
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2787
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.stagedChangesSkip")));
2788
- 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")));
2789
2841
  console.log();
2790
2842
  return;
2791
2843
  }
2792
2844
  if (relativePath !== "." && isPathIgnored(gitWorkdir, relativePath)) {
2793
2845
  const repoRelativePath = toRepoRelativePath(gitWorkdir, relativePath);
2794
2846
  console.log(
2795
- chalk8.yellow(
2847
+ chalk9.yellow(
2796
2848
  tr(lang, "cli", "init.warn.docsPathIgnoredSkipCommit", {
2797
2849
  path: repoRelativePath
2798
2850
  })
2799
2851
  )
2800
2852
  );
2801
2853
  console.log(
2802
- chalk8.gray(
2854
+ chalk9.gray(
2803
2855
  tr(lang, "cli", "init.warn.docsPathIgnoredHint", {
2804
2856
  path: repoRelativePath
2805
2857
  })
@@ -2808,7 +2860,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2808
2860
  console.log();
2809
2861
  return;
2810
2862
  }
2811
- 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) => path13.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
2812
2864
  const pathsToStage = [relativePath, ...extraRelativePaths];
2813
2865
  for (const p of pathsToStage) {
2814
2866
  runGitOrThrow(["add", p], gitWorkdir);
@@ -2827,34 +2879,34 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2827
2879
  try {
2828
2880
  runGitOrThrow(["remote", "add", "origin", docsRemote], gitWorkdir);
2829
2881
  console.log(
2830
- chalk8.green(
2882
+ chalk9.green(
2831
2883
  tr(lang, "cli", "init.log.gitRemoteSet", { remote: docsRemote })
2832
2884
  )
2833
2885
  );
2834
2886
  } catch {
2835
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.gitRemoteExists")));
2887
+ console.log(chalk9.yellow(tr(lang, "cli", "init.warn.gitRemoteExists")));
2836
2888
  }
2837
2889
  }
2838
- console.log(chalk8.green(tr(lang, "cli", "init.log.gitInitialCommitDone")));
2890
+ console.log(chalk9.green(tr(lang, "cli", "init.log.gitInitialCommitDone")));
2839
2891
  console.log();
2840
2892
  } catch {
2841
- console.log(chalk8.yellow(tr(lang, "cli", "init.warn.skipGitInit")));
2893
+ console.log(chalk9.yellow(tr(lang, "cli", "init.warn.skipGitInit")));
2842
2894
  console.log();
2843
2895
  }
2844
2896
  }
2845
2897
  function getAncestorDirs(startDir) {
2846
2898
  const dirs = [];
2847
- let current = path12.resolve(startDir);
2899
+ let current = path13.resolve(startDir);
2848
2900
  while (true) {
2849
2901
  dirs.push(current);
2850
- const parent = path12.dirname(current);
2902
+ const parent = path13.dirname(current);
2851
2903
  if (parent === current) break;
2852
2904
  current = parent;
2853
2905
  }
2854
2906
  return dirs;
2855
2907
  }
2856
2908
  function hasWorkspaceBoundary(dir) {
2857
- return fs.existsSync(path12.join(dir, "package.json")) || fs.existsSync(path12.join(dir, ".git"));
2909
+ return fs.existsSync(path13.join(dir, "package.json")) || fs.existsSync(path13.join(dir, ".git"));
2858
2910
  }
2859
2911
  function getSearchBaseDirs(cwd) {
2860
2912
  const ancestors = getAncestorDirs(cwd);
@@ -2870,7 +2922,7 @@ function normalizeComponentKeys(value) {
2870
2922
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2871
2923
  }
2872
2924
  async function inferComponentsFromFeaturesDir(docsDir) {
2873
- const featuresPath = path12.join(docsDir, "features");
2925
+ const featuresPath = path13.join(docsDir, "features");
2874
2926
  if (!await fs.pathExists(featuresPath)) return [];
2875
2927
  const entries = await fs.readdir(featuresPath, { withFileTypes: true });
2876
2928
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
@@ -2881,21 +2933,21 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2881
2933
  async function getConfig(cwd) {
2882
2934
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2883
2935
  const baseDirs = [
2884
- ...explicitDocsDir ? [path12.resolve(explicitDocsDir)] : [],
2936
+ ...explicitDocsDir ? [path13.resolve(explicitDocsDir)] : [],
2885
2937
  ...getSearchBaseDirs(cwd)
2886
2938
  ];
2887
2939
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2888
2940
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2889
2941
  for (const baseDir of baseDirs) {
2890
- const resolvedBaseDir = path12.resolve(baseDir);
2942
+ const resolvedBaseDir = path13.resolve(baseDir);
2891
2943
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2892
2944
  visitedBaseDirs.add(resolvedBaseDir);
2893
- const possibleDocsDirs = [path12.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2945
+ const possibleDocsDirs = [path13.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2894
2946
  for (const docsDir of possibleDocsDirs) {
2895
- const resolvedDocsDir = path12.resolve(docsDir);
2947
+ const resolvedDocsDir = path13.resolve(docsDir);
2896
2948
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2897
2949
  visitedDocsDirs.add(resolvedDocsDir);
2898
- const configPath = path12.join(resolvedDocsDir, ".lee-spec-kit.json");
2950
+ const configPath = path13.join(resolvedDocsDir, ".lee-spec-kit.json");
2899
2951
  if (await fs.pathExists(configPath)) {
2900
2952
  try {
2901
2953
  const configFile = await fs.readJson(configPath);
@@ -2925,16 +2977,16 @@ async function getConfig(cwd) {
2925
2977
  } catch {
2926
2978
  }
2927
2979
  }
2928
- const agentsPath = path12.join(resolvedDocsDir, "agents");
2929
- const featuresPath = path12.join(resolvedDocsDir, "features");
2980
+ const agentsPath = path13.join(resolvedDocsDir, "agents");
2981
+ const featuresPath = path13.join(resolvedDocsDir, "features");
2930
2982
  if (await fs.pathExists(agentsPath) && await fs.pathExists(featuresPath)) {
2931
2983
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2932
2984
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2933
2985
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2934
2986
  const langProbeCandidates = [
2935
- path12.join(agentsPath, "custom.md"),
2936
- path12.join(agentsPath, "constitution.md"),
2937
- path12.join(agentsPath, "agents.md")
2987
+ path13.join(agentsPath, "custom.md"),
2988
+ path13.join(agentsPath, "constitution.md"),
2989
+ path13.join(agentsPath, "agents.md")
2938
2990
  ];
2939
2991
  let lang = "en";
2940
2992
  for (const candidate of langProbeCandidates) {
@@ -3005,18 +3057,18 @@ async function patchMarkdownIfExists(filePath, transform) {
3005
3057
  await fs.writeFile(filePath, transform(content), "utf-8");
3006
3058
  }
3007
3059
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
3008
- await patchMarkdownIfExists(path12.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3060
+ await patchMarkdownIfExists(path13.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3009
3061
  await patchMarkdownIfExists(
3010
- path12.join(featureDir, "tasks.md"),
3062
+ path13.join(featureDir, "tasks.md"),
3011
3063
  (content) => sanitizeTasksForLocal(content, lang)
3012
3064
  );
3013
- await fs.remove(path12.join(featureDir, "issue.md"));
3014
- await fs.remove(path12.join(featureDir, "pr.md"));
3065
+ await fs.remove(path13.join(featureDir, "issue.md"));
3066
+ await fs.remove(path13.join(featureDir, "pr.md"));
3015
3067
  }
3016
3068
 
3017
3069
  // src/commands/feature.ts
3018
3070
  function featureCommand(program2) {
3019
- 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) => {
3071
+ 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) => {
3020
3072
  try {
3021
3073
  const result = await runFeature(name, options);
3022
3074
  if (options.json) {
@@ -3049,7 +3101,7 @@ function featureCommand(program2) {
3049
3101
  );
3050
3102
  return;
3051
3103
  }
3052
- console.log(chalk8.yellow(`
3104
+ console.log(chalk9.yellow(`
3053
3105
  ${tr(lang2, "cli", "common.canceled")}`));
3054
3106
  return;
3055
3107
  }
@@ -3070,8 +3122,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
3070
3122
  return;
3071
3123
  }
3072
3124
  console.error(
3073
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
3074
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
3125
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
3126
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
3075
3127
  );
3076
3128
  printCliErrorSuggestions(suggestions, lang);
3077
3129
  process.exitCode = 1;
@@ -3097,6 +3149,7 @@ async function runFeature(name, options) {
3097
3149
  projectType,
3098
3150
  config.components
3099
3151
  );
3152
+ const linkedIdea = options.idea ? await resolveIdeaReference(docsDir, options.idea, lang) : null;
3100
3153
  assertValid(
3101
3154
  validateSafeNameWithLang(name, lang),
3102
3155
  tr(lang, "cli", "validation.context.featureName"),
@@ -3162,19 +3215,19 @@ async function runFeature(name, options) {
3162
3215
  }
3163
3216
  let featuresDir;
3164
3217
  if (projectType === "multi") {
3165
- featuresDir = path12.join(docsDir, "features", component);
3218
+ featuresDir = path13.join(docsDir, "features", component);
3166
3219
  } else {
3167
- featuresDir = path12.join(docsDir, "features");
3220
+ featuresDir = path13.join(docsDir, "features");
3168
3221
  }
3169
3222
  const featureFolderName = `${featureId}-${name}`;
3170
- const featureDir = path12.join(featuresDir, featureFolderName);
3223
+ const featureDir = path13.join(featuresDir, featureFolderName);
3171
3224
  if (await fs.pathExists(featureDir)) {
3172
3225
  throw createCliError(
3173
3226
  "INVALID_ARGUMENT",
3174
3227
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
3175
3228
  );
3176
3229
  }
3177
- const featureBasePath = path12.join(
3230
+ const featureBasePath = path13.join(
3178
3231
  getTemplatesDir(),
3179
3232
  lang,
3180
3233
  "common",
@@ -3219,23 +3272,30 @@ async function runFeature(name, options) {
3219
3272
  }
3220
3273
  const fsAdapter = new DefaultFileSystemAdapter();
3221
3274
  await replaceInFiles(fsAdapter, featureDir, replacements);
3275
+ if (linkedIdea) {
3276
+ await stampIdeaReferenceInSpec(
3277
+ path13.join(featureDir, "spec.md"),
3278
+ path13.relative(featureDir, linkedIdea.path)
3279
+ );
3280
+ await markIdeaAsFeatureized(linkedIdea.path, featureFolderName);
3281
+ }
3222
3282
  if (config.workflow?.mode === "local") {
3223
3283
  await applyLocalWorkflowTemplateToFeatureDir(featureDir, lang);
3224
3284
  }
3225
3285
  if (!options.json) {
3226
3286
  console.log();
3227
3287
  console.log(
3228
- chalk8.green(tr(lang, "cli", "feature.created", { path: featureDir }))
3288
+ chalk9.green(tr(lang, "cli", "feature.created", { path: featureDir }))
3229
3289
  );
3230
3290
  console.log();
3231
- console.log(chalk8.blue(tr(lang, "cli", "feature.nextStepsTitle")));
3291
+ console.log(chalk9.blue(tr(lang, "cli", "feature.nextStepsTitle")));
3232
3292
  console.log(
3233
- chalk8.gray(
3293
+ chalk9.gray(
3234
3294
  tr(lang, "cli", "feature.nextSteps1", { path: featureDir })
3235
3295
  )
3236
3296
  );
3237
- console.log(chalk8.gray(tr(lang, "cli", "feature.nextSteps2")));
3238
- console.log(chalk8.gray(tr(lang, "cli", "feature.nextSteps3")));
3297
+ console.log(chalk9.gray(tr(lang, "cli", "feature.nextSteps2")));
3298
+ console.log(chalk9.gray(tr(lang, "cli", "feature.nextSteps3")));
3239
3299
  console.log();
3240
3300
  }
3241
3301
  return {
@@ -3243,18 +3303,115 @@ async function runFeature(name, options) {
3243
3303
  featureName: name,
3244
3304
  component: projectType === "multi" ? component : void 0,
3245
3305
  featurePath: featureDir,
3246
- featurePathFromDocs: path12.relative(docsDir, featureDir)
3306
+ featurePathFromDocs: path13.relative(docsDir, featureDir)
3247
3307
  };
3248
3308
  },
3249
3309
  { owner: "feature" }
3250
3310
  );
3251
3311
  }
3312
+ async function resolveIdeaReference(docsDir, ref, lang) {
3313
+ const ideasDir = path13.join(docsDir, "ideas");
3314
+ const trimmedRef = ref.trim();
3315
+ if (!trimmedRef) {
3316
+ throw createCliError(
3317
+ "INVALID_ARGUMENT",
3318
+ tr(lang, "cli", "feature.ideaNotFound", { ref })
3319
+ );
3320
+ }
3321
+ if (trimmedRef.includes("/") || trimmedRef.endsWith(".md")) {
3322
+ const candidate = path13.resolve(process.cwd(), trimmedRef);
3323
+ if (await fs.pathExists(candidate)) {
3324
+ return { path: candidate };
3325
+ }
3326
+ throw createCliError(
3327
+ "INVALID_ARGUMENT",
3328
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3329
+ );
3330
+ }
3331
+ if (!await fs.pathExists(ideasDir)) {
3332
+ throw createCliError(
3333
+ "INVALID_ARGUMENT",
3334
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3335
+ );
3336
+ }
3337
+ const entries = await fs.readdir(ideasDir, { withFileTypes: true });
3338
+ const files = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md")).map((entry) => entry.name);
3339
+ const exactName = `${trimmedRef}.md`;
3340
+ if (files.includes(exactName)) {
3341
+ return { path: path13.join(ideasDir, exactName) };
3342
+ }
3343
+ const byId = /^I\d{3,}$/.test(trimmedRef) ? files.filter((name) => name.startsWith(`${trimmedRef}-`)) : [];
3344
+ if (byId.length === 1) {
3345
+ return { path: path13.join(ideasDir, byId[0]) };
3346
+ }
3347
+ if (byId.length > 1) {
3348
+ throw createCliError(
3349
+ "INVALID_ARGUMENT",
3350
+ tr(lang, "cli", "feature.ideaAmbiguous", { ref: trimmedRef })
3351
+ );
3352
+ }
3353
+ throw createCliError(
3354
+ "INVALID_ARGUMENT",
3355
+ tr(lang, "cli", "feature.ideaNotFound", { ref: trimmedRef })
3356
+ );
3357
+ }
3358
+ async function stampIdeaReferenceInSpec(specPath, relativeIdeaPath) {
3359
+ const normalizedPath = relativeIdeaPath.replace(/\\/g, "/");
3360
+ const ideaLine = `- Idea: \`${normalizedPath}\``;
3361
+ let content = await fs.readFile(specPath, "utf-8");
3362
+ if (content.includes(ideaLine)) {
3363
+ return;
3364
+ }
3365
+ if (content.includes("## Related Documents")) {
3366
+ content = content.replace(
3367
+ "## Related Documents\n\n",
3368
+ `## Related Documents
3369
+
3370
+ ${ideaLine}
3371
+ `
3372
+ );
3373
+ } else {
3374
+ content = `${content.trimEnd()}
3375
+
3376
+ ${ideaLine}
3377
+ `;
3378
+ }
3379
+ await fs.writeFile(specPath, content, "utf-8");
3380
+ }
3381
+ async function markIdeaAsFeatureized(ideaPath, featureFolderName) {
3382
+ let content = await fs.readFile(ideaPath, "utf-8");
3383
+ content = replaceOrAppendIdeaMetadata(content, "Status", "Featureized");
3384
+ content = replaceOrAppendIdeaMetadata(content, "Feature", featureFolderName);
3385
+ await fs.writeFile(ideaPath, content, "utf-8");
3386
+ }
3387
+ function replaceOrAppendIdeaMetadata(content, label, value) {
3388
+ const pattern = new RegExp(`^- \\*\\*${escapeRegExp(label)}\\*\\*:.*$`, "m");
3389
+ const line = `- **${label}**: ${value}`;
3390
+ if (pattern.test(content)) {
3391
+ return content.replace(pattern, line);
3392
+ }
3393
+ const heading = "## Promotion Tracking";
3394
+ if (content.includes(heading)) {
3395
+ return content.replace(heading, `${heading}
3396
+
3397
+ ${line}`);
3398
+ }
3399
+ return `${content.trimEnd()}
3400
+
3401
+ ${heading}
3402
+
3403
+ ${line}
3404
+ `;
3405
+ }
3406
+ function escapeRegExp(value) {
3407
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3408
+ }
3252
3409
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3253
3410
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
3254
3411
  const candidates = [
3255
- ...explicitDocsDir ? [path12.resolve(explicitDocsDir)] : [],
3256
- path12.resolve(cwd, "docs"),
3257
- path12.resolve(cwd)
3412
+ ...explicitDocsDir ? [path13.resolve(explicitDocsDir)] : [],
3413
+ path13.resolve(cwd, "docs"),
3414
+ path13.resolve(cwd)
3258
3415
  ];
3259
3416
  const endAt = Date.now() + timeoutMs;
3260
3417
  while (Date.now() < endAt) {
@@ -3281,12 +3438,12 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3281
3438
  return getConfig(cwd);
3282
3439
  }
3283
3440
  async function getNextFeatureId(docsDir, projectType, components) {
3284
- const featuresDir = path12.join(docsDir, "features");
3441
+ const featuresDir = path13.join(docsDir, "features");
3285
3442
  let max = 0;
3286
3443
  const scanDirs = [];
3287
3444
  if (projectType === "multi") {
3288
3445
  scanDirs.push(
3289
- ...components.map((component) => path12.join(featuresDir, component))
3446
+ ...components.map((component) => path13.join(featuresDir, component))
3290
3447
  );
3291
3448
  } else {
3292
3449
  scanDirs.push(featuresDir);
@@ -3307,6 +3464,167 @@ async function getNextFeatureId(docsDir, projectType, components) {
3307
3464
  const width = Math.max(3, String(next).length);
3308
3465
  return `F${String(next).padStart(width, "0")}`;
3309
3466
  }
3467
+ function ideaCommand(program2) {
3468
+ 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) => {
3469
+ try {
3470
+ const result = await runIdea(name, options);
3471
+ if (options.json) {
3472
+ console.log(
3473
+ JSON.stringify(
3474
+ {
3475
+ status: "ok",
3476
+ reasonCode: "IDEA_CREATED",
3477
+ ideaId: result.ideaId,
3478
+ ideaName: result.ideaName,
3479
+ component: result.component,
3480
+ ideaPath: result.ideaPath,
3481
+ ideaPathFromDocs: result.ideaPathFromDocs
3482
+ },
3483
+ null,
3484
+ 2
3485
+ )
3486
+ );
3487
+ }
3488
+ } catch (error) {
3489
+ const config = await getConfig(process.cwd());
3490
+ const lang = config?.lang ?? DEFAULT_LANG;
3491
+ const cliError = toCliError(error);
3492
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
3493
+ if (options.json) {
3494
+ console.log(
3495
+ JSON.stringify({
3496
+ status: "error",
3497
+ reasonCode: cliError.code,
3498
+ error: cliError.message,
3499
+ suggestions
3500
+ })
3501
+ );
3502
+ process.exitCode = 1;
3503
+ return;
3504
+ }
3505
+ console.error(
3506
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
3507
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
3508
+ );
3509
+ printCliErrorSuggestions(suggestions, lang);
3510
+ process.exitCode = 1;
3511
+ }
3512
+ });
3513
+ }
3514
+ async function runIdea(name, options) {
3515
+ const cwd = process.cwd();
3516
+ const config = await getConfig(cwd);
3517
+ if (!config) {
3518
+ throw createCliError(
3519
+ "DOCS_NOT_FOUND",
3520
+ tr(DEFAULT_LANG, "cli", "common.docsNotFound")
3521
+ );
3522
+ }
3523
+ const { docsDir, projectType, lang } = config;
3524
+ const configuredComponents = resolveProjectComponents(
3525
+ projectType,
3526
+ config.components
3527
+ );
3528
+ assertValid(
3529
+ validateSafeNameWithLang(name, lang),
3530
+ tr(lang, "cli", "validation.context.ideaName"),
3531
+ lang
3532
+ );
3533
+ let component = (options.component || "").trim().toLowerCase();
3534
+ if (component && projectType === "single") {
3535
+ throw createCliError(
3536
+ "INVALID_ARGUMENT",
3537
+ "`--component` can only be used in multi mode."
3538
+ );
3539
+ }
3540
+ if (projectType === "multi" && component) {
3541
+ assertAllowedComponent(component, configuredComponents);
3542
+ }
3543
+ return withFileLock(
3544
+ getDocsLockPath(docsDir),
3545
+ async () => {
3546
+ const ideaId = options.id ? validateProvidedIdeaId(options.id, lang) : await getNextIdeaId(docsDir);
3547
+ const ideasDir = path13.join(docsDir, "ideas");
3548
+ const ideaFileName = `${ideaId}-${name}.md`;
3549
+ const ideaPath = path13.join(ideasDir, ideaFileName);
3550
+ if (await fs.pathExists(ideaPath)) {
3551
+ throw createCliError(
3552
+ "INVALID_ARGUMENT",
3553
+ tr(lang, "cli", "idea.fileExists", { path: ideaPath })
3554
+ );
3555
+ }
3556
+ const templatePath = path13.join(
3557
+ getTemplatesDir(),
3558
+ lang,
3559
+ "common",
3560
+ "ideas",
3561
+ "idea.md"
3562
+ );
3563
+ if (!await fs.pathExists(templatePath)) {
3564
+ throw createCliError(
3565
+ "DOCS_NOT_FOUND",
3566
+ tr(lang, "cli", "idea.templateNotFound")
3567
+ );
3568
+ }
3569
+ await fs.mkdir(ideasDir, { recursive: true });
3570
+ const template = await fs.readFile(templatePath, "utf-8");
3571
+ const content = applyIdeaTemplate(template, {
3572
+ ideaId,
3573
+ name,
3574
+ description: options.desc || "",
3575
+ component: component || "-",
3576
+ created: getLocalDateString()
3577
+ });
3578
+ await fs.writeFile(ideaPath, content, "utf-8");
3579
+ if (!options.json) {
3580
+ console.log();
3581
+ console.log(chalk9.green(tr(lang, "cli", "idea.created", { path: ideaPath })));
3582
+ console.log();
3583
+ console.log(chalk9.blue(tr(lang, "cli", "idea.nextStepsTitle")));
3584
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps1")));
3585
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps2", { ideaId })));
3586
+ console.log(chalk9.gray(tr(lang, "cli", "idea.nextSteps3")));
3587
+ console.log();
3588
+ }
3589
+ return {
3590
+ ideaId,
3591
+ ideaName: name,
3592
+ component: component || void 0,
3593
+ ideaPath,
3594
+ ideaPathFromDocs: path13.relative(docsDir, ideaPath)
3595
+ };
3596
+ },
3597
+ { owner: "idea" }
3598
+ );
3599
+ }
3600
+ function validateProvidedIdeaId(id, lang) {
3601
+ assertValid(
3602
+ validateIdeaIdWithLang(id, lang),
3603
+ tr(lang, "cli", "validation.context.ideaId"),
3604
+ lang
3605
+ );
3606
+ return id;
3607
+ }
3608
+ function applyIdeaTemplate(template, values) {
3609
+ 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);
3610
+ }
3611
+ async function getNextIdeaId(docsDir) {
3612
+ const ideasDir = path13.join(docsDir, "ideas");
3613
+ let max = 0;
3614
+ if (await fs.pathExists(ideasDir)) {
3615
+ const entries = await fs.readdir(ideasDir, { withFileTypes: true });
3616
+ for (const entry of entries) {
3617
+ if (!entry.isFile()) continue;
3618
+ const match = entry.name.match(/^I(\d+)-/);
3619
+ if (!match) continue;
3620
+ const num = parseInt(match[1], 10);
3621
+ if (num > max) max = num;
3622
+ }
3623
+ }
3624
+ const next = max + 1;
3625
+ const width = Math.max(3, String(next).length);
3626
+ return `I${String(next).padStart(width, "0")}`;
3627
+ }
3310
3628
  var DefaultCommandAdapter = class {
3311
3629
  execSync(command, options) {
3312
3630
  return execSync(command, options);
@@ -3566,15 +3884,15 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3566
3884
  2. \uB9AC\uBDF0 \uBC94\uC704\uB97C \uBD84\uB9AC\uD574 \uD655\uC778\uD558\uC138\uC694.
3567
3885
  - main \uAE30\uC900: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
3568
3886
  - worktree \uAE30\uC900: 'git diff --name-only', 'git diff --name-only --cached', 'git ls-files --others --exclude-standard'
3569
- 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.
3570
- 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.
3887
+ 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.
3888
+ 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.
3571
3889
  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.
3572
3890
  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.
3573
3891
  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.
3574
3892
  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.
3575
3893
  9. \uC2E4\uD589\uD55C \uBA85\uB839\uC774 \uC788\uC73C\uBA74 \`commandsExecuted\`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.
3576
- 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.
3577
- 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.`;
3894
+ 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.
3895
+ 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.`;
3578
3896
  }
3579
3897
  return `Conduct a pre-PR code review.
3580
3898
  0. Reuse the existing helper/sub-agent for this feature review if one already exists. Default to a single helper agent.
@@ -3582,15 +3900,15 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3582
3900
  2. Split and check the review scope.
3583
3901
  - Main scope: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
3584
3902
  - Worktree scope: 'git diff --name-only', 'git diff --name-only --cached', 'git ls-files --others --exclude-standard'
3585
- 3. Evaluate whether the implementation actually fits the feature intent. Capture concrete \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, and \`residualRisks\`, and explicitly set \`specAlignmentChecked\`.
3586
- 4. Generate a 'review-trace.json' file for all changed files, including \`findingCount\`, \`blockingFindings\`, evaluations for risk, security, perf, maintainability, and specific fileLine locators.
3903
+ 3. Evaluate whether the implementation actually fits the feature intent. Capture concrete \`featureIntentSummary\`, \`implementationFit\`, \`missingCases\`, \`residualRisks\`, and \`approvalRationale\`, and explicitly set \`specAlignmentChecked\`.
3904
+ 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.
3587
3905
  5. The baseline is '${fallbackText}'. Always perform the 'Pre-PR Core Checklist' section of the 'create-pr' document.
3588
3906
  6. Priority skills: ${skills.length > 0 ? skills.join(", ") : "None"} for deeper technical review.
3589
3907
  7. Run extra audit/targeted verification only when the review truly needs more evidence. Spawn additional helper agents only when necessary.
3590
3908
  8. If helper-agent quota is exhausted, continue the review in the main agent and just keep the evidence consistent.
3591
3909
  9. Record commands in \`commandsExecuted\` only when you actually ran them.
3592
- 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.
3593
- 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.`;
3910
+ 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.
3911
+ 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.`;
3594
3912
  }
3595
3913
  function getCodeReviewPrompt(lang) {
3596
3914
  if (lang === "ko") {
@@ -3730,7 +4048,7 @@ function isNonNegativeIntegerValue(value) {
3730
4048
  }
3731
4049
  function isLikelyCurrentPrePrReviewEvidence(evidencePath, feature) {
3732
4050
  try {
3733
- const raw = fs9.readFileSync(evidencePath, "utf-8");
4051
+ const raw = fs10.readFileSync(evidencePath, "utf-8");
3734
4052
  const parsed = JSON.parse(raw);
3735
4053
  const evidenceFeature = (parsed.feature || "").toString().trim();
3736
4054
  if (evidenceFeature && evidenceFeature !== feature.folderName) {
@@ -3746,31 +4064,31 @@ function resolvePrePrReviewEvidencePath(feature) {
3746
4064
  const candidates = [];
3747
4065
  const explicit = (feature.prePrReview.evidence || "").trim();
3748
4066
  if (explicit && explicit !== "-") {
3749
- if (path12.isAbsolute(explicit)) {
4067
+ if (path13.isAbsolute(explicit)) {
3750
4068
  candidates.push(explicit);
3751
4069
  } else {
3752
- candidates.push(path12.resolve(feature.path, explicit));
3753
- candidates.push(path12.resolve(docsRoot, explicit));
4070
+ candidates.push(path13.resolve(feature.path, explicit));
4071
+ candidates.push(path13.resolve(docsRoot, explicit));
3754
4072
  const normalizedExplicit = explicit.replace(/\\/g, "/");
3755
4073
  if (normalizedExplicit.startsWith("docs/")) {
3756
4074
  const withoutDocsPrefix = normalizedExplicit.slice("docs/".length);
3757
4075
  if (withoutDocsPrefix) {
3758
- candidates.push(path12.resolve(docsRoot, withoutDocsPrefix));
4076
+ candidates.push(path13.resolve(docsRoot, withoutDocsPrefix));
3759
4077
  }
3760
4078
  }
3761
4079
  }
3762
4080
  }
3763
- candidates.push(path12.join(feature.path, "review-trace.json"));
3764
- candidates.push(path12.join(docsRoot, "review-trace.json"));
4081
+ candidates.push(path13.join(feature.path, "review-trace.json"));
4082
+ candidates.push(path13.join(docsRoot, "review-trace.json"));
3765
4083
  const seen = /* @__PURE__ */ new Set();
3766
4084
  for (const candidate of candidates) {
3767
- const abs = path12.resolve(candidate);
4085
+ const abs = path13.resolve(candidate);
3768
4086
  if (seen.has(abs)) continue;
3769
4087
  seen.add(abs);
3770
- if (!fs9.existsSync(abs)) continue;
4088
+ if (!fs10.existsSync(abs)) continue;
3771
4089
  if (!abs.toLowerCase().endsWith(".json")) continue;
3772
4090
  if (!isLikelyCurrentPrePrReviewEvidence(abs, feature)) continue;
3773
- const rel = path12.relative(docsRoot, abs).replace(/\\/g, "/");
4091
+ const rel = path13.relative(docsRoot, abs).replace(/\\/g, "/");
3774
4092
  if (rel && !rel.startsWith("../")) {
3775
4093
  return rel;
3776
4094
  }
@@ -3811,8 +4129,8 @@ function getReviewFixCommitGuidance(feature, lang, options) {
3811
4129
  }
3812
4130
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
3813
4131
  if (!projectGitCwd) return null;
3814
- const normalized = path12.resolve(projectGitCwd);
3815
- const marker = `${path12.sep}.worktrees${path12.sep}`;
4132
+ const normalized = path13.resolve(projectGitCwd);
4133
+ const marker = `${path13.sep}.worktrees${path13.sep}`;
3816
4134
  const markerIndex = normalized.lastIndexOf(marker);
3817
4135
  if (markerIndex <= 0) return null;
3818
4136
  const projectRoot = normalized.slice(0, markerIndex);
@@ -3855,7 +4173,7 @@ function toTaskKey(rawTitle) {
3855
4173
  function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3856
4174
  const docsGitCwd = feature.git.docsGitCwd;
3857
4175
  const tasksRelativePath = normalizeGitRelativePath(
3858
- path12.join(feature.docs.featurePathFromDocs, "tasks.md")
4176
+ path13.join(feature.docs.featurePathFromDocs, "tasks.md")
3859
4177
  );
3860
4178
  const diff = readGitText(ctx, docsGitCwd, [
3861
4179
  "diff",
@@ -3930,7 +4248,7 @@ function checkTaskCommitGate(ctx, feature) {
3930
4248
  return { pass: true };
3931
4249
  }
3932
4250
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
3933
- const relativeDocsDir = path12.relative(projectGitCwd, feature.git.docsGitCwd);
4251
+ const relativeDocsDir = path13.relative(projectGitCwd, feature.git.docsGitCwd);
3934
4252
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
3935
4253
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
3936
4254
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -4312,9 +4630,9 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4312
4630
  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);
4313
4631
  const isPrePrReviewMetadataMissing = (f) => isPrePrReviewCurrent(f) && !f.docs.prePrReviewFieldExists;
4314
4632
  const isPrePrReviewFixRequired = (f) => isPrePrReviewCurrent(f) && !!f.prePrReview.decisionOutcome && f.prePrReview.decisionOutcome !== "approve";
4315
- const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status !== "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4316
- const isPrePrReviewRunning = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status === "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4317
- const isPrePrReviewRecord = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && (!!resolvePrePrReviewEvidencePath(f) || prePrReviewPolicy.evidenceMode === "any" && !prePrReviewPolicy.enforceExecutionEvidence);
4633
+ const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status !== "Running" && !resolvePrePrReviewEvidencePath(f);
4634
+ const isPrePrReviewRunning = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status === "Running" && !resolvePrePrReviewEvidencePath(f);
4635
+ const isPrePrReviewRecord = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && !!resolvePrePrReviewEvidencePath(f);
4318
4636
  const getPrePrReviewMetadataActions = () => [
4319
4637
  {
4320
4638
  type: "instruction",
@@ -4358,7 +4676,7 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4358
4676
  cmd: buildSelfCliCommand(buildPrePrReviewRunCommandArgs(f))
4359
4677
  }
4360
4678
  ];
4361
- const getPrePrReviewRunningActions = () => [
4679
+ const getPrePrReviewInProgressActions = () => [
4362
4680
  {
4363
4681
  type: "instruction",
4364
4682
  category: "pre_pr_review_run",
@@ -4896,15 +5214,15 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4896
5214
  actions: (f) => getPrePrReviewRunActions(f)
4897
5215
  },
4898
5216
  {
4899
- id: "pre_pr_review_running",
5217
+ id: "pre_pr_review_in_progress",
4900
5218
  phase: "running",
4901
5219
  owner: "subagent",
4902
5220
  category: "pre_pr_review_run",
4903
5221
  when: (f) => isPrePrReviewRunning(f),
4904
- actions: () => getPrePrReviewRunningActions()
5222
+ actions: () => getPrePrReviewInProgressActions()
4905
5223
  },
4906
5224
  {
4907
- id: "pre_pr_review_record",
5225
+ id: "pre_pr_review_record_pending",
4908
5226
  phase: "record",
4909
5227
  owner: "main",
4910
5228
  category: "pre_pr_review_record",
@@ -5546,17 +5864,17 @@ function isGitPathIgnored(ctx, cwd, relativePath) {
5546
5864
  }
5547
5865
  }
5548
5866
  var GIT_WORKTREE_CACHE = /* @__PURE__ */ new Map();
5549
- var WORKTREE_MARKER = `${path12.sep}.worktrees${path12.sep}`;
5867
+ var WORKTREE_MARKER = `${path13.sep}.worktrees${path13.sep}`;
5550
5868
  function resetContextGitCaches() {
5551
5869
  GIT_WORKTREE_CACHE.clear();
5552
5870
  }
5553
5871
  function isManagedWorktreePath(cwd) {
5554
5872
  if (!cwd) return false;
5555
- const normalized = path12.resolve(cwd);
5873
+ const normalized = path13.resolve(cwd);
5556
5874
  return normalized.includes(WORKTREE_MARKER);
5557
5875
  }
5558
5876
  function resolveProjectRootFromGitCwd(cwd) {
5559
- const normalized = path12.resolve(cwd);
5877
+ const normalized = path13.resolve(cwd);
5560
5878
  const markerIndex = normalized.lastIndexOf(WORKTREE_MARKER);
5561
5879
  if (markerIndex <= 0) return normalized;
5562
5880
  const projectRoot = normalized.slice(0, markerIndex);
@@ -5575,7 +5893,7 @@ function getGitTopLevel(ctx, cwd) {
5575
5893
  }
5576
5894
  function listGitWorktrees(ctx, cwd) {
5577
5895
  const topLevel = getGitTopLevel(ctx, cwd) || cwd;
5578
- const cacheKey = path12.resolve(topLevel);
5896
+ const cacheKey = path13.resolve(topLevel);
5579
5897
  const cached = GIT_WORKTREE_CACHE.get(cacheKey);
5580
5898
  if (cached) return cached;
5581
5899
  try {
@@ -5676,12 +5994,12 @@ function countDocumentLines(content) {
5676
5994
  if (lines[lines.length - 1] === "") return lines.length - 1;
5677
5995
  return lines.length;
5678
5996
  }
5679
- function escapeRegExp(value) {
5997
+ function escapeRegExp2(value) {
5680
5998
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5681
5999
  }
5682
6000
  function extractSpecValue(content, key) {
5683
6001
  const regex = new RegExp(
5684
- `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:\\s*(.*)$`,
6002
+ `^\\s*-\\s*\\*\\*${escapeRegExp2(key)}\\*\\*\\s*:\\s*(.*)$`,
5685
6003
  "m"
5686
6004
  );
5687
6005
  const match = content.match(regex);
@@ -5689,7 +6007,7 @@ function extractSpecValue(content, key) {
5689
6007
  }
5690
6008
  function hasSpecKey(content, key) {
5691
6009
  const regex = new RegExp(
5692
- `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:`,
6010
+ `^\\s*-\\s*\\*\\*${escapeRegExp2(key)}\\*\\*\\s*:`,
5693
6011
  "m"
5694
6012
  );
5695
6013
  return regex.test(content);
@@ -5881,7 +6199,7 @@ function splitReviewLogSections(content, headerRegex) {
5881
6199
  }
5882
6200
  function collectStructuredReviewEntries(section, keys) {
5883
6201
  const lines = section.split("\n");
5884
- const escaped = keys.map((key) => escapeRegExp(key));
6202
+ const escaped = keys.map((key) => escapeRegExp2(key));
5885
6203
  const fieldRegex = new RegExp(
5886
6204
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*(.*)$`,
5887
6205
  "i"
@@ -6063,17 +6381,17 @@ function resolveLocalEvidencePathCandidates(rawValue, context) {
6063
6381
  if (!evidencePath) return [];
6064
6382
  if (/^https?:\/\//i.test(evidencePath)) return [];
6065
6383
  const candidates = /* @__PURE__ */ new Set();
6066
- if (path12.isAbsolute(evidencePath)) {
6067
- candidates.add(path12.resolve(evidencePath));
6384
+ if (path13.isAbsolute(evidencePath)) {
6385
+ candidates.add(path13.resolve(evidencePath));
6068
6386
  } else {
6069
- candidates.add(path12.resolve(context.featurePath, evidencePath));
6070
- candidates.add(path12.resolve(context.docsDir, evidencePath));
6071
- candidates.add(path12.resolve(path12.dirname(context.docsDir), evidencePath));
6387
+ candidates.add(path13.resolve(context.featurePath, evidencePath));
6388
+ candidates.add(path13.resolve(context.docsDir, evidencePath));
6389
+ candidates.add(path13.resolve(path13.dirname(context.docsDir), evidencePath));
6072
6390
  const normalizedEvidencePath = evidencePath.replace(/\\/g, "/");
6073
6391
  if (normalizedEvidencePath.startsWith("docs/")) {
6074
6392
  const withoutDocsPrefix = normalizedEvidencePath.slice("docs/".length);
6075
6393
  if (withoutDocsPrefix) {
6076
- candidates.add(path12.resolve(context.docsDir, withoutDocsPrefix));
6394
+ candidates.add(path13.resolve(context.docsDir, withoutDocsPrefix));
6077
6395
  }
6078
6396
  }
6079
6397
  }
@@ -6135,13 +6453,13 @@ function parsePrLink(value) {
6135
6453
  return trimmed;
6136
6454
  }
6137
6455
  function normalizeGitPath(value) {
6138
- return value.split(path12.sep).join("/");
6456
+ return value.split(path13.sep).join("/");
6139
6457
  }
6140
6458
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
6141
- const relativeDocsDir = path12.relative(projectGitCwd, docsDir);
6459
+ const relativeDocsDir = path13.relative(projectGitCwd, docsDir);
6142
6460
  if (!relativeDocsDir) return [];
6143
- if (path12.isAbsolute(relativeDocsDir)) return [];
6144
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path12.sep}`)) {
6461
+ if (path13.isAbsolute(relativeDocsDir)) return [];
6462
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path13.sep}`)) {
6145
6463
  return [];
6146
6464
  }
6147
6465
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(
@@ -6179,7 +6497,7 @@ function getExpectedWorktreeCandidates(projectGitCwd, issueNumber, slug, folderN
6179
6497
  const seen = /* @__PURE__ */ new Set();
6180
6498
  const out = [];
6181
6499
  for (const name of names) {
6182
- const candidate = path12.resolve(projectRoot, ".worktrees", name);
6500
+ const candidate = path13.resolve(projectRoot, ".worktrees", name);
6183
6501
  if (seen.has(candidate)) continue;
6184
6502
  seen.add(candidate);
6185
6503
  out.push(candidate);
@@ -6193,7 +6511,7 @@ function resolveExistingExpectedWorktreePath(projectGitCwd, issueNumber, slug, f
6193
6511
  slug,
6194
6512
  folderName
6195
6513
  )) {
6196
- if (!fs9.existsSync(candidate)) continue;
6514
+ if (!fs10.existsSync(candidate)) continue;
6197
6515
  return candidate;
6198
6516
  }
6199
6517
  return void 0;
@@ -6223,7 +6541,7 @@ function resolveFeatureWorktreePath(ctx, projectGitCwd, issueNumber, slug, folde
6223
6541
  slug,
6224
6542
  folderName
6225
6543
  )) {
6226
- if (!fs9.existsSync(candidate)) continue;
6544
+ if (!fs10.existsSync(candidate)) continue;
6227
6545
  const branchName = getCurrentBranch(ctx, candidate);
6228
6546
  if (!expectedBranchesSet.has(branchName)) continue;
6229
6547
  return {
@@ -6364,10 +6682,10 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6364
6682
  const normalizedCandidates = uniqueNormalizedPaths(
6365
6683
  candidates.map((candidate) => {
6366
6684
  if (!candidate) return "";
6367
- if (!path12.isAbsolute(candidate)) return candidate;
6368
- const relative = path12.relative(projectGitCwd, candidate);
6685
+ if (!path13.isAbsolute(candidate)) return candidate;
6686
+ const relative = path13.relative(projectGitCwd, candidate);
6369
6687
  if (!relative) return "";
6370
- if (relative === ".." || relative.startsWith(`..${path12.sep}`))
6688
+ if (relative === ".." || relative.startsWith(`..${path13.sep}`))
6371
6689
  return "";
6372
6690
  return relative;
6373
6691
  }).filter(Boolean)
@@ -6381,7 +6699,7 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6381
6699
  if (cached) return [...cached];
6382
6700
  const existing = [];
6383
6701
  for (const candidate of normalizedCandidates) {
6384
- if (await ctx.fs.pathExists(path12.join(projectGitCwd, candidate))) {
6702
+ if (await ctx.fs.pathExists(path13.join(projectGitCwd, candidate))) {
6385
6703
  existing.push(candidate);
6386
6704
  }
6387
6705
  }
@@ -6469,16 +6787,16 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6469
6787
  const lang = options.lang;
6470
6788
  const workflowPolicy = resolveWorkflowPolicy(options.workflow);
6471
6789
  const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
6472
- const folderName = path12.basename(featurePath);
6790
+ const folderName = path13.basename(featurePath);
6473
6791
  const match = folderName.match(/^(F\d+)-(.+)$/);
6474
6792
  const id = match?.[1];
6475
6793
  const slug = match?.[2] || folderName;
6476
- const specPath = path12.join(featurePath, "spec.md");
6477
- const planPath = path12.join(featurePath, "plan.md");
6478
- const tasksPath = path12.join(featurePath, "tasks.md");
6479
- const decisionsPath = path12.join(featurePath, "decisions.md");
6480
- const issueDocPath = path12.join(featurePath, "issue.md");
6481
- const prDocPath = path12.join(featurePath, "pr.md");
6794
+ const specPath = path13.join(featurePath, "spec.md");
6795
+ const planPath = path13.join(featurePath, "plan.md");
6796
+ const tasksPath = path13.join(featurePath, "tasks.md");
6797
+ const decisionsPath = path13.join(featurePath, "decisions.md");
6798
+ const issueDocPath = path13.join(featurePath, "issue.md");
6799
+ const prDocPath = path13.join(featurePath, "pr.md");
6482
6800
  let specStatus;
6483
6801
  let issueNumber;
6484
6802
  const specExists = await ctx.fs.pathExists(specPath);
@@ -6740,7 +7058,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6740
7058
  } else if (workflowPolicy.requireWorktree && tasksSummary.total > tasksSummary.done && !projectInManagedWorktree) {
6741
7059
  warnings.push(tr(lang, "warnings", "workflowWorktreeRequired"));
6742
7060
  }
6743
- const relativeFeaturePathFromDocs = path12.relative(
7061
+ const relativeFeaturePathFromDocs = path13.relative(
6744
7062
  context.docsDir,
6745
7063
  featurePath
6746
7064
  );
@@ -7103,7 +7421,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7103
7421
  async function listFeatureDirs(ctx, rootDir) {
7104
7422
  const dirs = await listSubdirectories(ctx.fs, rootDir);
7105
7423
  return dirs.filter(
7106
- (value) => path12.basename(value).trim().toLowerCase() !== "feature-base"
7424
+ (value) => path13.basename(value).trim().toLowerCase() !== "feature-base"
7107
7425
  );
7108
7426
  }
7109
7427
  function normalizeRelPath(value) {
@@ -7244,7 +7562,7 @@ async function scanFeatures(ctx) {
7244
7562
  if (config.projectType === "single") {
7245
7563
  const featureDirs = await listFeatureDirs(
7246
7564
  ctx,
7247
- path12.join(config.docsDir, "features")
7565
+ path13.join(config.docsDir, "features")
7248
7566
  );
7249
7567
  componentFeatureDirs.set("single", featureDirs);
7250
7568
  allFeatureDirs.push(...featureDirs);
@@ -7256,14 +7574,14 @@ async function scanFeatures(ctx) {
7256
7574
  for (const component of components) {
7257
7575
  const componentDirs = await listFeatureDirs(
7258
7576
  ctx,
7259
- path12.join(config.docsDir, "features", component)
7577
+ path13.join(config.docsDir, "features", component)
7260
7578
  );
7261
7579
  componentFeatureDirs.set(component, componentDirs);
7262
7580
  allFeatureDirs.push(...componentDirs);
7263
7581
  }
7264
7582
  }
7265
7583
  const relativeFeaturePaths = allFeatureDirs.map(
7266
- (dir) => normalizeRelPath(path12.relative(config.docsDir, dir))
7584
+ (dir) => normalizeRelPath(path13.relative(config.docsDir, dir))
7267
7585
  );
7268
7586
  const docsGitMeta = buildDocsFeatureGitMeta(
7269
7587
  ctx,
@@ -7280,7 +7598,7 @@ async function scanFeatures(ctx) {
7280
7598
  const parsed = await Promise.all(
7281
7599
  target.dirs.map(async (dir) => {
7282
7600
  const relativeFeaturePathFromDocs = normalizeRelPath(
7283
- path12.relative(config.docsDir, dir)
7601
+ path13.relative(config.docsDir, dir)
7284
7602
  );
7285
7603
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
7286
7604
  return parseFeature(
@@ -7332,8 +7650,8 @@ function statusCommand(program2) {
7332
7650
  const cliError = toCliError(error);
7333
7651
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7334
7652
  console.error(
7335
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7336
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
7653
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
7654
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7337
7655
  );
7338
7656
  printCliErrorSuggestions(suggestions, lang);
7339
7657
  process.exitCode = 1;
@@ -7350,13 +7668,13 @@ async function runStatus(options) {
7350
7668
  );
7351
7669
  }
7352
7670
  const { docsDir, projectType, projectName, lang } = ctx.config;
7353
- const featuresDir = path12.join(docsDir, "features");
7671
+ const featuresDir = path13.join(docsDir, "features");
7354
7672
  const scan = await scanFeatures(ctx);
7355
7673
  const features = [];
7356
7674
  const idMap = /* @__PURE__ */ new Map();
7357
7675
  for (const f of scan.features) {
7358
7676
  const id = f.id || "UNKNOWN";
7359
- const relPath = path12.relative(docsDir, f.path);
7677
+ const relPath = path13.relative(docsDir, f.path);
7360
7678
  if (!idMap.has(id)) idMap.set(id, []);
7361
7679
  idMap.get(id).push(relPath);
7362
7680
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -7430,7 +7748,7 @@ async function runStatus(options) {
7430
7748
  return;
7431
7749
  }
7432
7750
  if (features.length === 0) {
7433
- console.log(chalk8.yellow(tr(lang, "cli", "status.noFeatures")));
7751
+ console.log(chalk9.yellow(tr(lang, "cli", "status.noFeatures")));
7434
7752
  return;
7435
7753
  }
7436
7754
  features.sort((a, b) => a.id.localeCompare(b.id));
@@ -7440,14 +7758,14 @@ async function runStatus(options) {
7440
7758
  console.log(header);
7441
7759
  console.log(separator);
7442
7760
  for (const f of features) {
7443
- const statusColor = f.status === "WORKFLOW_DONE" ? chalk8.green : f.status === "DONE" ? chalk8.cyan : f.status === "DOING" ? chalk8.yellow : chalk8.gray;
7761
+ const statusColor = f.status === "WORKFLOW_DONE" ? chalk9.green : f.status === "DONE" ? chalk9.cyan : f.status === "DOING" ? chalk9.yellow : chalk9.gray;
7444
7762
  console.log(
7445
7763
  `| ${f.id} | ${f.name} | ${f.repo} | ${f.issue} | ${statusColor(f.status)} | ${f.progress} | ${f.step} | ${f.substate} | ${f.path} |`
7446
7764
  );
7447
7765
  }
7448
7766
  console.log();
7449
7767
  if (options.write) {
7450
- const outputPath = path12.join(featuresDir, "status.md");
7768
+ const outputPath = path13.join(featuresDir, "status.md");
7451
7769
  const date = getLocalDateString();
7452
7770
  const content = [
7453
7771
  "# Feature Status",
@@ -7464,22 +7782,22 @@ async function runStatus(options) {
7464
7782
  ].join("\n");
7465
7783
  await ctx.fs.writeFile(outputPath, content, "utf-8");
7466
7784
  console.log(
7467
- chalk8.green(tr(lang, "cli", "status.wrote", { path: outputPath }))
7785
+ chalk9.green(tr(lang, "cli", "status.wrote", { path: outputPath }))
7468
7786
  );
7469
7787
  }
7470
7788
  }
7471
- function escapeRegExp2(value) {
7789
+ function escapeRegExp3(value) {
7472
7790
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7473
7791
  }
7474
7792
  async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallbackFolderName) {
7475
7793
  try {
7476
- const specPath = path12.join(featureDir, "spec.md");
7794
+ const specPath = path13.join(featureDir, "spec.md");
7477
7795
  if (!await fsAdapter.pathExists(specPath)) return fallbackSlug;
7478
7796
  const content = await fsAdapter.readFile(specPath, "utf-8");
7479
7797
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
7480
7798
  for (const key of keys) {
7481
7799
  const regex = new RegExp(
7482
- `^\\s*-\\s*\\*\\*${escapeRegExp2(key)}\\*\\*\\s*:\\s*(.*)$`,
7800
+ `^\\s*-\\s*\\*\\*${escapeRegExp3(key)}\\*\\*\\s*:\\s*(.*)$`,
7483
7801
  "m"
7484
7802
  );
7485
7803
  const match = content.match(regex);
@@ -7491,7 +7809,7 @@ async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallb
7491
7809
  return fallbackSlug || fallbackFolderName;
7492
7810
  }
7493
7811
  function updateCommand(program2) {
7494
- program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "Cleanup legacy agents/skills copies (CLI-managed)").option("--templates", "Cleanup legacy feature-base copies (CLI-managed)").option(
7812
+ program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--agents-md", "Sync project-scoped AGENTS.md entrypoint").option("--skills", "Cleanup legacy agents/skills copies (CLI-managed)").option("--templates", "Cleanup legacy feature-base copies (CLI-managed)").option(
7495
7813
  "-f, --force",
7496
7814
  "Force overwrite even if docs has uncommitted changes"
7497
7815
  ).action(async (options) => {
@@ -7501,15 +7819,15 @@ function updateCommand(program2) {
7501
7819
  const config = await getConfig(process.cwd());
7502
7820
  const lang = config?.lang ?? DEFAULT_LANG;
7503
7821
  if (error instanceof Error && error.message === "canceled") {
7504
- console.log(chalk8.yellow(`
7822
+ console.log(chalk9.yellow(`
7505
7823
  ${tr(lang, "cli", "common.canceled")}`));
7506
7824
  return;
7507
7825
  }
7508
7826
  const cliError = toCliError(error);
7509
7827
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7510
7828
  console.error(
7511
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7512
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
7829
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
7830
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7513
7831
  );
7514
7832
  printCliErrorSuggestions(suggestions, lang);
7515
7833
  process.exitCode = 1;
@@ -7534,30 +7852,31 @@ async function runUpdate(options) {
7534
7852
  const docsLockPath = getDocsLockPath(docsDir);
7535
7853
  const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang, [docsLockPath]);
7536
7854
  const configBackfill = await backfillMissingConfigDefaults(docsDir);
7537
- const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
7855
+ const hasExplicitSelection = !!(options.agents || options.agentsMd || options.skills || options.templates);
7538
7856
  const updateAgents = options.agents || options.skills || !hasExplicitSelection;
7857
+ const updateAgentsMd = options.agentsMd || !hasExplicitSelection;
7539
7858
  const updateTemplates = options.templates || !hasExplicitSelection;
7540
7859
  const agentsMode = options.skills && !options.agents ? "skills" : "all";
7541
- console.log(chalk8.blue(tr(lang, "cli", "update.start")));
7542
- console.log(chalk8.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
7860
+ console.log(chalk9.blue(tr(lang, "cli", "update.start")));
7861
+ console.log(chalk9.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
7543
7862
  console.log(
7544
- chalk8.gray(` - ${tr(lang, "cli", "update.typeLabel")}: ${projectType}`)
7863
+ chalk9.gray(` - ${tr(lang, "cli", "update.typeLabel")}: ${projectType}`)
7545
7864
  );
7546
7865
  console.log();
7547
7866
  let updatedCount = 0;
7548
7867
  if (updateAgents) {
7549
7868
  if (agentsMode === "skills") {
7550
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingSkills")));
7869
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingSkills")));
7551
7870
  console.log(
7552
- chalk8.gray(tr(lang, "cli", "update.engineManagedSkillsBuiltin"))
7871
+ chalk9.gray(tr(lang, "cli", "update.engineManagedSkillsBuiltin"))
7553
7872
  );
7554
- console.log(chalk8.green(` \u2705 ${tr(lang, "cli", "update.skillsUpdated")}`));
7873
+ console.log(chalk9.green(` \u2705 ${tr(lang, "cli", "update.skillsUpdated")}`));
7555
7874
  } else {
7556
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingAgents")));
7875
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingAgents")));
7557
7876
  }
7558
7877
  if (agentsMode === "all") {
7559
- const commonAgentsBase = path12.join(templatesDir, lang, "common", "agents");
7560
- const targetAgentsBase = path12.join(docsDir, "agents");
7878
+ const commonAgentsBase = path13.join(templatesDir, lang, "common", "agents");
7879
+ const targetAgentsBase = path13.join(docsDir, "agents");
7561
7880
  const commonAgents = commonAgentsBase;
7562
7881
  const targetAgents = targetAgentsBase;
7563
7882
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
@@ -7585,20 +7904,32 @@ async function runUpdate(options) {
7585
7904
  updatedCount += count;
7586
7905
  }
7587
7906
  console.log(
7588
- chalk8.green(
7907
+ chalk9.green(
7589
7908
  ` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`
7590
7909
  )
7591
7910
  );
7592
7911
  }
7593
7912
  }
7913
+ if (updateAgentsMd) {
7914
+ const agentsMdTargets = await collectAgentsMdTargets(cwd, config);
7915
+ for (const target of agentsMdTargets) {
7916
+ const result = await upsertLeeSpecKitAgentsMd(target, {
7917
+ lang,
7918
+ docsRepo: config.docsRepo ?? "embedded"
7919
+ });
7920
+ if (result.changed) {
7921
+ updatedCount += 1;
7922
+ }
7923
+ }
7924
+ }
7594
7925
  if (updateTemplates) {
7595
- console.log(chalk8.blue(tr(lang, "cli", "update.updatingFeatureBase")));
7596
- console.log(chalk8.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
7926
+ console.log(chalk9.blue(tr(lang, "cli", "update.updatingFeatureBase")));
7927
+ console.log(chalk9.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
7597
7928
  }
7598
7929
  const pruned = await pruneEngineManagedDocs(docsDir);
7599
7930
  if (pruned.length > 0) {
7600
7931
  console.log(
7601
- chalk8.gray(
7932
+ chalk9.gray(
7602
7933
  ` - ${tr(lang, "cli", "update.engineManagedPruned", {
7603
7934
  count: pruned.length
7604
7935
  })}`
@@ -7608,23 +7939,59 @@ async function runUpdate(options) {
7608
7939
  console.log();
7609
7940
  if (configBackfill.changed) {
7610
7941
  console.log(
7611
- chalk8.gray(
7942
+ chalk9.gray(
7612
7943
  ` - ${tr(lang, "cli", "update.fileUpdated", { file: ".lee-spec-kit.json" })}`
7613
7944
  )
7614
7945
  );
7615
7946
  console.log(
7616
- chalk8.gray(
7947
+ chalk9.gray(
7617
7948
  ` (${configBackfill.changedPaths.join(", ")})`
7618
7949
  )
7619
7950
  );
7620
7951
  }
7621
7952
  console.log(
7622
- chalk8.green(`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`)
7953
+ chalk9.green(`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`)
7623
7954
  );
7624
7955
  },
7625
7956
  { owner: "update" }
7626
7957
  );
7627
7958
  }
7959
+ function getGitTopLevelOrNull2(cwd) {
7960
+ try {
7961
+ const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
7962
+ cwd,
7963
+ encoding: "utf-8",
7964
+ stdio: ["ignore", "pipe", "ignore"]
7965
+ });
7966
+ const value = String(out || "").trim();
7967
+ return value ? value : null;
7968
+ } catch {
7969
+ return null;
7970
+ }
7971
+ }
7972
+ async function collectAgentsMdTargets(cwd, config) {
7973
+ if (!config) return [];
7974
+ const targets = /* @__PURE__ */ new Set();
7975
+ const docsRepo = config.docsRepo ?? "embedded";
7976
+ if (docsRepo === "embedded") {
7977
+ const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path13.resolve(config.docsDir, "..");
7978
+ targets.add(path13.join(repoRoot, "AGENTS.md"));
7979
+ return [...targets];
7980
+ }
7981
+ targets.add(path13.join(config.docsDir, "AGENTS.md"));
7982
+ const baseDir = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || process.cwd();
7983
+ const rawRoots = typeof config.projectRoot === "string" ? [config.projectRoot] : config.projectRoot && typeof config.projectRoot === "object" ? Object.values(config.projectRoot) : [];
7984
+ for (const rawRoot of rawRoots) {
7985
+ const value = String(rawRoot || "").trim();
7986
+ if (!value) continue;
7987
+ const resolved = path13.resolve(baseDir, value);
7988
+ if (!await fs.pathExists(resolved)) continue;
7989
+ const stat = await fs.stat(resolved);
7990
+ if (!stat.isDirectory()) continue;
7991
+ targets.add(path13.join(resolved, "AGENTS.md"));
7992
+ }
7993
+ return [...targets];
7994
+ }
7628
7995
  function isPlainObject(value) {
7629
7996
  return !!value && typeof value === "object" && !Array.isArray(value);
7630
7997
  }
@@ -7661,7 +8028,7 @@ function normalizeDecisionEnumList2(raw) {
7661
8028
  return [...deduped];
7662
8029
  }
7663
8030
  async function backfillMissingConfigDefaults(docsDir) {
7664
- const configPath = path12.join(docsDir, ".lee-spec-kit.json");
8031
+ const configPath = path13.join(docsDir, ".lee-spec-kit.json");
7665
8032
  if (!await fs.pathExists(configPath)) {
7666
8033
  return { changed: false, changedPaths: [] };
7667
8034
  }
@@ -7784,8 +8151,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7784
8151
  const files = await fs.readdir(sourceDir);
7785
8152
  let updatedCount = 0;
7786
8153
  for (const file of files) {
7787
- const sourcePath = path12.join(sourceDir, file);
7788
- const targetPath = path12.join(targetDir, file);
8154
+ const sourcePath = path13.join(sourceDir, file);
8155
+ const targetPath = path13.join(targetDir, file);
7789
8156
  const stat = await fs.stat(sourcePath);
7790
8157
  if (stat.isFile()) {
7791
8158
  if (protectedFiles.has(file)) {
@@ -7803,7 +8170,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7803
8170
  }
7804
8171
  if (!force) {
7805
8172
  console.log(
7806
- chalk8.yellow(
8173
+ chalk9.yellow(
7807
8174
  ` \u26A0\uFE0F ${file} - ${tr(lang, "cli", "update.changeDetected")}`
7808
8175
  )
7809
8176
  );
@@ -7813,7 +8180,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
7813
8180
  if (shouldUpdate) {
7814
8181
  await fs.writeFile(targetPath, sourceContent);
7815
8182
  console.log(
7816
- chalk8.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
8183
+ chalk9.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
7817
8184
  );
7818
8185
  updatedCount++;
7819
8186
  }
@@ -7868,7 +8235,7 @@ function extractPorcelainPaths(line) {
7868
8235
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
7869
8236
  const top = getGitTopLevel2(docsDir);
7870
8237
  if (!top) return null;
7871
- const rel = path12.relative(top, docsDir) || ".";
8238
+ const rel = path13.relative(top, docsDir) || ".";
7872
8239
  try {
7873
8240
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
7874
8241
  cwd: top,
@@ -7880,7 +8247,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
7880
8247
  }
7881
8248
  const ignoredRelPaths = new Set(
7882
8249
  ignoredAbsPaths.map(
7883
- (absPath) => normalizeGitPath2(path12.relative(top, absPath) || ".")
8250
+ (absPath) => normalizeGitPath2(path13.relative(top, absPath) || ".")
7884
8251
  )
7885
8252
  );
7886
8253
  const filtered = output.split("\n").filter((line) => {
@@ -7918,7 +8285,7 @@ function configCommand(program2) {
7918
8285
  if (error instanceof Error && error.message === "canceled") {
7919
8286
  const config2 = await getConfig(process.cwd());
7920
8287
  const lang2 = config2?.lang ?? DEFAULT_LANG;
7921
- console.log(chalk8.yellow(`
8288
+ console.log(chalk9.yellow(`
7922
8289
  ${tr(lang2, "cli", "common.canceled")}`));
7923
8290
  return;
7924
8291
  }
@@ -7927,8 +8294,8 @@ ${tr(lang2, "cli", "common.canceled")}`));
7927
8294
  const cliError = toCliError(error);
7928
8295
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
7929
8296
  console.error(
7930
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
7931
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
8297
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
8298
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
7932
8299
  );
7933
8300
  printCliErrorSuggestions(suggestions, lang);
7934
8301
  process.exitCode = 1;
@@ -7938,7 +8305,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
7938
8305
  }
7939
8306
  async function runConfig(options) {
7940
8307
  const cwd = process.cwd();
7941
- const targetCwd = options.dir ? path12.resolve(cwd, options.dir) : cwd;
8308
+ const targetCwd = options.dir ? path13.resolve(cwd, options.dir) : cwd;
7942
8309
  const config = await getConfig(targetCwd);
7943
8310
  if (!config) {
7944
8311
  throw createCliError(
@@ -7946,13 +8313,13 @@ async function runConfig(options) {
7946
8313
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
7947
8314
  );
7948
8315
  }
7949
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
8316
+ const configPath = path13.join(config.docsDir, ".lee-spec-kit.json");
7950
8317
  if (!options.projectRoot) {
7951
8318
  console.log();
7952
- console.log(chalk8.blue(tr(config.lang, "cli", "config.currentTitle")));
8319
+ console.log(chalk9.blue(tr(config.lang, "cli", "config.currentTitle")));
7953
8320
  console.log();
7954
8321
  console.log(
7955
- chalk8.gray(
8322
+ chalk9.gray(
7956
8323
  ` ${tr(config.lang, "cli", "config.pathLabel")}: ${configPath}`
7957
8324
  )
7958
8325
  );
@@ -7969,7 +8336,7 @@ async function runConfig(options) {
7969
8336
  const configFile = await fs.readJson(configPath);
7970
8337
  if (configFile.docsRepo !== "standalone") {
7971
8338
  console.log(
7972
- chalk8.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
8339
+ chalk9.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
7973
8340
  );
7974
8341
  return;
7975
8342
  }
@@ -8019,7 +8386,7 @@ async function runConfig(options) {
8019
8386
  currentRoot[targetComponent] = projectRoot;
8020
8387
  configFile.projectRoot = currentRoot;
8021
8388
  console.log(
8022
- chalk8.green(
8389
+ chalk9.green(
8023
8390
  tr(config.lang, "cli", "config.projectRootSet", {
8024
8391
  repo: targetComponent.toUpperCase(),
8025
8392
  path: projectRoot
@@ -8035,7 +8402,7 @@ async function runConfig(options) {
8035
8402
  }
8036
8403
  configFile.projectRoot = projectRoot;
8037
8404
  console.log(
8038
- chalk8.green(
8405
+ chalk9.green(
8039
8406
  tr(config.lang, "cli", "config.projectRootSetSingle", {
8040
8407
  path: projectRoot
8041
8408
  })
@@ -8052,47 +8419,47 @@ var BUILTIN_DOC_DEFINITIONS = [
8052
8419
  {
8053
8420
  id: "agents",
8054
8421
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
8055
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "agents.md")
8422
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "agents.md")
8056
8423
  },
8057
8424
  {
8058
8425
  id: "git-workflow",
8059
8426
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
8060
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "git-workflow.md")
8427
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "git-workflow.md")
8061
8428
  },
8062
8429
  {
8063
8430
  id: "issue-doc",
8064
8431
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
8065
- relativePath: (_, lang) => path12.join(lang, "common", "features", "feature-base", "issue.md")
8432
+ relativePath: (_, lang) => path13.join(lang, "common", "features", "feature-base", "issue.md")
8066
8433
  },
8067
8434
  {
8068
8435
  id: "pr-doc",
8069
8436
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
8070
- relativePath: (_, lang) => path12.join(lang, "common", "features", "feature-base", "pr.md")
8437
+ relativePath: (_, lang) => path13.join(lang, "common", "features", "feature-base", "pr.md")
8071
8438
  },
8072
8439
  {
8073
8440
  id: "create-feature",
8074
8441
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
8075
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-feature.md")
8442
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "skills", "create-feature.md")
8076
8443
  },
8077
8444
  {
8078
8445
  id: "execute-task",
8079
8446
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
8080
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "execute-task.md")
8447
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "skills", "execute-task.md")
8081
8448
  },
8082
8449
  {
8083
8450
  id: "create-issue",
8084
8451
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
8085
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-issue.md")
8452
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "skills", "create-issue.md")
8086
8453
  },
8087
8454
  {
8088
8455
  id: "create-pr",
8089
8456
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
8090
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "create-pr.md")
8457
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "skills", "create-pr.md")
8091
8458
  },
8092
8459
  {
8093
8460
  id: "split-feature",
8094
8461
  title: { ko: "feature \uBD84\uD560 \uAC00\uC774\uB4DC", en: "feature split guide" },
8095
- relativePath: (_, lang) => path12.join(lang, "common", "agents", "skills", "split-feature.md")
8462
+ relativePath: (_, lang) => path13.join(lang, "common", "agents", "skills", "split-feature.md")
8096
8463
  }
8097
8464
  ];
8098
8465
  var DOC_FOLLOWUPS = {
@@ -8189,7 +8556,7 @@ function listBuiltinDocs(projectType, lang) {
8189
8556
  id: doc.id,
8190
8557
  title: doc.title[lang],
8191
8558
  relativePath,
8192
- absolutePath: path12.join(templatesDir, relativePath)
8559
+ absolutePath: path13.join(templatesDir, relativePath)
8193
8560
  };
8194
8561
  });
8195
8562
  }
@@ -8610,7 +8977,7 @@ function getActionExecutionMetadata(action) {
8610
8977
  return {
8611
8978
  handoffOnly: true,
8612
8979
  advancesWorkflow: false,
8613
- nextMainState: "pre_pr_review_running"
8980
+ nextMainState: "pre_pr_review_in_progress"
8614
8981
  };
8615
8982
  }
8616
8983
  return null;
@@ -8749,6 +9116,73 @@ function buildApprovalCommand(state, featureName, selectedComponent, execute) {
8749
9116
  }
8750
9117
  return `npx lee-spec-kit context ${featureRef}${componentArg} --approve <LABEL>`;
8751
9118
  }
9119
+ function buildFeatureComponentArgs(feature) {
9120
+ return feature.type && feature.type !== "single" ? ["--component", feature.type] : [];
9121
+ }
9122
+ function buildPrePrRecordCommand(feature, decision) {
9123
+ const args = [
9124
+ "pre-pr-review",
9125
+ feature.folderName,
9126
+ ...buildFeatureComponentArgs(feature),
9127
+ "--evidence",
9128
+ "review-trace.json",
9129
+ "--decision",
9130
+ decision
9131
+ ];
9132
+ return `npx lee-spec-kit ${args.join(" ")}`;
9133
+ }
9134
+ function buildDelegatedActionContract(state) {
9135
+ const feature = state.matchedFeature;
9136
+ const substateId = feature?.currentSubstateId;
9137
+ if (!feature || !substateId) return null;
9138
+ if (substateId === "pre_pr_review_run" || substateId === "pre_pr_review_in_progress") {
9139
+ return {
9140
+ required: true,
9141
+ mode: "command",
9142
+ category: "pre_pr_review_run",
9143
+ currentSubstateId: substateId,
9144
+ delegatedWorkRequired: true,
9145
+ handoffOnly: true,
9146
+ advancesWorkflow: false,
9147
+ doNotReapproveSameLabel: substateId === "pre_pr_review_in_progress",
9148
+ nextMainState: "pre_pr_review_in_progress",
9149
+ reuseKey: `pre-pr:${feature.folderName}`,
9150
+ evidenceFile: "review-trace.json",
9151
+ nextStepRequirement: "generate_review_trace_then_record",
9152
+ recordCommands: {
9153
+ changesRequested: buildPrePrRecordCommand(
9154
+ feature,
9155
+ "changes_requested"
9156
+ ),
9157
+ approve: buildPrePrRecordCommand(feature, "approve")
9158
+ },
9159
+ 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."
9160
+ };
9161
+ }
9162
+ if (substateId === "code_review_run" || substateId === "code_review_running") {
9163
+ return {
9164
+ required: true,
9165
+ mode: "command",
9166
+ category: "code_review_run",
9167
+ currentSubstateId: substateId,
9168
+ delegatedWorkRequired: true,
9169
+ handoffOnly: true,
9170
+ advancesWorkflow: false,
9171
+ doNotReapproveSameLabel: substateId === "code_review_running",
9172
+ nextMainState: "code_review_running",
9173
+ reuseKey: `code-review:${feature.folderName}`,
9174
+ guidance: substateId === "code_review_running" ? "A PR review handoff is already prepared. Reuse or resume the delegated review-fix work, then refresh PR Review Evidence and PR Review Decision before continuing. Do not re-approve the same label." : "After approval, spawn_agent first and hand off the delegated PR review-fix work. Handoff-only execution only prepares the delegated session; continue the delegated work immediately after approval."
9175
+ };
9176
+ }
9177
+ return null;
9178
+ }
9179
+ function buildDelegatedApprovalGuidance(handoffRequired, handoffMode) {
9180
+ const base = "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`).";
9181
+ const delegatedCommand = handoffRequired && handoffMode === "command" ? ' When `matchedFeature.currentSubstateOwner="subagent"` and `agentOrchestration.subAgentHandoff.required=true` with `mode="command"`, call spawn_agent first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, `--execute` only prepares the handoff; continue the delegated work immediately and do not re-approve the same label.' : "";
9182
+ const nonDelegated = " For non-delegated command actions, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed.";
9183
+ const orchestration = ' Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.';
9184
+ return `${base}${delegatedCommand}${nonDelegated}${orchestration}`;
9185
+ }
8752
9186
  function buildFinalApprovalPrompt(lang, actionOptions) {
8753
9187
  if (actionOptions.length === 0) return "";
8754
9188
  const labels = listLabels(actionOptions);
@@ -8954,12 +9388,12 @@ function printSuggestionOptions(lang, suggestionOptions) {
8954
9388
  if (suggestionOptions.length === 0) return;
8955
9389
  const finalPrompt = buildSuggestionFinalPrompt(lang, suggestionOptions);
8956
9390
  console.log(
8957
- chalk8.green(chalk8.bold(`\u{1F449} ${tr(lang, "cli", "context.suggestionHeader")}`))
9391
+ chalk9.green(chalk9.bold(`\u{1F449} ${tr(lang, "cli", "context.suggestionHeader")}`))
8958
9392
  );
8959
9393
  suggestionOptions.forEach((option) => {
8960
9394
  console.log(` ${option.label}: ${option.summary}`);
8961
9395
  console.log(
8962
- chalk8.gray(
9396
+ chalk9.gray(
8963
9397
  ` \u21B3 ${tr(lang, "cli", "context.suggestionCommandHint", {
8964
9398
  command: option.command
8965
9399
  })}`
@@ -8967,7 +9401,7 @@ function printSuggestionOptions(lang, suggestionOptions) {
8967
9401
  );
8968
9402
  });
8969
9403
  if (finalPrompt) {
8970
- console.log(chalk8.cyan(` \u21B3 ${finalPrompt}`));
9404
+ console.log(chalk9.cyan(` \u21B3 ${finalPrompt}`));
8971
9405
  }
8972
9406
  }
8973
9407
  function buildRequiredDocHints(actionOptions) {
@@ -9159,7 +9593,7 @@ function getApprovalSessionId() {
9159
9593
  function getApprovalTicketPaths(config) {
9160
9594
  return {
9161
9595
  runtimePath: getApprovalTicketStorePath(config.docsDir),
9162
- legacyPath: path12.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9596
+ legacyPath: path13.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9163
9597
  };
9164
9598
  }
9165
9599
  async function loadApprovalTicketStore(storePath) {
@@ -9173,7 +9607,7 @@ async function loadApprovalTicketStore(storePath) {
9173
9607
  }
9174
9608
  }
9175
9609
  async function saveApprovalTicketStore(storePath, payload) {
9176
- await fs.ensureDir(path12.dirname(storePath));
9610
+ await fs.ensureDir(path13.dirname(storePath));
9177
9611
  await fs.writeJson(storePath, payload, { spaces: 2 });
9178
9612
  }
9179
9613
  function pruneApprovalTickets(tickets, nowMs) {
@@ -9358,6 +9792,28 @@ function getCommandExecutionLockPath(action, config) {
9358
9792
  }
9359
9793
  return getProjectExecutionLockPath(action.cwd);
9360
9794
  }
9795
+ function buildApprovedHandoffMetadata(action, featureRef) {
9796
+ if (action.category === "pre_pr_review_run") {
9797
+ return {
9798
+ delegatedWorkRequired: true,
9799
+ doNotReapproveSameLabel: true,
9800
+ reuseKey: `pre-pr:${featureRef}`,
9801
+ nextStepRequirement: "generate_review_trace_then_record",
9802
+ evidenceFile: "review-trace.json"
9803
+ };
9804
+ }
9805
+ if (action.category === "code_review_run") {
9806
+ return {
9807
+ delegatedWorkRequired: true,
9808
+ doNotReapproveSameLabel: true,
9809
+ reuseKey: `code-review:${featureRef}`
9810
+ };
9811
+ }
9812
+ return {
9813
+ delegatedWorkRequired: true,
9814
+ doNotReapproveSameLabel: true
9815
+ };
9816
+ }
9361
9817
  async function runApprovedOption(state, config, lang, featureName, selectionOptions, options) {
9362
9818
  const approval = options.approve || "";
9363
9819
  const ticketToken = (options.ticket || "").trim();
@@ -9482,10 +9938,10 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9482
9938
  return;
9483
9939
  }
9484
9940
  console.log();
9485
- console.log(chalk8.green(`\u2705 Approved option: ${parsedLabel}`));
9486
- console.log(chalk8.gray(` - Action: ${freshSelected.detail}`));
9941
+ console.log(chalk9.green(`\u2705 Approved option: ${parsedLabel}`));
9942
+ console.log(chalk9.gray(` - Action: ${freshSelected.detail}`));
9487
9943
  if (userRequest) {
9488
- console.log(chalk8.gray(` - User request: ${userRequest}`));
9944
+ console.log(chalk9.gray(` - User request: ${userRequest}`));
9489
9945
  }
9490
9946
  if (selectedAction.type === "command") {
9491
9947
  const selectedComponent = selectionOptions.component || "";
@@ -9496,24 +9952,24 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9496
9952
  `--ticket ${ticket.token}`
9497
9953
  );
9498
9954
  console.log(
9499
- chalk8.gray(
9955
+ chalk9.gray(
9500
9956
  ` - Ticket: ${ticket.token} (expires: ${ticket.expiresAt})`
9501
9957
  )
9502
9958
  );
9503
9959
  } else {
9504
9960
  executeCommand = executeCommand.replace(" [--ticket <TICKET>]", "");
9505
9961
  }
9506
- console.log(chalk8.gray(` - Run with: ${executeCommand}`));
9962
+ console.log(chalk9.gray(` - Run with: ${executeCommand}`));
9507
9963
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9508
9964
  console.log(
9509
- chalk8.gray(
9965
+ chalk9.gray(
9510
9966
  " - This command prepares a handoff only; complete the delegated work and update workflow evidence before re-running context."
9511
9967
  )
9512
9968
  );
9513
9969
  }
9514
9970
  } else {
9515
9971
  console.log(
9516
- chalk8.gray(" - Instruction-only action (no command execution).")
9972
+ chalk9.gray(" - Instruction-only action (no command execution).")
9517
9973
  );
9518
9974
  }
9519
9975
  console.log();
@@ -9564,19 +10020,19 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9564
10020
  }
9565
10021
  console.log();
9566
10022
  console.log(
9567
- chalk8.yellow(`\u26A0\uFE0F Approved label ${parsedLabel} is instruction-only.`)
10023
+ chalk9.yellow(`\u26A0\uFE0F Approved label ${parsedLabel} is instruction-only.`)
9568
10024
  );
9569
10025
  if (userRequest) {
9570
- console.log(chalk8.gray(` User request: ${userRequest}`));
10026
+ console.log(chalk9.gray(` User request: ${userRequest}`));
9571
10027
  }
9572
- console.log(chalk8.gray(` ${selectedAction.message}`));
10028
+ console.log(chalk9.gray(` ${selectedAction.message}`));
9573
10029
  console.log();
9574
10030
  return;
9575
10031
  }
9576
10032
  if (!jsonMode) {
9577
10033
  console.log();
9578
- console.log(chalk8.blue(`\u25B6 Executing option ${parsedLabel}...`));
9579
- console.log(chalk8.gray(` ${selectedAction.cmd}`));
10034
+ console.log(chalk9.blue(`\u25B6 Executing option ${parsedLabel}...`));
10035
+ console.log(chalk9.gray(` ${selectedAction.cmd}`));
9580
10036
  console.log();
9581
10037
  }
9582
10038
  try {
@@ -9588,6 +10044,10 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9588
10044
  );
9589
10045
  if (jsonMode) {
9590
10046
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
10047
+ const handoffMetadata = buildApprovedHandoffMetadata(
10048
+ selectedAction,
10049
+ freshState.matchedFeature?.folderName ?? featureRef
10050
+ );
9591
10051
  console.log(
9592
10052
  JSON.stringify(
9593
10053
  {
@@ -9602,6 +10062,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9602
10062
  handoffOnly: true,
9603
10063
  advancesWorkflow: false,
9604
10064
  nextMainState: executionMetadata.nextMainState,
10065
+ ...handoffMetadata,
9605
10066
  stdout: execResult.stdout?.trim() || void 0,
9606
10067
  stderr: execResult.stderr?.trim() || void 0
9607
10068
  },
@@ -9633,14 +10094,14 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9633
10094
  }
9634
10095
  if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9635
10096
  console.log(
9636
- chalk8.yellow(
10097
+ chalk9.yellow(
9637
10098
  "Prepared handoff only. Complete the delegated work and update workflow evidence before re-running context."
9638
10099
  )
9639
10100
  );
9640
10101
  console.log();
9641
10102
  return;
9642
10103
  }
9643
- console.log(chalk8.green(`\u2705 Executed option ${parsedLabel}.`));
10104
+ console.log(chalk9.green(`\u2705 Executed option ${parsedLabel}.`));
9644
10105
  console.log();
9645
10106
  } catch (error) {
9646
10107
  const message = error instanceof Error ? error.message : String(error);
@@ -9688,8 +10149,8 @@ function contextCommand(program2) {
9688
10149
  );
9689
10150
  } else {
9690
10151
  console.error(
9691
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
9692
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
10152
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
10153
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
9693
10154
  );
9694
10155
  printCliErrorSuggestions(suggestions, lang);
9695
10156
  }
@@ -9782,6 +10243,11 @@ async function runContext(featureName, options) {
9782
10243
  state.matchedFeature?.folderName || null,
9783
10244
  state.matchedFeature?.currentSubstateOwner
9784
10245
  );
10246
+ const delegatedAction = buildDelegatedActionContract(state);
10247
+ const approvalGuidance = buildDelegatedApprovalGuidance(
10248
+ agentOrchestration.subAgentHandoff.required,
10249
+ agentOrchestration.subAgentHandoff.mode
10250
+ );
9785
10251
  if (options.approve || options.execute) {
9786
10252
  await runApprovedOption(
9787
10253
  state,
@@ -9840,6 +10306,7 @@ async function runContext(featureName, options) {
9840
10306
  agentOrchestration: {
9841
10307
  subAgentHandoff: agentOrchestration.subAgentHandoff
9842
10308
  },
10309
+ delegatedAction,
9843
10310
  autoRun: {
9844
10311
  available: autoRunPlan.available,
9845
10312
  policyEligible: autoRunPlan.policyEligible,
@@ -9975,13 +10442,14 @@ async function runContext(featureName, options) {
9975
10442
  "actionOptions[].detail",
9976
10443
  "actionOptions[].approvalPrompt"
9977
10444
  ] : [],
9978
- recommendation: 'Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`). For command execution, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed. Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.',
10445
+ recommendation: approvalGuidance,
9979
10446
  oneApprovalPerAction: approvalRequired,
9980
10447
  requireFreshContext: true,
9981
10448
  contextVersion: state.contextVersion,
9982
10449
  config: config.approval ?? { mode: "builtin" }
9983
10450
  },
9984
10451
  agentOrchestration,
10452
+ delegatedAction,
9985
10453
  autoRun: {
9986
10454
  available: autoRunPlan.available,
9987
10455
  policyEligible: autoRunPlan.policyEligible,
@@ -9995,7 +10463,10 @@ async function runContext(featureName, options) {
9995
10463
  guidance: 'Use auto-run only when `autoRun.available=true`. If `autoRun.policyEligible=true` but `autoRun.executableNow=false`, resolve `autoRun.manualBoundary` first. Do not treat `autoRun.available` alone as a delegation trigger; use `agentOrchestration.subAgentHandoff.required` + `mode="auto_run"` for actual delegation. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories.'
9996
10464
  },
9997
10465
  approvalRequest: {
9998
- guidance: 'User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`. Do not expose `requiredDocs`, `checkPolicy`, or raw `cmd` unless explicitly requested. For approved command actions, prefer one-shot `flow --approve <LABEL> --execute`. Keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.',
10466
+ guidance: approvalGuidance.replace(
10467
+ "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user.",
10468
+ "User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`."
10469
+ ),
9999
10470
  required: approvalRequired,
10000
10471
  finalPrompt: finalApprovalPrompt,
10001
10472
  userFacingLines: approvalUserFacingLines,
@@ -10048,11 +10519,11 @@ async function runContext(featureName, options) {
10048
10519
  return;
10049
10520
  }
10050
10521
  console.log();
10051
- console.log(chalk8.bold(tr(lang, "cli", "context.header")));
10522
+ console.log(chalk9.bold(tr(lang, "cli", "context.header")));
10052
10523
  if (config.projectType === "single") {
10053
10524
  if (state.branches.project.single) {
10054
10525
  console.log(
10055
- chalk8.gray(
10526
+ chalk9.gray(
10056
10527
  ` (Detected from Project Branch: ${state.branches.project.single})`
10057
10528
  )
10058
10529
  );
@@ -10061,7 +10532,7 @@ async function runContext(featureName, options) {
10061
10532
  const branchName = state.branches.project[selectedComponent] || "";
10062
10533
  if (branchName) {
10063
10534
  console.log(
10064
- chalk8.gray(
10535
+ chalk9.gray(
10065
10536
  ` (Detected from Project Branch: ${selectedComponent.toUpperCase()} ${branchName})`
10066
10537
  )
10067
10538
  );
@@ -10070,37 +10541,37 @@ async function runContext(featureName, options) {
10070
10541
  const parts = Object.entries(state.branches.project).filter(([key, value]) => key !== "single" && !!value).map(([key, value]) => `${key.toUpperCase()} ${value}`);
10071
10542
  if (parts.length > 0) {
10072
10543
  console.log(
10073
- chalk8.gray(` (Detected from Project Branch: ${parts.join(" / ")})`)
10544
+ chalk9.gray(` (Detected from Project Branch: ${parts.join(" / ")})`)
10074
10545
  );
10075
10546
  }
10076
10547
  }
10077
10548
  if (config.docsRepo === "standalone" && state.branches.docs) {
10078
- console.log(chalk8.gray(` (Docs Branch: ${state.branches.docs})`));
10549
+ console.log(chalk9.gray(` (Docs Branch: ${state.branches.docs})`));
10079
10550
  }
10080
- 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"));
10551
+ 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"));
10081
10552
  console.log();
10082
10553
  if (state.features.length === 0) {
10083
- console.log(chalk8.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10554
+ console.log(chalk9.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10084
10555
  console.log();
10085
10556
  printSuggestionOptions(lang, suggestionOptions);
10086
10557
  console.log();
10087
10558
  return;
10088
10559
  }
10089
10560
  if (state.warnings.length > 0) {
10090
- console.log(chalk8.yellow(tr(lang, "cli", "context.envWarnings")));
10091
- state.warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
10561
+ console.log(chalk9.yellow(tr(lang, "cli", "context.envWarnings")));
10562
+ state.warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
10092
10563
  console.log();
10093
10564
  }
10094
10565
  if (state.targetFeatures.length === 0) {
10095
- console.log(chalk8.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10566
+ console.log(chalk9.yellow(tr(lang, "cli", "context.noActiveFeatures")));
10096
10567
  if (state.status === "no_open") {
10097
10568
  console.log(
10098
- chalk8.gray(
10569
+ chalk9.gray(
10099
10570
  ` $ npx lee-spec-kit context --done # ${tr(lang, "cli", "context.tipShowDone")}`
10100
10571
  )
10101
10572
  );
10102
10573
  console.log(
10103
- chalk8.gray(
10574
+ chalk9.gray(
10104
10575
  ` $ npx lee-spec-kit context --all # ${tr(lang, "cli", "context.tipShowAll")}`
10105
10576
  )
10106
10577
  );
@@ -10113,7 +10584,7 @@ async function runContext(featureName, options) {
10113
10584
  if (state.targetFeatures.length > 1) {
10114
10585
  if (state.selectionMode === "open") {
10115
10586
  console.log(
10116
- chalk8.gray(
10587
+ chalk9.gray(
10117
10588
  ` ${tr(lang, "cli", "context.openFallbackSummary", {
10118
10589
  inProgress: state.inProgressFeatures.length,
10119
10590
  readyToClose: state.readyToCloseFeatures.length,
@@ -10125,7 +10596,7 @@ async function runContext(featureName, options) {
10125
10596
  }
10126
10597
  if (state.selectionMode === "open") {
10127
10598
  console.log(
10128
- chalk8.blue(
10599
+ chalk9.blue(
10129
10600
  `\u{1F539} ${tr(lang, "cli", "context.sectionInProgress")} (${state.inProgressFeatures.length})`
10130
10601
  )
10131
10602
  );
@@ -10137,14 +10608,14 @@ async function runContext(featureName, options) {
10137
10608
  workflowPolicy,
10138
10609
  prePrReviewPolicy
10139
10610
  );
10140
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10611
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10141
10612
  console.log(
10142
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10613
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10143
10614
  );
10144
10615
  });
10145
10616
  console.log();
10146
10617
  console.log(
10147
- chalk8.blue(
10618
+ chalk9.blue(
10148
10619
  `\u{1F538} ${tr(lang, "cli", "context.sectionReadyToClose")} (${state.readyToCloseFeatures.length})`
10149
10620
  )
10150
10621
  );
@@ -10156,14 +10627,14 @@ async function runContext(featureName, options) {
10156
10627
  workflowPolicy,
10157
10628
  prePrReviewPolicy
10158
10629
  );
10159
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10630
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10160
10631
  console.log(
10161
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10632
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10162
10633
  );
10163
10634
  });
10164
10635
  } else {
10165
10636
  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:`;
10166
- console.log(chalk8.blue(title));
10637
+ console.log(chalk9.blue(title));
10167
10638
  console.log();
10168
10639
  state.targetFeatures.forEach((f2) => {
10169
10640
  const stepName2 = getListLabel(
@@ -10173,24 +10644,24 @@ async function runContext(featureName, options) {
10173
10644
  workflowPolicy,
10174
10645
  prePrReviewPolicy
10175
10646
  );
10176
- const typeStr = config.projectType === "multi" ? chalk8.cyan(`(${f2.type})`) : "";
10647
+ const typeStr = config.projectType === "multi" ? chalk9.cyan(`(${f2.type})`) : "";
10177
10648
  console.log(
10178
- ` \u2022 ${chalk8.bold(f2.folderName)} ${typeStr} - ${chalk8.yellow(stepName2)}`
10649
+ ` \u2022 ${chalk9.bold(f2.folderName)} ${typeStr} - ${chalk9.yellow(stepName2)}`
10179
10650
  );
10180
10651
  });
10181
10652
  }
10182
10653
  console.log();
10183
- console.log(chalk8.gray(tr(lang, "cli", "context.tipDetails")));
10654
+ console.log(chalk9.gray(tr(lang, "cli", "context.tipDetails")));
10184
10655
  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>";
10185
- console.log(chalk8.gray(selectorTip));
10656
+ console.log(chalk9.gray(selectorTip));
10186
10657
  if (state.selectionMode === "open") {
10187
10658
  console.log(
10188
- chalk8.gray(
10659
+ chalk9.gray(
10189
10660
  ` $ npx lee-spec-kit context --all # ${tr(lang, "cli", "context.tipShowAll")}`
10190
10661
  )
10191
10662
  );
10192
10663
  console.log(
10193
- chalk8.gray(
10664
+ chalk9.gray(
10194
10665
  ` $ npx lee-spec-kit context --done # ${tr(lang, "cli", "context.tipShowDone")}`
10195
10666
  )
10196
10667
  );
@@ -10202,47 +10673,47 @@ async function runContext(featureName, options) {
10202
10673
  }
10203
10674
  const f = state.targetFeatures[0];
10204
10675
  const stepName = stepsMap[f.currentStep] || "Unknown";
10205
- const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk8.yellow(tr(lang, "cli", "context.checkRequired")) : "";
10676
+ const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk9.yellow(tr(lang, "cli", "context.checkRequired")) : "";
10206
10677
  const hasCheckAction = approvalRequired;
10207
10678
  console.log(
10208
- `\u{1F539} Feature: ${chalk8.bold(f.folderName)} ${config.projectType === "multi" ? chalk8.cyan(`(${f.type})`) : ""}`
10679
+ `\u{1F539} Feature: ${chalk9.bold(f.folderName)} ${config.projectType === "multi" ? chalk9.cyan(`(${f.type})`) : ""}`
10209
10680
  );
10210
10681
  console.log(
10211
- ` \u2022 Completion: ${f.completion.implementationDone ? chalk8.green("Implementation \u2705") : chalk8.gray("Implementation \u25EF")} / ${f.completion.workflowDone ? chalk8.green("Workflow \u2705") : chalk8.yellow("Workflow \u25EF")}`
10682
+ ` \u2022 Completion: ${f.completion.implementationDone ? chalk9.green("Implementation \u2705") : chalk9.gray("Implementation \u25EF")} / ${f.completion.workflowDone ? chalk9.green("Workflow \u2705") : chalk9.yellow("Workflow \u25EF")}`
10212
10683
  );
10213
10684
  if (f.issueNumber) {
10214
10685
  console.log(` \u2022 Issue: #${f.issueNumber}`);
10215
10686
  }
10216
- console.log(` \u2022 Path: ${path12.relative(cwd, f.path)}`);
10687
+ console.log(` \u2022 Path: ${path13.relative(cwd, f.path)}`);
10217
10688
  if (f.git.projectBranch) {
10218
10689
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
10219
10690
  }
10220
10691
  console.log();
10221
10692
  console.log(
10222
- `\u{1F539} Progress: ${chalk8.yellow(`Step ${f.currentStep}. ${stepName}`)}`
10693
+ `\u{1F539} Progress: ${chalk9.yellow(`Step ${f.currentStep}. ${stepName}`)}`
10223
10694
  );
10224
10695
  if (f.activeTask) {
10225
10696
  console.log(
10226
- ` \u2022 Active Task: ${chalk8.yellow(`[${f.activeTask.status}]`)} ${f.activeTask.title}`
10697
+ ` \u2022 Active Task: ${chalk9.yellow(`[${f.activeTask.status}]`)} ${f.activeTask.title}`
10227
10698
  );
10228
10699
  } else if (f.nextTodoTask && f.currentStep === 10) {
10229
10700
  console.log(
10230
- ` \u2022 Next TODO: ${chalk8.gray(`[${f.nextTodoTask.status}]`)} ${f.nextTodoTask.title}`
10701
+ ` \u2022 Next TODO: ${chalk9.gray(`[${f.nextTodoTask.status}]`)} ${f.nextTodoTask.title}`
10231
10702
  );
10232
10703
  }
10233
10704
  printChecklist(f, stepDefinitions);
10234
10705
  if (f.warnings.length > 0) {
10235
10706
  console.log();
10236
- console.log(chalk8.yellow("\u26A0\uFE0F Feature Warnings:"));
10237
- f.warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
10707
+ console.log(chalk9.yellow("\u26A0\uFE0F Feature Warnings:"));
10708
+ f.warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
10238
10709
  }
10239
10710
  console.log();
10240
- 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"));
10711
+ 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"));
10241
10712
  if (hasCheckAction) {
10242
- console.log(chalk8.gray(tr(lang, "cli", "context.checkPolicyHint")));
10713
+ console.log(chalk9.gray(tr(lang, "cli", "context.checkPolicyHint")));
10243
10714
  }
10244
10715
  if (!f.actions || f.actions.length === 0) {
10245
- console.log(`\u{1F449} Next Action: ${chalk8.green(chalk8.bold(f.nextAction))}`);
10716
+ console.log(`\u{1F449} Next Action: ${chalk9.green(chalk9.bold(f.nextAction))}`);
10246
10717
  console.log();
10247
10718
  return;
10248
10719
  }
@@ -10255,7 +10726,7 @@ async function runContext(featureName, options) {
10255
10726
  f.currentSubstateOwner
10256
10727
  );
10257
10728
  const showOptionLabels = hasCheckAction;
10258
- console.log(chalk8.green(chalk8.bold("\u{1F449} Next Options (Atomic):")));
10729
+ console.log(chalk9.green(chalk9.bold("\u{1F449} Next Options (Atomic):")));
10259
10730
  let hasDocsCommand = false;
10260
10731
  actionOptions.forEach((option) => {
10261
10732
  const requiresCheck = option.action.requiresUserCheck;
@@ -10268,26 +10739,26 @@ async function runContext(featureName, options) {
10268
10739
  });
10269
10740
  if (hasDocsCommand) {
10270
10741
  console.log(
10271
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`)
10742
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`)
10272
10743
  );
10273
10744
  }
10274
10745
  if (hasCommandOption && longRunningDelegation.shouldDelegate) {
10275
10746
  console.log(
10276
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`)
10747
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`)
10277
10748
  );
10278
10749
  }
10279
10750
  if (hasCheckAction) {
10280
10751
  console.log(
10281
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`)
10752
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`)
10282
10753
  );
10283
10754
  console.log(
10284
- chalk8.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`)
10755
+ chalk9.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`)
10285
10756
  );
10286
10757
  }
10287
10758
  if (requiredDocs.length > 0) {
10288
10759
  for (const requiredDoc of requiredDocs) {
10289
10760
  console.log(
10290
- chalk8.gray(
10761
+ chalk9.gray(
10291
10762
  ` \u21B3 ${tr(lang, "cli", "context.readBuiltinDocFirst", {
10292
10763
  command: requiredDoc.command
10293
10764
  })}`
@@ -10296,9 +10767,9 @@ async function runContext(featureName, options) {
10296
10767
  }
10297
10768
  }
10298
10769
  if (autoRunPlan.available) {
10299
- console.log(chalk8.gray(` \u21B3 ${autoRunPlan.summary}`));
10770
+ console.log(chalk9.gray(` \u21B3 ${autoRunPlan.summary}`));
10300
10771
  console.log(
10301
- chalk8.gray(
10772
+ chalk9.gray(
10302
10773
  ` \u21B3 ${tr(lang, "cli", "context.autoRunCommandHint", {
10303
10774
  command: autoRunPlan.command
10304
10775
  })}`
@@ -10318,16 +10789,16 @@ async function runContext(featureName, options) {
10318
10789
  selectedComponent,
10319
10790
  true
10320
10791
  );
10321
- console.log(chalk8.cyan(` \u21B3 ${finalApprovalPrompt}`));
10792
+ console.log(chalk9.cyan(` \u21B3 ${finalApprovalPrompt}`));
10322
10793
  console.log(
10323
- chalk8.gray(
10794
+ chalk9.gray(
10324
10795
  ` \u21B3 ${tr(lang, "cli", "context.finalLabelCommandHint", {
10325
10796
  command: approveCommand
10326
10797
  })}`
10327
10798
  )
10328
10799
  );
10329
10800
  console.log(
10330
- chalk8.gray(
10801
+ chalk9.gray(
10331
10802
  ` \u21B3 ${tr(lang, "cli", "context.finalTicketCommandHint", {
10332
10803
  command: executeCommand
10333
10804
  })}`
@@ -10341,8 +10812,8 @@ function printChecklist(f, stepDefinitions) {
10341
10812
  checklistSteps.forEach((definition) => {
10342
10813
  const done = definition.checklist.done(f);
10343
10814
  const detail = definition.checklist.detail?.(f) ?? "";
10344
- const mark = done ? chalk8.green("\u2705") : chalk8.gray("\u25EF");
10345
- const label = definition.step === f.currentStep ? chalk8.bold(definition.name) : definition.name;
10815
+ const mark = done ? chalk9.green("\u2705") : chalk9.gray("\u25EF");
10816
+ const label = definition.step === f.currentStep ? chalk9.bold(definition.name) : definition.name;
10346
10817
  console.log(` ${mark} ${definition.step}. ${label} ${detail}`);
10347
10818
  });
10348
10819
  }
@@ -10365,7 +10836,7 @@ function extractTitleAfterId(line, id) {
10365
10836
  return cleaned ? cleaned : void 0;
10366
10837
  }
10367
10838
  async function scanPrdRequirements(fsAdapter, docsDir) {
10368
- const prdDir = path12.join(docsDir, "prd");
10839
+ const prdDir = path13.join(docsDir, "prd");
10369
10840
  const files = await walkFiles(fsAdapter, prdDir, {
10370
10841
  extensions: [".md"],
10371
10842
  ignoreDirs: [".git", "node_modules", "dist", "tmp"]
@@ -10373,7 +10844,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10373
10844
  const definitions = /* @__PURE__ */ new Map();
10374
10845
  const duplicates = [];
10375
10846
  for (const filePath of files) {
10376
- if (path12.basename(filePath).toLowerCase() === "readme.md") {
10847
+ if (path13.basename(filePath).toLowerCase() === "readme.md") {
10377
10848
  continue;
10378
10849
  }
10379
10850
  let content = "";
@@ -10382,7 +10853,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10382
10853
  } catch {
10383
10854
  continue;
10384
10855
  }
10385
- const relFile = normalizeRelPath2(path12.relative(docsDir, filePath));
10856
+ const relFile = normalizeRelPath2(path13.relative(docsDir, filePath));
10386
10857
  const lines = content.split(/\r?\n/);
10387
10858
  let inCodeBlock = false;
10388
10859
  for (let i = 0; i < lines.length; i += 1) {
@@ -10457,7 +10928,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
10457
10928
  ]);
10458
10929
  function formatPath(cwd, p) {
10459
10930
  if (!p) return "";
10460
- return path12.isAbsolute(p) ? path12.relative(cwd, p) : p;
10931
+ return path13.isAbsolute(p) ? path13.relative(cwd, p) : p;
10461
10932
  }
10462
10933
  function detectPlaceholders(content) {
10463
10934
  const patterns = [
@@ -10616,7 +11087,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
10616
11087
  const placeholderContext = {
10617
11088
  projectName: config.projectName,
10618
11089
  featureName: f.slug,
10619
- featurePath: f.docs.featurePathFromDocs || path12.relative(config.docsDir, f.path),
11090
+ featurePath: f.docs.featurePathFromDocs || path13.relative(config.docsDir, f.path),
10620
11091
  repoType: f.type,
10621
11092
  featureNumber
10622
11093
  };
@@ -10626,7 +11097,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
10626
11097
  "tasks.md"
10627
11098
  ];
10628
11099
  for (const file of files) {
10629
- const fullPath = path12.join(f.path, file);
11100
+ const fullPath = path13.join(f.path, file);
10630
11101
  if (!await fs.pathExists(fullPath)) continue;
10631
11102
  const original = await fs.readFile(fullPath, "utf-8");
10632
11103
  let next = original;
@@ -10679,7 +11150,7 @@ async function checkDocsStructure(config, cwd) {
10679
11150
  const issues = [];
10680
11151
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
10681
11152
  for (const dir of requiredDirs) {
10682
- const p = path12.join(config.docsDir, dir);
11153
+ const p = path13.join(config.docsDir, dir);
10683
11154
  if (!await fs.pathExists(p)) {
10684
11155
  issues.push({
10685
11156
  level: "error",
@@ -10691,7 +11162,7 @@ async function checkDocsStructure(config, cwd) {
10691
11162
  });
10692
11163
  }
10693
11164
  }
10694
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
11165
+ const configPath = path13.join(config.docsDir, ".lee-spec-kit.json");
10695
11166
  if (!await fs.pathExists(configPath)) {
10696
11167
  issues.push({
10697
11168
  level: "warn",
@@ -10719,7 +11190,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10719
11190
  }
10720
11191
  const idMap = /* @__PURE__ */ new Map();
10721
11192
  for (const f of features) {
10722
- const rel = f.docs.featurePathFromDocs || path12.relative(config.docsDir, f.path);
11193
+ const rel = f.docs.featurePathFromDocs || path13.relative(config.docsDir, f.path);
10723
11194
  const id = f.id || "UNKNOWN";
10724
11195
  if (!idMap.has(id)) idMap.set(id, []);
10725
11196
  idMap.get(id).push(rel);
@@ -10727,7 +11198,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10727
11198
  if (!isInitialTemplateState) {
10728
11199
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
10729
11200
  for (const file of featureDocs) {
10730
- const p = path12.join(f.path, file);
11201
+ const p = path13.join(f.path, file);
10731
11202
  if (!await fs.pathExists(p)) continue;
10732
11203
  const content = await fs.readFile(p, "utf-8");
10733
11204
  const placeholders = detectPlaceholders(content);
@@ -10742,7 +11213,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10742
11213
  });
10743
11214
  }
10744
11215
  if (decisionsPlaceholderMode !== "off") {
10745
- const decisionsPath = path12.join(f.path, "decisions.md");
11216
+ const decisionsPath = path13.join(f.path, "decisions.md");
10746
11217
  if (await fs.pathExists(decisionsPath)) {
10747
11218
  const content = await fs.readFile(decisionsPath, "utf-8");
10748
11219
  const placeholders = detectPlaceholders(content);
@@ -10771,7 +11242,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10771
11242
  level: "warn",
10772
11243
  code: "spec_status_unset",
10773
11244
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
10774
- path: formatPath(cwd, path12.join(f.path, "spec.md"))
11245
+ path: formatPath(cwd, path13.join(f.path, "spec.md"))
10775
11246
  });
10776
11247
  }
10777
11248
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -10779,7 +11250,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10779
11250
  level: "warn",
10780
11251
  code: "plan_status_unset",
10781
11252
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
10782
- path: formatPath(cwd, path12.join(f.path, "plan.md"))
11253
+ path: formatPath(cwd, path13.join(f.path, "plan.md"))
10783
11254
  });
10784
11255
  }
10785
11256
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -10787,11 +11258,11 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10787
11258
  level: "warn",
10788
11259
  code: "tasks_empty",
10789
11260
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
10790
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11261
+ path: formatPath(cwd, path13.join(f.path, "tasks.md"))
10791
11262
  });
10792
11263
  }
10793
11264
  if (f.docs.tasksExists) {
10794
- const tasksPath = path12.join(f.path, "tasks.md");
11265
+ const tasksPath = path13.join(f.path, "tasks.md");
10795
11266
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
10796
11267
  const unknownPrdTags = [...new Set(
10797
11268
  parseTaskLines(tasksContent).flatMap((task) => task.tags).filter((tag) => isPrdRequirementId(tag)).map((tag) => tag.trim().toUpperCase()).filter((tag) => !prdDefinitions.has(tag))
@@ -10813,7 +11284,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10813
11284
  level: "warn",
10814
11285
  code: "tasks_doc_status_missing",
10815
11286
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
10816
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11287
+ path: formatPath(cwd, path13.join(f.path, "tasks.md"))
10817
11288
  });
10818
11289
  }
10819
11290
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -10821,7 +11292,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10821
11292
  level: "warn",
10822
11293
  code: "tasks_doc_status_unset",
10823
11294
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
10824
- path: formatPath(cwd, path12.join(f.path, "tasks.md"))
11295
+ path: formatPath(cwd, path13.join(f.path, "tasks.md"))
10825
11296
  });
10826
11297
  }
10827
11298
  }
@@ -10845,7 +11316,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10845
11316
  level: "warn",
10846
11317
  code: "missing_feature_id",
10847
11318
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
10848
- path: formatPath(cwd, path12.join(config.docsDir, p))
11319
+ path: formatPath(cwd, path13.join(config.docsDir, p))
10849
11320
  });
10850
11321
  }
10851
11322
  return issues;
@@ -10975,25 +11446,25 @@ function doctorCommand(program2) {
10975
11446
  return;
10976
11447
  }
10977
11448
  console.log();
10978
- console.log(chalk8.bold(tr(lang, "cli", "doctor.title")));
10979
- console.log(chalk8.gray(`- Docs: ${path12.relative(cwd, docsDir)}`));
10980
- console.log(chalk8.gray(`- Type: ${projectType}`));
10981
- console.log(chalk8.gray(`- Lang: ${lang}`));
11449
+ console.log(chalk9.bold(tr(lang, "cli", "doctor.title")));
11450
+ console.log(chalk9.gray(`- Docs: ${path13.relative(cwd, docsDir)}`));
11451
+ console.log(chalk9.gray(`- Type: ${projectType}`));
11452
+ console.log(chalk9.gray(`- Lang: ${lang}`));
10982
11453
  console.log();
10983
11454
  if (warnings.length > 0) {
10984
- console.log(chalk8.yellow(tr(lang, "cli", "doctor.envWarnings")));
10985
- warnings.forEach((w) => console.log(chalk8.yellow(` - ${w}`)));
11455
+ console.log(chalk9.yellow(tr(lang, "cli", "doctor.envWarnings")));
11456
+ warnings.forEach((w) => console.log(chalk9.yellow(` - ${w}`)));
10986
11457
  console.log();
10987
11458
  }
10988
11459
  if (fixResult) {
10989
11460
  const label = fixResult.dryRun ? "\u{1F9EA} Doctor Fix (dry-run)" : "\u{1F6E0}\uFE0F Doctor Fix";
10990
11461
  console.log(
10991
- chalk8.blue(`${label}: ${fixResult.changedFiles} file(s)`)
11462
+ chalk9.blue(`${label}: ${fixResult.changedFiles} file(s)`)
10992
11463
  );
10993
11464
  if (fixResult.changedFiles > 0) {
10994
11465
  fixResult.entries.forEach(
10995
11466
  (entry) => console.log(
10996
- chalk8.gray(` - ${entry.path}: ${entry.changes.join(", ")}`)
11467
+ chalk9.gray(` - ${entry.path}: ${entry.changes.join(", ")}`)
10997
11468
  )
10998
11469
  );
10999
11470
  }
@@ -11003,51 +11474,51 @@ function doctorCommand(program2) {
11003
11474
  const warns = issues.filter((i) => i.level === "warn");
11004
11475
  const infos = issues.filter((i) => i.level === "info");
11005
11476
  if (!hasIssues && infos.length === 0) {
11006
- console.log(chalk8.green(tr(lang, "cli", "doctor.noIssues")));
11477
+ console.log(chalk9.green(tr(lang, "cli", "doctor.noIssues")));
11007
11478
  console.log();
11008
11479
  return;
11009
11480
  }
11010
11481
  if (!hasIssues && infos.length > 0) {
11011
- console.log(chalk8.green(tr(lang, "cli", "doctor.noIssues")));
11482
+ console.log(chalk9.green(tr(lang, "cli", "doctor.noIssues")));
11012
11483
  console.log();
11013
11484
  }
11014
11485
  if (errors.length > 0) {
11015
11486
  console.log(
11016
- chalk8.red(
11487
+ chalk9.red(
11017
11488
  `\u274C ${tr(lang, "cli", "doctor.errorsTitle")} (${errors.length})`
11018
11489
  )
11019
11490
  );
11020
11491
  errors.forEach(
11021
11492
  (i) => console.log(
11022
- chalk8.red(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11493
+ chalk9.red(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11023
11494
  )
11024
11495
  );
11025
11496
  console.log();
11026
11497
  }
11027
11498
  if (warns.length > 0) {
11028
11499
  console.log(
11029
- chalk8.yellow(
11500
+ chalk9.yellow(
11030
11501
  `\u26A0\uFE0F ${tr(lang, "cli", "doctor.warningsTitle")} (${warns.length})`
11031
11502
  )
11032
11503
  );
11033
11504
  warns.forEach(
11034
11505
  (i) => console.log(
11035
- chalk8.yellow(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11506
+ chalk9.yellow(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11036
11507
  )
11037
11508
  );
11038
11509
  console.log();
11039
11510
  }
11040
11511
  if (infos.length > 0) {
11041
- console.log(chalk8.blue(`\u2139\uFE0F Info (${infos.length})`));
11512
+ console.log(chalk9.blue(`\u2139\uFE0F Info (${infos.length})`));
11042
11513
  infos.forEach(
11043
11514
  (i) => console.log(
11044
- chalk8.blue(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11515
+ chalk9.blue(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
11045
11516
  )
11046
11517
  );
11047
11518
  console.log();
11048
11519
  }
11049
11520
  console.log(
11050
- chalk8.gray(
11521
+ chalk9.gray(
11051
11522
  tr(lang, "cli", "doctor.tipJson", {
11052
11523
  strictFlag: options.strict ? " --strict" : ""
11053
11524
  })
@@ -11076,8 +11547,8 @@ function doctorCommand(program2) {
11076
11547
  );
11077
11548
  } else {
11078
11549
  console.error(
11079
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
11080
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
11550
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
11551
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
11081
11552
  );
11082
11553
  printCliErrorSuggestions(suggestions, lang);
11083
11554
  }
@@ -11106,8 +11577,8 @@ function viewCommand(program2) {
11106
11577
  );
11107
11578
  } else {
11108
11579
  console.error(
11109
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
11110
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
11580
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
11581
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
11111
11582
  );
11112
11583
  printCliErrorSuggestions(suggestions, lang);
11113
11584
  }
@@ -11156,49 +11627,49 @@ async function runView(featureName, options) {
11156
11627
  return;
11157
11628
  }
11158
11629
  console.log();
11159
- console.log(chalk8.bold("\u{1F4CA} Workflow View"));
11160
- console.log(chalk8.gray(`- Docs: ${path12.relative(cwd, config.docsDir)}`));
11630
+ console.log(chalk9.bold("\u{1F4CA} Workflow View"));
11631
+ console.log(chalk9.gray(`- Docs: ${path13.relative(cwd, config.docsDir)}`));
11161
11632
  console.log(
11162
- chalk8.gray(
11633
+ chalk9.gray(
11163
11634
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
11164
11635
  )
11165
11636
  );
11166
11637
  console.log(
11167
- chalk8.gray(
11638
+ chalk9.gray(
11168
11639
  `- In Progress: ${state.inProgressFeatures.length}, Ready To Close: ${state.readyToCloseFeatures.length}`
11169
11640
  )
11170
11641
  );
11171
11642
  if (state.warnings.length > 0) {
11172
11643
  console.log();
11173
- console.log(chalk8.yellow("\u26A0\uFE0F Environment warnings:"));
11644
+ console.log(chalk9.yellow("\u26A0\uFE0F Environment warnings:"));
11174
11645
  for (const warning of state.warnings) {
11175
- console.log(chalk8.yellow(` - ${warning}`));
11646
+ console.log(chalk9.yellow(` - ${warning}`));
11176
11647
  }
11177
11648
  }
11178
11649
  if (state.features.length === 0) {
11179
11650
  console.log();
11180
- console.log(chalk8.yellow("No features found."));
11181
- console.log(chalk8.gray("Try: npx lee-spec-kit feature <name>"));
11651
+ console.log(chalk9.yellow("No features found."));
11652
+ console.log(chalk9.gray("Try: npx lee-spec-kit feature <name>"));
11182
11653
  console.log();
11183
11654
  return;
11184
11655
  }
11185
11656
  if (!state.matchedFeature) {
11186
11657
  console.log();
11187
11658
  console.log(
11188
- chalk8.blue(`Selection: ${state.status} (${toReasonCode(state.status)})`)
11659
+ chalk9.blue(`Selection: ${state.status} (${toReasonCode(state.status)})`)
11189
11660
  );
11190
11661
  const rows = state.targetFeatures.length > 0 ? state.targetFeatures : state.features;
11191
11662
  for (const f2 of rows) {
11192
- const statusText = f2.completion.workflowDone ? chalk8.green("WORKFLOW_DONE") : f2.completion.implementationDone ? chalk8.cyan("DONE") : chalk8.yellow("IN_PROGRESS");
11663
+ const statusText = f2.completion.workflowDone ? chalk9.green("WORKFLOW_DONE") : f2.completion.implementationDone ? chalk9.cyan("DONE") : chalk9.yellow("IN_PROGRESS");
11193
11664
  const substateText = f2.currentSubstateId ? `${f2.currentSubstateId} (${f2.currentSubstateOwner ?? "-"} / ${f2.currentSubstatePhase ?? "-"})` : "-";
11194
11665
  console.log(
11195
11666
  `- ${f2.folderName} (${f2.tasks.done}/${f2.tasks.total}) ${statusText} step:${f2.currentStep} substate:${substateText}`
11196
11667
  );
11197
- console.log(chalk8.gray(` next: ${f2.nextAction}`));
11668
+ console.log(chalk9.gray(` next: ${f2.nextAction}`));
11198
11669
  }
11199
11670
  console.log();
11200
11671
  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>";
11201
- console.log(chalk8.gray(selectorTip));
11672
+ console.log(chalk9.gray(selectorTip));
11202
11673
  console.log();
11203
11674
  return;
11204
11675
  }
@@ -11206,7 +11677,7 @@ async function runView(featureName, options) {
11206
11677
  const completion = `${f.tasks.done}/${f.tasks.total}`;
11207
11678
  const stateLabel = f.completion.workflowDone ? "WORKFLOW_DONE" : f.completion.implementationDone ? "DONE" : "IN_PROGRESS";
11208
11679
  console.log();
11209
- console.log(chalk8.blue(`Feature: ${chalk8.bold(f.folderName)}`));
11680
+ console.log(chalk9.blue(`Feature: ${chalk9.bold(f.folderName)}`));
11210
11681
  console.log(`- State: ${stateLabel}`);
11211
11682
  console.log(`- Progress: ${completion}`);
11212
11683
  console.log(`- Step: ${f.currentStep}`);
@@ -11217,14 +11688,14 @@ async function runView(featureName, options) {
11217
11688
  console.log(`- Next: ${nextSummary}`);
11218
11689
  if (state.actionOptions.length > 0) {
11219
11690
  console.log();
11220
- console.log(chalk8.green("Atomic options:"));
11691
+ console.log(chalk9.green("Atomic options:"));
11221
11692
  for (const option of state.actionOptions) {
11222
11693
  const lang = config.lang ?? DEFAULT_LANG;
11223
11694
  const requiresCheck = option.action.requiresUserCheck ? tr(lang, "cli", "context.checkRequired") : "";
11224
11695
  console.log(` ${option.label}. ${requiresCheck}${option.detail}`);
11225
11696
  }
11226
11697
  console.log(
11227
- chalk8.gray(
11698
+ chalk9.gray(
11228
11699
  `Approve with: npx lee-spec-kit context ${f.folderName} --approve <LABEL>`
11229
11700
  )
11230
11701
  );
@@ -11242,13 +11713,13 @@ function normalizeRunId(raw) {
11242
11713
  return value;
11243
11714
  }
11244
11715
  function getFlowRunBaseDir(cwd) {
11245
- return path12.join(getRuntimeStateDir(cwd), "flow-runs");
11716
+ return path13.join(getRuntimeStateDir(cwd), "flow-runs");
11246
11717
  }
11247
11718
  function getFlowRunPath(cwd, runId) {
11248
- return path12.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11719
+ return path13.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11249
11720
  }
11250
11721
  function getFlowRunLockPath(cwd, runId) {
11251
- return path12.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11722
+ return path13.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11252
11723
  }
11253
11724
  async function readFlowRunRecordUnsafe(cwd, runId) {
11254
11725
  const normalized = normalizeRunId(runId);
@@ -11274,7 +11745,7 @@ async function readFlowRunRecordUnsafe(cwd, runId) {
11274
11745
  }
11275
11746
  async function writeFlowRunRecord(cwd, record) {
11276
11747
  const filePath = getFlowRunPath(cwd, record.runId);
11277
- await fs.ensureDir(path12.dirname(filePath));
11748
+ await fs.ensureDir(path13.dirname(filePath));
11278
11749
  await fs.writeJson(filePath, record, { spaces: 2 });
11279
11750
  }
11280
11751
  async function createFlowRunRecord(cwd, input) {
@@ -11403,6 +11874,7 @@ function toCompactFlowContextSnapshot(state) {
11403
11874
  actionOptions: state.actionOptions.map(
11404
11875
  (option) => toCompactFlowActionOption(option)
11405
11876
  ),
11877
+ delegatedAction: buildDelegatedActionContract(state),
11406
11878
  primaryActionLabel: primaryAction?.label ?? null,
11407
11879
  primaryActionType: primaryAction?.action.type ?? null,
11408
11880
  primaryActionCategory: primaryAction?.action.category ?? null,
@@ -12068,8 +12540,8 @@ function flowCommand(program2) {
12068
12540
  );
12069
12541
  } else {
12070
12542
  console.error(
12071
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
12072
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
12543
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
12544
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
12073
12545
  );
12074
12546
  printCliErrorSuggestions(suggestions, lang);
12075
12547
  }
@@ -12388,16 +12860,16 @@ async function runFlow(featureName, options) {
12388
12860
  return;
12389
12861
  }
12390
12862
  console.log();
12391
- console.log(chalk8.bold("\u{1F501} Flow Summary"));
12863
+ console.log(chalk9.bold("\u{1F501} Flow Summary"));
12392
12864
  console.log(
12393
- chalk8.gray(
12865
+ chalk9.gray(
12394
12866
  `- Before: ${before.status} (${toReasonCode(before.status)}) / After: ${after.status} (${toReasonCode(after.status)})`
12395
12867
  )
12396
12868
  );
12397
12869
  if (approvalResult && typeof approvalResult === "object") {
12398
12870
  const result = approvalResult;
12399
12871
  console.log(
12400
- chalk8.gray(
12872
+ chalk9.gray(
12401
12873
  `- Approval: ${result.status ?? "unknown"} (${result.reasonCode ?? "-"})`
12402
12874
  )
12403
12875
  );
@@ -12405,18 +12877,18 @@ async function runFlow(featureName, options) {
12405
12877
  if (autoRun) {
12406
12878
  const presetSuffix = autoRun.preset ? `, preset ${autoRun.preset}` : "";
12407
12879
  console.log(
12408
- chalk8.gray(
12880
+ chalk9.gray(
12409
12881
  `- Auto: ${autoRun.status} (${autoRun.reasonCode}), iterations ${autoRun.iterations}, executions ${autoRun.executions.length}${presetSuffix}`
12410
12882
  )
12411
12883
  );
12412
- console.log(chalk8.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
12884
+ console.log(chalk9.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
12413
12885
  if (autoRun.run) {
12414
12886
  console.log(
12415
- chalk8.gray(
12887
+ chalk9.gray(
12416
12888
  `- Auto run: ${autoRun.run.mode} ${autoRun.run.runId} (${autoRun.run.status})`
12417
12889
  )
12418
12890
  );
12419
- console.log(chalk8.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
12891
+ console.log(chalk9.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
12420
12892
  }
12421
12893
  }
12422
12894
  const agentOrchestration = buildAgentOrchestrationPolicy2(
@@ -12424,21 +12896,21 @@ async function runFlow(featureName, options) {
12424
12896
  resolvedFeatureName || null
12425
12897
  );
12426
12898
  console.log(
12427
- chalk8.gray(
12899
+ chalk9.gray(
12428
12900
  `- Orchestration: ${agentOrchestration.mode}, prefer substate-owner delegation and fall back to legacy long-running categories`
12429
12901
  )
12430
12902
  );
12431
12903
  const statusCounts = statusReport.counts;
12432
12904
  const doctorCounts = doctorReport.counts;
12433
- console.log(chalk8.gray(`- Status features: ${statusCounts?.features ?? 0}`));
12905
+ console.log(chalk9.gray(`- Status features: ${statusCounts?.features ?? 0}`));
12434
12906
  console.log(
12435
- chalk8.gray(
12907
+ chalk9.gray(
12436
12908
  `- Doctor issues: ${doctorCounts?.issues ?? 0} (errors ${doctorCounts?.errors ?? 0}, warnings ${doctorCounts?.warnings ?? 0})`
12437
12909
  )
12438
12910
  );
12439
12911
  if (strictChecks) {
12440
- const strictLabel = strictChecks.statusStrictOk && strictChecks.doctorStrictOk ? chalk8.green("PASS") : chalk8.red("FAIL");
12441
- console.log(chalk8.gray(`- Strict checks: ${strictLabel}`));
12912
+ const strictLabel = strictChecks.statusStrictOk && strictChecks.doctorStrictOk ? chalk9.green("PASS") : chalk9.red("FAIL");
12913
+ console.log(chalk9.gray(`- Strict checks: ${strictLabel}`));
12442
12914
  }
12443
12915
  console.log();
12444
12916
  if (autoRun?.status === "gate_reached" && autoRun.gate?.userFacingLines?.length) {
@@ -12446,7 +12918,7 @@ async function runFlow(featureName, options) {
12446
12918
  console.log(line);
12447
12919
  }
12448
12920
  console.log(
12449
- chalk8.gray(
12921
+ chalk9.gray(
12450
12922
  "Auto gate reached. Reply with one of the labels shown above (example: A OK)."
12451
12923
  )
12452
12924
  );
@@ -12457,15 +12929,15 @@ async function runFlow(featureName, options) {
12457
12929
  }
12458
12930
  if (after.matchedFeature) {
12459
12931
  console.log(
12460
- chalk8.blue(
12932
+ chalk9.blue(
12461
12933
  `Next: npx lee-spec-kit context ${after.matchedFeature.folderName}${componentHint}`
12462
12934
  )
12463
12935
  );
12464
12936
  } else {
12465
- console.log(chalk8.blue(`Next: npx lee-spec-kit context${componentHint}`));
12937
+ console.log(chalk9.blue(`Next: npx lee-spec-kit context${componentHint}`));
12466
12938
  }
12467
12939
  console.log(
12468
- chalk8.gray(
12940
+ chalk9.gray(
12469
12941
  "Tip: add --approve <LABEL> [--execute] to run the selected atomic action."
12470
12942
  )
12471
12943
  );
@@ -12584,27 +13056,27 @@ function tg(lang, key, vars = {}) {
12584
13056
  function detectGithubCliLangSync(cwd) {
12585
13057
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
12586
13058
  const startDirs = [
12587
- explicitDocsDir ? path12.resolve(explicitDocsDir) : "",
12588
- path12.resolve(cwd)
13059
+ explicitDocsDir ? path13.resolve(explicitDocsDir) : "",
13060
+ path13.resolve(cwd)
12589
13061
  ].filter(Boolean);
12590
13062
  const scanOrder = [];
12591
13063
  const seen = /* @__PURE__ */ new Set();
12592
13064
  for (const start of startDirs) {
12593
13065
  let current = start;
12594
13066
  while (true) {
12595
- const abs = path12.resolve(current);
13067
+ const abs = path13.resolve(current);
12596
13068
  if (!seen.has(abs)) {
12597
13069
  scanOrder.push(abs);
12598
13070
  seen.add(abs);
12599
13071
  }
12600
- const parent = path12.dirname(abs);
13072
+ const parent = path13.dirname(abs);
12601
13073
  if (parent === abs) break;
12602
13074
  current = parent;
12603
13075
  }
12604
13076
  }
12605
13077
  for (const base of scanOrder) {
12606
- for (const docsDir of [path12.join(base, "docs"), base]) {
12607
- const configPath = path12.join(docsDir, ".lee-spec-kit.json");
13078
+ for (const docsDir of [path13.join(base, "docs"), base]) {
13079
+ const configPath = path13.join(docsDir, ".lee-spec-kit.json");
12608
13080
  if (fs.existsSync(configPath)) {
12609
13081
  try {
12610
13082
  const parsed = fs.readJsonSync(configPath);
@@ -12613,11 +13085,11 @@ function detectGithubCliLangSync(cwd) {
12613
13085
  } catch {
12614
13086
  }
12615
13087
  }
12616
- const agentsPath = path12.join(docsDir, "agents");
12617
- const featuresPath = path12.join(docsDir, "features");
13088
+ const agentsPath = path13.join(docsDir, "agents");
13089
+ const featuresPath = path13.join(docsDir, "features");
12618
13090
  if (!fs.existsSync(agentsPath) || !fs.existsSync(featuresPath)) continue;
12619
13091
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
12620
- const file = path12.join(agentsPath, probe);
13092
+ const file = path13.join(agentsPath, probe);
12621
13093
  if (!fs.existsSync(file)) continue;
12622
13094
  try {
12623
13095
  const content = fs.readFileSync(file, "utf-8");
@@ -12637,13 +13109,13 @@ function parseLabels(raw, lang) {
12637
13109
  }
12638
13110
  return [...new Set(labels)];
12639
13111
  }
12640
- function escapeRegExp3(value) {
13112
+ function escapeRegExp4(value) {
12641
13113
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12642
13114
  }
12643
13115
  function extractDraftMetadataValue(content, keys) {
12644
13116
  for (const key of keys) {
12645
13117
  const re = new RegExp(
12646
- `^\\s*-\\s*\\*\\*${escapeRegExp3(key)}\\*\\*\\s*:\\s*(.*?)\\s*$`,
13118
+ `^\\s*-\\s*\\*\\*${escapeRegExp4(key)}\\*\\*\\s*:\\s*(.*?)\\s*$`,
12647
13119
  "mi"
12648
13120
  );
12649
13121
  const match = content.match(re);
@@ -12722,7 +13194,7 @@ async function prepareGithubBody(params) {
12722
13194
  };
12723
13195
  }
12724
13196
  }
12725
- await fs.ensureDir(path12.dirname(defaultBodyFile));
13197
+ await fs.ensureDir(path13.dirname(defaultBodyFile));
12726
13198
  await fs.writeFile(defaultBodyFile, generatedBody, "utf-8");
12727
13199
  return {
12728
13200
  body: generatedBody,
@@ -12762,7 +13234,7 @@ function ensureSections(body, sections, kind, lang) {
12762
13234
  };
12763
13235
  const hasMetadataField = (field) => {
12764
13236
  const re = new RegExp(
12765
- `^\\s*-\\s*\\*\\*${escapeRegExp3(field)}\\*\\*\\s*:`,
13237
+ `^\\s*-\\s*\\*\\*${escapeRegExp4(field)}\\*\\*\\s*:`,
12766
13238
  "m"
12767
13239
  );
12768
13240
  return re.test(body);
@@ -12792,7 +13264,7 @@ function ensureSections(body, sections, kind, lang) {
12792
13264
  }
12793
13265
  function ensureDocsExist(docsDir, relativePaths, lang) {
12794
13266
  const missing = relativePaths.filter(
12795
- (relativePath) => !fs.existsSync(path12.join(docsDir, relativePath))
13267
+ (relativePath) => !fs.existsSync(path13.join(docsDir, relativePath))
12796
13268
  );
12797
13269
  if (missing.length > 0) {
12798
13270
  throw createCliError(
@@ -12802,18 +13274,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
12802
13274
  }
12803
13275
  }
12804
13276
  function buildDefaultBodyFileName(kind, docsDir, component) {
12805
- const key = `${path12.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13277
+ const key = `${path13.resolve(docsDir)}::${component.trim().toLowerCase()}`;
12806
13278
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
12807
13279
  return `lee-spec-kit.${digest}.${kind}.md`;
12808
13280
  }
12809
13281
  function toBodyFilePath(raw, kind, docsDir, component, lang) {
12810
- const selected = raw?.trim() || path12.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13282
+ const selected = raw?.trim() || path13.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
12811
13283
  assertValid(
12812
13284
  validatePathWithLang(selected, lang),
12813
13285
  `github.${kind}.bodyFile`,
12814
13286
  lang
12815
13287
  );
12816
- return path12.resolve(selected);
13288
+ return path13.resolve(selected);
12817
13289
  }
12818
13290
  function toProjectRootDocsPath(relativePathFromDocs) {
12819
13291
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -13650,7 +14122,7 @@ function stripMarkdownCodeContexts(body) {
13650
14122
  function hasIssueClosingKeyword(body, issueNumber) {
13651
14123
  if (!issueNumber) return false;
13652
14124
  const cleaned = stripMarkdownCodeContexts(body);
13653
- const issue = escapeRegExp3(issueNumber);
14125
+ const issue = escapeRegExp4(issueNumber);
13654
14126
  const closeKeywordRegex = new RegExp(
13655
14127
  `\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\b\\s*(?:[a-zA-Z0-9_.-]+\\/)?#\\s*${issue}\\b`,
13656
14128
  "i"
@@ -13868,7 +14340,7 @@ function ensureCleanWorktree(cwd, lang) {
13868
14340
  function commitAndPushPaths(cwd, absPaths, message, lang, options) {
13869
14341
  const uniqueRelativePaths = [
13870
14342
  ...new Set(
13871
- absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path12.relative(cwd, absPath) || absPath)
14343
+ absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path13.relative(cwd, absPath) || absPath)
13872
14344
  )
13873
14345
  ];
13874
14346
  if (uniqueRelativePaths.length === 0) return;
@@ -14064,15 +14536,15 @@ function githubCommand(program2) {
14064
14536
  config.lang
14065
14537
  );
14066
14538
  const specContent = await fs.readFile(
14067
- path12.join(config.docsDir, paths.specPath),
14539
+ path13.join(config.docsDir, paths.specPath),
14068
14540
  "utf-8"
14069
14541
  );
14070
14542
  const planContent = await fs.readFile(
14071
- path12.join(config.docsDir, paths.planPath),
14543
+ path13.join(config.docsDir, paths.planPath),
14072
14544
  "utf-8"
14073
14545
  );
14074
14546
  const tasksContent = await fs.readFile(
14075
- path12.join(config.docsDir, paths.tasksPath),
14547
+ path13.join(config.docsDir, paths.tasksPath),
14076
14548
  "utf-8"
14077
14549
  );
14078
14550
  const overview = resolveOverviewFromSpec(
@@ -14115,7 +14587,7 @@ function githubCommand(program2) {
14115
14587
  create: options.create,
14116
14588
  explicitBodyFile,
14117
14589
  defaultBodyFile,
14118
- workflowDraftPath: path12.join(config.docsDir, paths.issuePath),
14590
+ workflowDraftPath: path13.join(config.docsDir, paths.issuePath),
14119
14591
  generatedBody,
14120
14592
  requiredSections: getRequiredIssueSections(config.lang),
14121
14593
  kindLabel: tg(config.lang, "kindIssue"),
@@ -14131,7 +14603,7 @@ function githubCommand(program2) {
14131
14603
  `${feature.type}-issue-sanitized`,
14132
14604
  config.lang
14133
14605
  );
14134
- await fs.ensureDir(path12.dirname(sanitizedBodyFile));
14606
+ await fs.ensureDir(path13.dirname(sanitizedBodyFile));
14135
14607
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14136
14608
  bodyFile = sanitizedBodyFile;
14137
14609
  }
@@ -14180,12 +14652,12 @@ function githubCommand(program2) {
14180
14652
  const syncedIssueNumber = extractIssueNumberFromUrl(issueUrl);
14181
14653
  if (syncedIssueNumber) {
14182
14654
  const synced = syncTasksIssueMetadata(
14183
- path12.join(config.docsDir, paths.tasksPath),
14655
+ path13.join(config.docsDir, paths.tasksPath),
14184
14656
  syncedIssueNumber,
14185
14657
  config.lang
14186
14658
  );
14187
14659
  const draftSynced = syncIssueDraftMetadata(
14188
- path12.join(config.docsDir, paths.issuePath),
14660
+ path13.join(config.docsDir, paths.issuePath),
14189
14661
  syncedIssueNumber
14190
14662
  );
14191
14663
  syncChanged = synced.changed || draftSynced.changed;
@@ -14213,31 +14685,31 @@ function githubCommand(program2) {
14213
14685
  return;
14214
14686
  }
14215
14687
  console.log();
14216
- console.log(chalk8.bold(tg(config.lang, "issueHeader")));
14688
+ console.log(chalk9.bold(tg(config.lang, "issueHeader")));
14217
14689
  console.log(
14218
- chalk8.gray(
14690
+ chalk9.gray(
14219
14691
  `- ${tg(config.lang, "labelFeature")}: ${feature.folderName}`
14220
14692
  )
14221
14693
  );
14222
14694
  console.log(
14223
- chalk8.gray(
14695
+ chalk9.gray(
14224
14696
  `- ${tg(config.lang, "labelBodyFile")}: ${bodyFile}`
14225
14697
  )
14226
14698
  );
14227
14699
  console.log(
14228
- chalk8.gray(
14700
+ chalk9.gray(
14229
14701
  `- ${tg(config.lang, "labelLabels")}: ${labels.join(", ")}`
14230
14702
  )
14231
14703
  );
14232
14704
  if (issueUrl) {
14233
14705
  console.log(
14234
- chalk8.green(
14706
+ chalk9.green(
14235
14707
  tg(config.lang, "issueCreated", { url: issueUrl })
14236
14708
  )
14237
14709
  );
14238
14710
  } else {
14239
14711
  console.log(
14240
- chalk8.blue(tg(config.lang, "issueTemplateGenerated"))
14712
+ chalk9.blue(tg(config.lang, "issueTemplateGenerated"))
14241
14713
  );
14242
14714
  }
14243
14715
  console.log();
@@ -14256,8 +14728,8 @@ function githubCommand(program2) {
14256
14728
  );
14257
14729
  } else {
14258
14730
  console.error(
14259
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14260
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
14731
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
14732
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14261
14733
  );
14262
14734
  printCliErrorSuggestions(suggestions, lang);
14263
14735
  }
@@ -14296,13 +14768,13 @@ function githubCommand(program2) {
14296
14768
  config.lang
14297
14769
  );
14298
14770
  const specContent = await fs.readFile(
14299
- path12.join(config.docsDir, paths.specPath),
14771
+ path13.join(config.docsDir, paths.specPath),
14300
14772
  "utf-8"
14301
14773
  );
14302
- const planPath = path12.join(config.docsDir, paths.planPath);
14774
+ const planPath = path13.join(config.docsDir, paths.planPath);
14303
14775
  const planContent = await fs.pathExists(planPath) ? await fs.readFile(planPath, "utf-8") : "";
14304
14776
  const tasksContent = await fs.readFile(
14305
- path12.join(config.docsDir, paths.tasksPath),
14777
+ path13.join(config.docsDir, paths.tasksPath),
14306
14778
  "utf-8"
14307
14779
  );
14308
14780
  const overview = resolveOverviewFromSpec(
@@ -14350,7 +14822,7 @@ function githubCommand(program2) {
14350
14822
  create: options.create,
14351
14823
  explicitBodyFile,
14352
14824
  defaultBodyFile,
14353
- workflowDraftPath: path12.join(config.docsDir, paths.prPath),
14825
+ workflowDraftPath: path13.join(config.docsDir, paths.prPath),
14354
14826
  generatedBody,
14355
14827
  requiredSections: getRequiredPrSections(config.lang),
14356
14828
  kindLabel: tg(config.lang, "kindPr"),
@@ -14368,7 +14840,7 @@ function githubCommand(program2) {
14368
14840
  `${feature.type}-pr-sanitized`,
14369
14841
  config.lang
14370
14842
  );
14371
- await fs.ensureDir(path12.dirname(sanitizedBodyFile));
14843
+ await fs.ensureDir(path13.dirname(sanitizedBodyFile));
14372
14844
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14373
14845
  bodyFile = sanitizedBodyFile;
14374
14846
  }
@@ -14396,7 +14868,7 @@ function githubCommand(program2) {
14396
14868
  if (preparedBody.source === "generated") {
14397
14869
  await fs.writeFile(bodyFile, body, "utf-8");
14398
14870
  } else {
14399
- await fs.ensureDir(path12.dirname(fallbackBodyFile));
14871
+ await fs.ensureDir(path13.dirname(fallbackBodyFile));
14400
14872
  await fs.writeFile(fallbackBodyFile, body, "utf-8");
14401
14873
  bodyFile = fallbackBodyFile;
14402
14874
  }
@@ -14457,13 +14929,13 @@ function githubCommand(program2) {
14457
14929
  }
14458
14930
  if (prUrl && options.syncTasks !== false) {
14459
14931
  const syncedTasks = syncTasksPrMetadata(
14460
- path12.join(config.docsDir, paths.tasksPath),
14932
+ path13.join(config.docsDir, paths.tasksPath),
14461
14933
  prUrl,
14462
14934
  "Review",
14463
14935
  config.lang
14464
14936
  );
14465
14937
  const syncedDraft = syncPrDraftMetadata(
14466
- path12.join(config.docsDir, paths.prPath),
14938
+ path13.join(config.docsDir, paths.prPath),
14467
14939
  prUrl,
14468
14940
  "Review"
14469
14941
  );
@@ -14504,13 +14976,13 @@ function githubCommand(program2) {
14504
14976
  mergeAlreadyMerged = merged.alreadyMerged;
14505
14977
  if (prUrl && options.syncTasks !== false) {
14506
14978
  const mergedTasksSync = syncTasksPrMetadata(
14507
- path12.join(config.docsDir, paths.tasksPath),
14979
+ path13.join(config.docsDir, paths.tasksPath),
14508
14980
  prUrl,
14509
14981
  "Approved",
14510
14982
  config.lang
14511
14983
  );
14512
14984
  const mergedDraftSync = syncPrDraftMetadata(
14513
- path12.join(config.docsDir, paths.prPath),
14985
+ path13.join(config.docsDir, paths.prPath),
14514
14986
  prUrl,
14515
14987
  "Approved"
14516
14988
  );
@@ -14594,35 +15066,35 @@ function githubCommand(program2) {
14594
15066
  return;
14595
15067
  }
14596
15068
  console.log();
14597
- console.log(chalk8.bold(tg(config.lang, "prHeader")));
15069
+ console.log(chalk9.bold(tg(config.lang, "prHeader")));
14598
15070
  console.log(
14599
- chalk8.gray(
15071
+ chalk9.gray(
14600
15072
  `- ${tg(config.lang, "labelFeature")}: ${feature.folderName}`
14601
15073
  )
14602
15074
  );
14603
15075
  console.log(
14604
- chalk8.gray(
15076
+ chalk9.gray(
14605
15077
  `- ${tg(config.lang, "labelBodyFile")}: ${bodyFile}`
14606
15078
  )
14607
15079
  );
14608
15080
  console.log(
14609
- chalk8.gray(
15081
+ chalk9.gray(
14610
15082
  `- ${tg(config.lang, "labelLabels")}: ${labels.join(", ")}`
14611
15083
  )
14612
15084
  );
14613
15085
  if (prUrl) {
14614
15086
  console.log(
14615
- chalk8.gray(`- ${tg(config.lang, "labelPr")}: ${prUrl}`)
15087
+ chalk9.gray(`- ${tg(config.lang, "labelPr")}: ${prUrl}`)
14616
15088
  );
14617
15089
  }
14618
15090
  if (syncChanged) {
14619
15091
  console.log(
14620
- chalk8.green(tg(config.lang, "prTasksSynced"))
15092
+ chalk9.green(tg(config.lang, "prTasksSynced"))
14621
15093
  );
14622
15094
  }
14623
15095
  if (options.merge) {
14624
15096
  console.log(
14625
- chalk8.green(
15097
+ chalk9.green(
14626
15098
  tg(config.lang, "prMerged", {
14627
15099
  attempts: mergedAttempts ?? 1
14628
15100
  })
@@ -14630,15 +15102,15 @@ function githubCommand(program2) {
14630
15102
  );
14631
15103
  if (mergeAlreadyMerged) {
14632
15104
  console.log(
14633
- chalk8.yellow(tg(config.lang, "prAlreadyMergedNotice"))
15105
+ chalk9.yellow(tg(config.lang, "prAlreadyMergedNotice"))
14634
15106
  );
14635
15107
  }
14636
15108
  for (const warning of postMergeWarnings) {
14637
- console.log(chalk8.yellow(`\u26A0\uFE0F ${warning}`));
15109
+ console.log(chalk9.yellow(`\u26A0\uFE0F ${warning}`));
14638
15110
  }
14639
15111
  } else if (!options.create) {
14640
15112
  console.log(
14641
- chalk8.blue(tg(config.lang, "prTemplateGenerated"))
15113
+ chalk9.blue(tg(config.lang, "prTemplateGenerated"))
14642
15114
  );
14643
15115
  }
14644
15116
  console.log();
@@ -14657,8 +15129,8 @@ function githubCommand(program2) {
14657
15129
  );
14658
15130
  } else {
14659
15131
  console.error(
14660
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14661
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15132
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15133
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14662
15134
  );
14663
15135
  printCliErrorSuggestions(suggestions, lang);
14664
15136
  }
@@ -14716,10 +15188,10 @@ function docsCommand(program2) {
14716
15188
  return;
14717
15189
  }
14718
15190
  console.log();
14719
- console.log(chalk8.bold(tr(config.lang, "cli", "docs.listHeader")));
15191
+ console.log(chalk9.bold(tr(config.lang, "cli", "docs.listHeader")));
14720
15192
  for (const doc of docsList) {
14721
15193
  console.log(`- ${doc.id}: ${doc.title}`);
14722
- console.log(chalk8.gray(` ${doc.command}`));
15194
+ console.log(chalk9.gray(` ${doc.command}`));
14723
15195
  }
14724
15196
  console.log();
14725
15197
  } catch (error) {
@@ -14738,8 +15210,8 @@ function docsCommand(program2) {
14738
15210
  );
14739
15211
  } else {
14740
15212
  console.error(
14741
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14742
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15213
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15214
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14743
15215
  );
14744
15216
  printCliErrorSuggestions(suggestions, lang);
14745
15217
  }
@@ -14781,25 +15253,25 @@ function docsCommand(program2) {
14781
15253
  );
14782
15254
  return;
14783
15255
  }
14784
- const relativeFromCwd = path12.relative(process.cwd(), loaded.entry.absolutePath);
15256
+ const relativeFromCwd = path13.relative(process.cwd(), loaded.entry.absolutePath);
14785
15257
  console.log();
14786
- console.log(chalk8.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
15258
+ console.log(chalk9.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
14787
15259
  console.log(
14788
- chalk8.gray(
15260
+ chalk9.gray(
14789
15261
  `${tr(config.lang, "cli", "docs.sourceLabel")}: ${relativeFromCwd || loaded.entry.absolutePath}`
14790
15262
  )
14791
15263
  );
14792
15264
  console.log(
14793
- chalk8.gray(`${tr(config.lang, "cli", "docs.hashLabel")}: ${loaded.hash}`)
15265
+ chalk9.gray(`${tr(config.lang, "cli", "docs.hashLabel")}: ${loaded.hash}`)
14794
15266
  );
14795
15267
  console.log();
14796
15268
  process.stdout.write(loaded.content.endsWith("\n") ? loaded.content : `${loaded.content}
14797
15269
  `);
14798
15270
  if (followups.length > 0) {
14799
15271
  console.log();
14800
- console.log(chalk8.blue(`${tr(config.lang, "cli", "docs.nextDocs")}:`));
15272
+ console.log(chalk9.blue(`${tr(config.lang, "cli", "docs.nextDocs")}:`));
14801
15273
  for (const followup of followups) {
14802
- console.log(chalk8.gray(`- ${followup.command}`));
15274
+ console.log(chalk9.gray(`- ${followup.command}`));
14803
15275
  }
14804
15276
  console.log();
14805
15277
  }
@@ -14819,8 +15291,8 @@ function docsCommand(program2) {
14819
15291
  );
14820
15292
  } else {
14821
15293
  console.error(
14822
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14823
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15294
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15295
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14824
15296
  );
14825
15297
  printCliErrorSuggestions(suggestions, lang);
14826
15298
  }
@@ -14849,8 +15321,8 @@ function detectCommand(program2) {
14849
15321
  );
14850
15322
  } else {
14851
15323
  console.error(
14852
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
14853
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15324
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15325
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
14854
15326
  );
14855
15327
  printCliErrorSuggestions(suggestions, lang);
14856
15328
  }
@@ -14861,7 +15333,7 @@ function detectCommand(program2) {
14861
15333
  }
14862
15334
  async function runDetect(options) {
14863
15335
  const cwd = process.cwd();
14864
- const targetCwd = options.dir ? path12.resolve(cwd, options.dir) : cwd;
15336
+ const targetCwd = options.dir ? path13.resolve(cwd, options.dir) : cwd;
14865
15337
  const config = await getConfig(targetCwd);
14866
15338
  const detected = !!config;
14867
15339
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
@@ -14888,7 +15360,7 @@ async function runDetect(options) {
14888
15360
  );
14889
15361
  return;
14890
15362
  }
14891
- const configPath2 = path12.join(config.docsDir, ".lee-spec-kit.json");
15363
+ const configPath2 = path13.join(config.docsDir, ".lee-spec-kit.json");
14892
15364
  const configFilePresent2 = await fs.pathExists(configPath2);
14893
15365
  const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
14894
15366
  console.log(
@@ -14914,26 +15386,26 @@ async function runDetect(options) {
14914
15386
  }
14915
15387
  const lang = config?.lang ?? DEFAULT_LANG;
14916
15388
  console.log();
14917
- console.log(chalk8.blue(tr(lang, "cli", "detect.header")));
14918
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelTarget")}: ${targetCwd}`));
15389
+ console.log(chalk9.blue(tr(lang, "cli", "detect.header")));
15390
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelTarget")}: ${targetCwd}`));
14919
15391
  if (!config) {
14920
- console.log(chalk8.yellow(`- ${tr(lang, "cli", "detect.resultNotDetected")}`));
14921
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.notDetectedHint")}`));
15392
+ console.log(chalk9.yellow(`- ${tr(lang, "cli", "detect.resultNotDetected")}`));
15393
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.notDetectedHint")}`));
14922
15394
  console.log();
14923
15395
  return;
14924
15396
  }
14925
- const configPath = path12.join(config.docsDir, ".lee-spec-kit.json");
15397
+ const configPath = path13.join(config.docsDir, ".lee-spec-kit.json");
14926
15398
  const configFilePresent = await fs.pathExists(configPath);
14927
15399
  const detectionSource = configFilePresent ? "config" : "heuristic";
14928
- console.log(chalk8.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
14929
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
15400
+ console.log(chalk9.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
15401
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
14930
15402
  console.log(
14931
- chalk8.gray(
15403
+ chalk9.gray(
14932
15404
  `- ${tr(lang, "cli", "detect.labelConfigPath")}: ${configFilePresent ? configPath : "-"}`
14933
15405
  )
14934
15406
  );
14935
15407
  console.log(
14936
- chalk8.gray(
15408
+ chalk9.gray(
14937
15409
  `- ${tr(lang, "cli", "detect.labelSource")}: ${tr(
14938
15410
  lang,
14939
15411
  "cli",
@@ -14942,14 +15414,14 @@ async function runDetect(options) {
14942
15414
  )
14943
15415
  );
14944
15416
  console.log(
14945
- chalk8.gray(
15417
+ chalk9.gray(
14946
15418
  `- ${tr(lang, "cli", "detect.labelProjectType")}: ${config.projectType}`
14947
15419
  )
14948
15420
  );
14949
- console.log(chalk8.gray(`- ${tr(lang, "cli", "detect.labelLang")}: ${config.lang}`));
15421
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelLang")}: ${config.lang}`));
14950
15422
  if (config.projectName) {
14951
15423
  console.log(
14952
- chalk8.gray(
15424
+ chalk9.gray(
14953
15425
  `- ${tr(lang, "cli", "detect.labelProjectName")}: ${config.projectName}`
14954
15426
  )
14955
15427
  );
@@ -14999,19 +15471,19 @@ function hasTemplateMarkers(content) {
14999
15471
  return patterns.some((pattern) => pattern.test(content));
15000
15472
  }
15001
15473
  async function countFeatureDirs(ctx, docsDir, projectType) {
15002
- const featuresRoot = path12.join(docsDir, "features");
15474
+ const featuresRoot = path13.join(docsDir, "features");
15003
15475
  if (projectType === "single") {
15004
15476
  const dirs = await listSubdirectories(ctx.fs, featuresRoot);
15005
- return dirs.filter((value) => path12.basename(value) !== "feature-base").length;
15477
+ return dirs.filter((value) => path13.basename(value) !== "feature-base").length;
15006
15478
  }
15007
15479
  const components = await listSubdirectories(ctx.fs, featuresRoot);
15008
15480
  let total = 0;
15009
15481
  for (const componentDir of components) {
15010
- const componentName = path12.basename(componentDir).trim().toLowerCase();
15482
+ const componentName = path13.basename(componentDir).trim().toLowerCase();
15011
15483
  if (!componentName || componentName === "feature-base") continue;
15012
15484
  const dirs = await listSubdirectories(ctx.fs, componentDir);
15013
15485
  total += dirs.filter(
15014
- (value) => path12.basename(value) !== "feature-base"
15486
+ (value) => path13.basename(value) !== "feature-base"
15015
15487
  ).length;
15016
15488
  }
15017
15489
  return total;
@@ -15023,7 +15495,7 @@ async function hasUserPrdFile(ctx, prdDir) {
15023
15495
  ignoreDirs: ["node_modules"]
15024
15496
  });
15025
15497
  return files.some(
15026
- (absolutePath) => path12.basename(absolutePath).toLowerCase() !== "readme.md"
15498
+ (absolutePath) => path13.basename(absolutePath).toLowerCase() !== "readme.md"
15027
15499
  );
15028
15500
  }
15029
15501
  function finalizeChecks(checks) {
@@ -15040,17 +15512,17 @@ function finalizeChecks(checks) {
15040
15512
  function printOnboardResult(lang, result) {
15041
15513
  console.log();
15042
15514
  console.log(
15043
- chalk8.bold(t(lang, "\u{1F9ED} Onboarding \uC810\uAC80", "\u{1F9ED} Onboarding Checks"))
15515
+ chalk9.bold(t(lang, "\u{1F9ED} Onboarding \uC810\uAC80", "\u{1F9ED} Onboarding Checks"))
15044
15516
  );
15045
15517
  for (const check of result.checks) {
15046
- const mark = check.status === "ok" ? chalk8.green("\u2705") : check.status === "warn" ? chalk8.yellow("\u26A0\uFE0F") : chalk8.red("\u274C");
15047
- const level = check.status === "ok" ? chalk8.green("OK") : check.status === "warn" ? chalk8.yellow("WARN") : chalk8.red("BLOCK");
15518
+ const mark = check.status === "ok" ? chalk9.green("\u2705") : check.status === "warn" ? chalk9.yellow("\u26A0\uFE0F") : chalk9.red("\u274C");
15519
+ const level = check.status === "ok" ? chalk9.green("OK") : check.status === "warn" ? chalk9.yellow("WARN") : chalk9.red("BLOCK");
15048
15520
  console.log(`${mark} [${level}] ${check.title}`);
15049
15521
  console.log(` ${check.message}`);
15050
- if (check.path) console.log(chalk8.gray(` path: ${check.path}`));
15522
+ if (check.path) console.log(chalk9.gray(` path: ${check.path}`));
15051
15523
  if (check.suggestedCommand) {
15052
15524
  console.log(
15053
- chalk8.gray(
15525
+ chalk9.gray(
15054
15526
  ` ${t(lang, "\uB2E4\uC74C \uBA85\uB839", "next")}: ${check.suggestedCommand}`
15055
15527
  )
15056
15528
  );
@@ -15058,7 +15530,7 @@ function printOnboardResult(lang, result) {
15058
15530
  }
15059
15531
  console.log();
15060
15532
  console.log(
15061
- chalk8.bold(
15533
+ chalk9.bold(
15062
15534
  t(
15063
15535
  lang,
15064
15536
  `\uC694\uC57D: OK ${result.summary.ok}, WARN ${result.summary.warn}, BLOCK ${result.summary.block}`,
@@ -15068,13 +15540,13 @@ function printOnboardResult(lang, result) {
15068
15540
  );
15069
15541
  if (result.status === "ready") {
15070
15542
  console.log(
15071
- chalk8.green(
15543
+ chalk9.green(
15072
15544
  t(lang, "\uC628\uBCF4\uB529 \uC900\uBE44\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Onboarding checks passed.")
15073
15545
  )
15074
15546
  );
15075
15547
  } else if (result.status === "needs_action") {
15076
15548
  console.log(
15077
- chalk8.yellow(
15549
+ chalk9.yellow(
15078
15550
  t(
15079
15551
  lang,
15080
15552
  "\uCD94\uAC00 \uC815\uB9AC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
@@ -15084,7 +15556,7 @@ function printOnboardResult(lang, result) {
15084
15556
  );
15085
15557
  } else {
15086
15558
  console.log(
15087
- chalk8.red(
15559
+ chalk9.red(
15088
15560
  t(
15089
15561
  lang,
15090
15562
  "\uC628\uBCF4\uB529 \uC120\uD589 \uC791\uC5C5\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
@@ -15192,7 +15664,7 @@ async function runOnboardChecks(ctx) {
15192
15664
  });
15193
15665
  }
15194
15666
  }
15195
- const constitutionPath = path12.join(docsDir, "agents", "constitution.md");
15667
+ const constitutionPath = path13.join(docsDir, "agents", "constitution.md");
15196
15668
  if (!await fs.pathExists(constitutionPath)) {
15197
15669
  checks.push({
15198
15670
  id: "constitution_exists",
@@ -15234,7 +15706,7 @@ async function runOnboardChecks(ctx) {
15234
15706
  });
15235
15707
  }
15236
15708
  }
15237
- const customPath = path12.join(docsDir, "agents", "custom.md");
15709
+ const customPath = path13.join(docsDir, "agents", "custom.md");
15238
15710
  if (await fs.pathExists(customPath)) {
15239
15711
  const content = await fs.readFile(customPath, "utf-8");
15240
15712
  if (hasTemplateMarkers(content)) {
@@ -15263,7 +15735,7 @@ async function runOnboardChecks(ctx) {
15263
15735
  });
15264
15736
  }
15265
15737
  }
15266
- const prdDir = path12.join(docsDir, "prd");
15738
+ const prdDir = path13.join(docsDir, "prd");
15267
15739
  const featureCount = await countFeatureDirs(ctx, docsDir, config.projectType);
15268
15740
  const prdReady = await hasUserPrdFile(ctx, prdDir);
15269
15741
  if (!prdReady) {
@@ -15281,7 +15753,7 @@ async function runOnboardChecks(ctx) {
15281
15753
  "PRD is empty. If features already exist, fill PRD as soon as possible."
15282
15754
  ),
15283
15755
  path: prdDir,
15284
- suggestedCommand: `touch ${quotePath(path12.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
15756
+ suggestedCommand: `touch ${quotePath(path13.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
15285
15757
  });
15286
15758
  } else {
15287
15759
  checks.push({
@@ -15422,8 +15894,8 @@ function onboardCommand(program2) {
15422
15894
  );
15423
15895
  } else {
15424
15896
  console.error(
15425
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
15426
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
15897
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
15898
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
15427
15899
  );
15428
15900
  printCliErrorSuggestions(suggestions, lang);
15429
15901
  }
@@ -15466,6 +15938,14 @@ function asNonEmptyString(value, fallback) {
15466
15938
  const trimmed = value.trim();
15467
15939
  return trimmed || fallback;
15468
15940
  }
15941
+ function asRequiredNonEmptyString(value, field) {
15942
+ const normalized = asNonEmptyString(value, "");
15943
+ if (normalized) return normalized;
15944
+ throw createCliError(
15945
+ "VALIDATION_FAILED",
15946
+ `Evidence JSON ${field} is required.`
15947
+ );
15948
+ }
15469
15949
  function asRequiredBoolean(value, field) {
15470
15950
  if (typeof value === "boolean") return value;
15471
15951
  throw createCliError(
@@ -15544,6 +16024,61 @@ function normalizeCommandsExecuted(value) {
15544
16024
  return entry.trim();
15545
16025
  });
15546
16026
  }
16027
+ function normalizeRequiredPathList(value, field) {
16028
+ if (!Array.isArray(value)) {
16029
+ throw createCliError(
16030
+ "VALIDATION_FAILED",
16031
+ `Evidence JSON ${field} must be a non-empty array of repo-relative paths.`
16032
+ );
16033
+ }
16034
+ const out = value.map((entry, index) => {
16035
+ if (typeof entry !== "string" || !entry.trim()) {
16036
+ throw createCliError(
16037
+ "VALIDATION_FAILED",
16038
+ `Evidence JSON ${field}[${index}] must be a non-empty repo-relative path string.`
16039
+ );
16040
+ }
16041
+ return normalizeGitPath3(entry);
16042
+ }).filter(Boolean);
16043
+ if (out.length === 0) {
16044
+ throw createCliError(
16045
+ "VALIDATION_FAILED",
16046
+ `Evidence JSON ${field} must include at least one repo-relative path.`
16047
+ );
16048
+ }
16049
+ return uniquePaths(out);
16050
+ }
16051
+ function normalizeRiskSummaries(value) {
16052
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16053
+ throw createCliError(
16054
+ "VALIDATION_FAILED",
16055
+ 'Evidence JSON "riskSummaries" is required and must include blocking, important, and minor fields.'
16056
+ );
16057
+ }
16058
+ const riskSummaries = value;
16059
+ return {
16060
+ blocking: asRequiredReviewField(
16061
+ riskSummaries.blocking,
16062
+ '"riskSummaries.blocking"',
16063
+ { allowExplicitNone: true }
16064
+ ),
16065
+ important: asRequiredReviewField(
16066
+ riskSummaries.important,
16067
+ '"riskSummaries.important"',
16068
+ { allowExplicitNone: true }
16069
+ ),
16070
+ minor: asRequiredReviewField(
16071
+ riskSummaries.minor,
16072
+ '"riskSummaries.minor"',
16073
+ { allowExplicitNone: true }
16074
+ )
16075
+ };
16076
+ }
16077
+ function formatMissingPathGuidance(field, missingFiles) {
16078
+ return `Evidence JSON ${field} is missing these repo-relative paths:
16079
+ ${missingFiles.map((file) => `- ${file}`).join("\n")}
16080
+ Use paths relative to the project git root, matching files.path entries.`;
16081
+ }
15547
16082
  function normalizeGitPath3(value) {
15548
16083
  return value.trim().replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
15549
16084
  }
@@ -15656,7 +16191,7 @@ var PrePrReviewValidator = class {
15656
16191
  return result.evidence;
15657
16192
  }
15658
16193
  async validateEvidenceWithScope(evidencePath, projectRoot) {
15659
- const fullPath = path12.resolve(evidencePath);
16194
+ const fullPath = path13.resolve(evidencePath);
15660
16195
  if (!await fs.pathExists(fullPath)) {
15661
16196
  throw createCliError(
15662
16197
  "INVALID_ARGUMENT",
@@ -15699,6 +16234,27 @@ var PrePrReviewValidator = class {
15699
16234
  evidence.blockingFindings,
15700
16235
  '"blockingFindings"'
15701
16236
  ),
16237
+ baseSha: asRequiredNonEmptyString(evidence.baseSha, '"baseSha"'),
16238
+ headSha: asRequiredNonEmptyString(evidence.headSha, '"headSha"'),
16239
+ changedFiles: normalizeRequiredPathList(
16240
+ evidence.changedFiles,
16241
+ '"changedFiles"'
16242
+ ),
16243
+ reviewedFiles: normalizeRequiredPathList(
16244
+ evidence.reviewedFiles,
16245
+ '"reviewedFiles"'
16246
+ ),
16247
+ riskSummaries: normalizeRiskSummaries(evidence.riskSummaries),
16248
+ approvalRationale: asRequiredReviewField(
16249
+ evidence.approvalRationale,
16250
+ '"approvalRationale"'
16251
+ ),
16252
+ coverage: {
16253
+ changedFileCount: 0,
16254
+ reviewedFileCount: 0,
16255
+ reviewCoverageRatio: 0,
16256
+ missingFiles: []
16257
+ },
15702
16258
  files: normalizeEvidenceFiles(evidence.files),
15703
16259
  residualRisks: normalizeResidualRisks(evidence.residualRisks),
15704
16260
  commandsExecuted: normalizeCommandsExecuted(evidence.commandsExecuted)
@@ -15733,9 +16289,9 @@ var PrePrReviewValidator = class {
15733
16289
  ]);
15734
16290
  const reviewedFiles = new Set(
15735
16291
  normalizedEvidence.files.map(
15736
- (f) => path12.relative(
16292
+ (f) => path13.relative(
15737
16293
  projectRoot,
15738
- path12.resolve(projectRoot, f.path)
16294
+ path13.resolve(projectRoot, f.path)
15739
16295
  )
15740
16296
  ).map((entry) => normalizeGitPath3(entry)).filter(Boolean)
15741
16297
  );
@@ -15747,6 +16303,36 @@ var PrePrReviewValidator = class {
15747
16303
  ${missingFiles.map((f) => `- ${f}`).join("\n")}`
15748
16304
  );
15749
16305
  }
16306
+ const changedFilesInEvidence = new Set(
16307
+ normalizedEvidence.changedFiles.map((entry) => normalizeGitPath3(entry))
16308
+ );
16309
+ const missingChangedFiles = changedFiles.filter(
16310
+ (file) => !changedFilesInEvidence.has(file)
16311
+ );
16312
+ if (missingChangedFiles.length > 0) {
16313
+ throw createCliError(
16314
+ "VALIDATION_FAILED",
16315
+ formatMissingPathGuidance('"changedFiles"', missingChangedFiles)
16316
+ );
16317
+ }
16318
+ const reviewedFilesInEvidence = new Set(
16319
+ normalizedEvidence.reviewedFiles.map((entry) => normalizeGitPath3(entry))
16320
+ );
16321
+ const missingReviewedFiles = changedFiles.filter(
16322
+ (file) => !reviewedFilesInEvidence.has(file)
16323
+ );
16324
+ if (missingReviewedFiles.length > 0) {
16325
+ throw createCliError(
16326
+ "VALIDATION_FAILED",
16327
+ formatMissingPathGuidance('"reviewedFiles"', missingReviewedFiles)
16328
+ );
16329
+ }
16330
+ normalizedEvidence.coverage = {
16331
+ changedFileCount: changedFiles.length,
16332
+ reviewedFileCount: normalizedEvidence.reviewedFiles.length,
16333
+ reviewCoverageRatio: changedFiles.length === 0 ? 1 : normalizedEvidence.reviewedFiles.length / changedFiles.length,
16334
+ missingFiles: missingReviewedFiles
16335
+ };
15750
16336
  return {
15751
16337
  evidence: normalizedEvidence,
15752
16338
  scope
@@ -15881,11 +16467,27 @@ var DEFAULT_EVIDENCE_FOR_ANY_MODE = {
15881
16467
  specAlignmentChecked: true,
15882
16468
  findingCount: 0,
15883
16469
  blockingFindings: 0,
16470
+ baseSha: "unrecorded",
16471
+ headSha: "unrecorded",
16472
+ changedFiles: [],
16473
+ reviewedFiles: [],
16474
+ riskSummaries: {
16475
+ blocking: "none",
16476
+ important: "manual direct record without structured evidence",
16477
+ minor: "manual direct record without structured evidence"
16478
+ },
16479
+ approvalRationale: "manual direct record without structured evidence; not valid for approve",
16480
+ coverage: {
16481
+ changedFileCount: 0,
16482
+ reviewedFileCount: 0,
16483
+ reviewCoverageRatio: 0,
16484
+ missingFiles: []
16485
+ },
15884
16486
  files: [],
15885
16487
  residualRisks: ["none"],
15886
16488
  commandsExecuted: []
15887
16489
  };
15888
- function escapeRegExp4(value) {
16490
+ function escapeRegExp5(value) {
15889
16491
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15890
16492
  }
15891
16493
  function normalizeDecision(raw) {
@@ -15899,14 +16501,14 @@ function normalizeDecision(raw) {
15899
16501
  return null;
15900
16502
  }
15901
16503
  function findSpecLineIndex(lines, keys) {
15902
- const escaped = keys.map((key) => escapeRegExp4(key));
16504
+ const escaped = keys.map((key) => escapeRegExp5(key));
15903
16505
  const re = new RegExp(
15904
16506
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
15905
16507
  );
15906
16508
  return lines.findIndex((line) => re.test(line));
15907
16509
  }
15908
16510
  function replaceSpecLine(line, keys, preferredKey, value) {
15909
- const escaped = keys.map((key) => escapeRegExp4(key));
16511
+ const escaped = keys.map((key) => escapeRegExp5(key));
15910
16512
  const re = new RegExp(
15911
16513
  `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
15912
16514
  );
@@ -15996,11 +16598,28 @@ ${normalizedCommands.map((c) => ` - \`${c}\``).join("\n")}
15996
16598
  - **Spec Alignment Checked**: ${input.evidence.specAlignmentChecked ? "yes" : "no"}
15997
16599
  - **Finding Count**: ${input.evidence.findingCount}
15998
16600
  - **Blocking Findings**: ${input.evidence.blockingFindings}
16601
+ - **Base SHA**: ${input.evidence.baseSha}
16602
+ - **Head SHA**: ${input.evidence.headSha}
16603
+ - **Approval Rationale**: ${input.evidence.approvalRationale}
15999
16604
  ${commandsRun}
16000
16605
 
16001
16606
  - **Residual Risks**:
16002
16607
  ${residualRisksSection}
16003
16608
 
16609
+ - **Risk Summaries**:
16610
+ - Blocking: ${input.evidence.riskSummaries.blocking}
16611
+ - Important: ${input.evidence.riskSummaries.important}
16612
+ - Minor: ${input.evidence.riskSummaries.minor}
16613
+
16614
+ - **Evidence Coverage**:
16615
+ - Changed File Count: ${input.evidence.coverage.changedFileCount}
16616
+ - Reviewed File Count: ${input.evidence.coverage.reviewedFileCount}
16617
+ - Review Coverage Ratio: ${input.evidence.coverage.reviewCoverageRatio.toFixed(2)}
16618
+ - Reviewed Files:
16619
+ ${input.evidence.reviewedFiles.length > 0 ? input.evidence.reviewedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)"}
16620
+ - Evidence Changed Files:
16621
+ ${input.evidence.changedFiles.length > 0 ? input.evidence.changedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)"}
16622
+
16004
16623
  - **Review Scope**:
16005
16624
  - **Main Base Ref**: ${input.scope.baseRef}
16006
16625
  - **Main Merge Base**: ${input.scope.mergeBase ?? "unresolved"}
@@ -16054,7 +16673,7 @@ function prePrReviewCommand(program2) {
16054
16673
  })
16055
16674
  );
16056
16675
  } else {
16057
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
16676
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16058
16677
  printCliErrorSuggestions(suggestions, lang);
16059
16678
  }
16060
16679
  process.exitCode = 1;
@@ -16066,7 +16685,7 @@ function prePrReviewCommand(program2) {
16066
16685
  "Decision outcome: approve | changes_requested | blocked"
16067
16686
  ).option("--note <text>", "Decision note text").option(
16068
16687
  "--evidence <path>",
16069
- "Optional review evidence path (for example review-trace.json); required only when policy demands it"
16688
+ "Structured review evidence path (for example review-trace.json); required for approve and whenever policy demands it"
16070
16689
  ).option("--json", "Output JSON").action(
16071
16690
  async (featureName, options) => {
16072
16691
  try {
@@ -16086,7 +16705,7 @@ function prePrReviewCommand(program2) {
16086
16705
  })
16087
16706
  );
16088
16707
  } else {
16089
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
16708
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16090
16709
  printCliErrorSuggestions(suggestions, lang);
16091
16710
  }
16092
16711
  process.exitCode = 1;
@@ -16133,7 +16752,7 @@ async function runPrePrReviewRun(featureName, options) {
16133
16752
  );
16134
16753
  const policy = resolvePrePrReviewPolicy(config.workflow);
16135
16754
  const preferred = getPreferredKeys(config.lang);
16136
- const tasksPath = path12.join(feature.path, "tasks.md");
16755
+ const tasksPath = path13.join(feature.path, "tasks.md");
16137
16756
  let tasksUpdated = false;
16138
16757
  if (await fs.pathExists(tasksPath)) {
16139
16758
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -16183,7 +16802,8 @@ async function runPrePrReviewRun(featureName, options) {
16183
16802
  suggestedParallelism: 1,
16184
16803
  fallbackToMainAgentWhenQuotaExceeded: true,
16185
16804
  nextStepRequirement: "generate_review_trace_then_record",
16186
- nextMainState: "pre_pr_review_running",
16805
+ delegatedWorkRequired: true,
16806
+ nextMainState: "pre_pr_review_in_progress",
16187
16807
  evidenceFile: "review-trace.json",
16188
16808
  tasksUpdated,
16189
16809
  tasksPath,
@@ -16202,24 +16822,27 @@ async function runPrePrReviewRun(featureName, options) {
16202
16822
  console.log(prompt);
16203
16823
  console.log();
16204
16824
  console.log(
16205
- chalk8.yellow(
16206
- 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."
16825
+ chalk9.yellow(
16826
+ 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."
16207
16827
  )
16208
16828
  );
16209
16829
  console.log(
16210
- chalk8.gray(
16830
+ chalk9.gray(
16211
16831
  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."
16212
16832
  )
16213
16833
  );
16214
16834
  console.log(
16215
- chalk8.gray(
16835
+ chalk9.gray(
16216
16836
  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."
16217
16837
  )
16218
16838
  );
16219
16839
  console.log(`Reuse key: pre-pr:${featureRef}`);
16220
16840
  console.log(`Suggested parallelism: 1`);
16221
- console.log(`Next main state: pre_pr_review_running`);
16841
+ console.log(`Next main state: pre_pr_review_in_progress`);
16222
16842
  console.log(`Evidence file: review-trace.json`);
16843
+ console.log(
16844
+ 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"
16845
+ );
16223
16846
  if (tasksUpdated) {
16224
16847
  console.log(`tasks.md updated: ${tasksPath}`);
16225
16848
  console.log(
@@ -16240,7 +16863,7 @@ async function runPrePrReview(featureName, options) {
16240
16863
  `tasks.md not found for feature: ${feature.folderName}`
16241
16864
  );
16242
16865
  }
16243
- const tasksPath = path12.join(feature.path, "tasks.md");
16866
+ const tasksPath = path13.join(feature.path, "tasks.md");
16244
16867
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
16245
16868
  const policy = resolvePrePrReviewPolicy(config.workflow);
16246
16869
  const preferred = getPreferredKeys(config.lang);
@@ -16273,6 +16896,12 @@ async function runPrePrReview(featureName, options) {
16273
16896
  "`--evidence <path>` is required when workflow.prePrReview.enforceExecutionEvidence=true."
16274
16897
  );
16275
16898
  }
16899
+ if (decision === "approve" && !options.evidence) {
16900
+ throw createCliError(
16901
+ "INVALID_ARGUMENT",
16902
+ "`--evidence <path>` is required for approve decisions. Generate structured review evidence first, for example `--evidence review-trace.json`."
16903
+ );
16904
+ }
16276
16905
  if (options.evidence) {
16277
16906
  const validator = new PrePrReviewValidator(ctx);
16278
16907
  const validationResult = await validator.validateEvidenceWithScope(
@@ -16332,7 +16961,7 @@ async function runPrePrReview(featureName, options) {
16332
16961
  }
16333
16962
  }
16334
16963
  }
16335
- const decisionsPath = path12.join(feature.path, "decisions.md");
16964
+ const decisionsPath = path13.join(feature.path, "decisions.md");
16336
16965
  const decisionLogEntry = buildReportContent({
16337
16966
  folderName: feature.folderName,
16338
16967
  date,
@@ -16348,9 +16977,9 @@ async function runPrePrReview(featureName, options) {
16348
16977
  await fs.writeFile(decisionsPath, nextDecisions, "utf-8");
16349
16978
  }
16350
16979
  const decisionsPathFromDocs = normalizePathForDoc(
16351
- path12.join(feature.docs.featurePathFromDocs, "decisions.md")
16980
+ path13.join(feature.docs.featurePathFromDocs, "decisions.md")
16352
16981
  );
16353
- const evidencePath = path12.basename(config.docsDir) === "docs" ? normalizePathForDoc(path12.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
16982
+ const evidencePath = path13.basename(config.docsDir) === "docs" ? normalizePathForDoc(path13.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
16354
16983
  let nextTasks = tasksContent;
16355
16984
  nextTasks = upsertSpecLine(
16356
16985
  nextTasks,
@@ -16397,26 +17026,26 @@ async function runPrePrReview(featureName, options) {
16397
17026
  return;
16398
17027
  }
16399
17028
  console.log();
16400
- console.log(chalk8.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
16401
- console.log(chalk8.gray(`- Decision: ${decision}`));
16402
- console.log(chalk8.gray(`- Decisions log: ${decisionsPath}`));
17029
+ console.log(chalk9.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
17030
+ console.log(chalk9.gray(`- Decision: ${decision}`));
17031
+ console.log(chalk9.gray(`- Decisions log: ${decisionsPath}`));
16403
17032
  if (nextTasks !== tasksContent) {
16404
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17033
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
16405
17034
  }
16406
17035
  console.log();
16407
17036
  }
16408
- function escapeRegExp5(value) {
17037
+ function escapeRegExp6(value) {
16409
17038
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16410
17039
  }
16411
17040
  function findSpecLineIndex2(lines, keys) {
16412
- const escaped = keys.map((key) => escapeRegExp5(key));
17041
+ const escaped = keys.map((key) => escapeRegExp6(key));
16413
17042
  const re = new RegExp(
16414
17043
  `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
16415
17044
  );
16416
17045
  return lines.findIndex((line) => re.test(line));
16417
17046
  }
16418
17047
  function replaceSpecLine2(line, keys, preferredKey, value) {
16419
- const escaped = keys.map((key) => escapeRegExp5(key));
17048
+ const escaped = keys.map((key) => escapeRegExp6(key));
16420
17049
  const re = new RegExp(
16421
17050
  `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
16422
17051
  );
@@ -16491,7 +17120,7 @@ async function runCodeReviewRun(featureName, options) {
16491
17120
  );
16492
17121
  }
16493
17122
  const feature = state.matchedFeature;
16494
- const tasksPath = path12.join(feature.path, "tasks.md");
17123
+ const tasksPath = path13.join(feature.path, "tasks.md");
16495
17124
  let tasksUpdated = false;
16496
17125
  if (await fs.pathExists(tasksPath)) {
16497
17126
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -16526,7 +17155,7 @@ async function runCodeReviewRun(featureName, options) {
16526
17155
  nextMainState: "code_review_running",
16527
17156
  tasksUpdated,
16528
17157
  tasksPath,
16529
- decisionsPath: path12.join(feature.path, "decisions.md"),
17158
+ decisionsPath: path13.join(feature.path, "decisions.md"),
16530
17159
  prompt,
16531
17160
  recordedAt: getLocalDateString()
16532
17161
  };
@@ -16537,30 +17166,30 @@ async function runCodeReviewRun(featureName, options) {
16537
17166
  console.log(prompt);
16538
17167
  console.log();
16539
17168
  console.log(
16540
- chalk8.yellow(
17169
+ chalk9.yellow(
16541
17170
  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."
16542
17171
  )
16543
17172
  );
16544
- console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16545
- console.log(chalk8.gray(`- owner: ${payload.owner}`));
16546
- console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16547
- console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
17173
+ console.log(chalk9.gray(`- substate: ${payload.substateId}`));
17174
+ console.log(chalk9.gray(`- owner: ${payload.owner}`));
17175
+ console.log(chalk9.gray(`- reuse key: ${payload.reuseKey}`));
17176
+ console.log(chalk9.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16548
17177
  console.log(
16549
- chalk8.gray(
17178
+ chalk9.gray(
16550
17179
  `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16551
17180
  )
16552
17181
  );
16553
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17182
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
16554
17183
  if (tasksUpdated) {
16555
- console.log(chalk8.gray(`- tasks.md updated: ${payload.tasksPath}`));
17184
+ console.log(chalk9.gray(`- tasks.md updated: ${payload.tasksPath}`));
16556
17185
  console.log(
16557
- chalk8.gray(
17186
+ chalk9.gray(
16558
17187
  config.lang === "ko" ? "- PR \uB9AC\uBDF0 \uC0C1\uD0DC: Running" : "- PR Review status: Running"
16559
17188
  )
16560
17189
  );
16561
17190
  }
16562
- console.log(chalk8.gray(`- tasks.md: ${payload.tasksPath}`));
16563
- console.log(chalk8.gray(`- decisions.md: ${payload.decisionsPath}`));
17191
+ console.log(chalk9.gray(`- tasks.md: ${payload.tasksPath}`));
17192
+ console.log(chalk9.gray(`- decisions.md: ${payload.decisionsPath}`));
16564
17193
  }
16565
17194
  function codeReviewRunCommand(program2) {
16566
17195
  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(
@@ -16582,7 +17211,7 @@ function codeReviewRunCommand(program2) {
16582
17211
  })
16583
17212
  );
16584
17213
  } else {
16585
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17214
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16586
17215
  printCliErrorSuggestions(suggestions, lang);
16587
17216
  }
16588
17217
  process.exitCode = 1;
@@ -16617,8 +17246,8 @@ function requirementsCommand(program2) {
16617
17246
  const lang = ctx?.config?.lang ?? DEFAULT_LANG;
16618
17247
  const cliError = toCliError(error);
16619
17248
  console.error(
16620
- chalk8.red(tr(lang, "cli", "common.errorLabel")),
16621
- chalk8.red(`[${cliError.code}] ${cliError.message}`)
17249
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
17250
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
16622
17251
  );
16623
17252
  process.exitCode = 1;
16624
17253
  }
@@ -16651,7 +17280,7 @@ async function runRequirements(options) {
16651
17280
  }
16652
17281
  for (const feature of scan.features) {
16653
17282
  if (!feature.docs.tasksExists) continue;
16654
- const tasksPath = path12.join(feature.path, "tasks.md");
17283
+ const tasksPath = path13.join(feature.path, "tasks.md");
16655
17284
  let tasksContent = "";
16656
17285
  try {
16657
17286
  tasksContent = await ctx.fs.readFile(tasksPath, "utf-8");
@@ -16785,10 +17414,10 @@ async function runRequirements(options) {
16785
17414
  process.stdout.write(`${lines.join("\n")}
16786
17415
  `);
16787
17416
  if (options.write) {
16788
- const outputPath = path12.join(docsDir, "prd", "status.md");
17417
+ const outputPath = path13.join(docsDir, "prd", "status.md");
16789
17418
  await ctx.fs.writeFile(outputPath, `${lines.join("\n")}
16790
17419
  `, "utf-8");
16791
- console.log(chalk8.green(`\u2705 wrote: ${outputPath}`));
17420
+ console.log(chalk9.green(`\u2705 wrote: ${outputPath}`));
16792
17421
  }
16793
17422
  if (options.strict && issuesFound) process.exitCode = 1;
16794
17423
  }
@@ -16870,7 +17499,7 @@ async function resolveTaskRunContext(featureName, options) {
16870
17499
  }
16871
17500
  async function runTaskRun(featureName, options) {
16872
17501
  const { config, feature } = await resolveTaskRunContext(featureName, options);
16873
- const tasksPath = path12.join(feature.path, "tasks.md");
17502
+ const tasksPath = path13.join(feature.path, "tasks.md");
16874
17503
  if (!await fs.pathExists(tasksPath)) {
16875
17504
  throw createCliError(
16876
17505
  "PRECONDITION_FAILED",
@@ -16941,20 +17570,20 @@ async function runTaskRun(featureName, options) {
16941
17570
  }
16942
17571
  console.log(prompt);
16943
17572
  console.log();
16944
- console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16945
- console.log(chalk8.gray(`- owner: ${payload.owner}`));
16946
- console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16947
- console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
17573
+ console.log(chalk9.gray(`- substate: ${payload.substateId}`));
17574
+ console.log(chalk9.gray(`- owner: ${payload.owner}`));
17575
+ console.log(chalk9.gray(`- reuse key: ${payload.reuseKey}`));
17576
+ console.log(chalk9.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16948
17577
  console.log(
16949
- chalk8.gray(
17578
+ chalk9.gray(
16950
17579
  `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16951
17580
  )
16952
17581
  );
16953
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17582
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
16954
17583
  if (tasksUpdated) {
16955
17584
  console.log();
16956
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
16957
- console.log(chalk8.gray(`- task status: TODO -> DOING`));
17585
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
17586
+ console.log(chalk9.gray(`- task status: TODO -> DOING`));
16958
17587
  }
16959
17588
  }
16960
17589
  function taskRunCommand(program2) {
@@ -16978,7 +17607,7 @@ function taskRunCommand(program2) {
16978
17607
  })
16979
17608
  );
16980
17609
  } else {
16981
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17610
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
16982
17611
  printCliErrorSuggestions(suggestions, lang);
16983
17612
  }
16984
17613
  process.exitCode = 1;
@@ -17029,7 +17658,7 @@ async function resolveTaskCompleteContext(featureName, options) {
17029
17658
  }
17030
17659
  async function runTaskComplete(featureName, options) {
17031
17660
  const { feature } = await resolveTaskCompleteContext(featureName, options);
17032
- const tasksPath = path12.join(feature.path, "tasks.md");
17661
+ const tasksPath = path13.join(feature.path, "tasks.md");
17033
17662
  if (!await fs.pathExists(tasksPath)) {
17034
17663
  throw createCliError(
17035
17664
  "PRECONDITION_FAILED",
@@ -17089,13 +17718,13 @@ async function runTaskComplete(featureName, options) {
17089
17718
  return;
17090
17719
  }
17091
17720
  console.log(
17092
- chalk8.green(
17721
+ chalk9.green(
17093
17722
  `Marked task ${resolvedTask.taskId} as DONE for ${feature.folderName}.`
17094
17723
  )
17095
17724
  );
17096
- console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17097
- console.log(chalk8.gray(`- status: ${resolvedTask.status} -> DONE`));
17098
- console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17725
+ console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
17726
+ console.log(chalk9.gray(`- status: ${resolvedTask.status} -> DONE`));
17727
+ console.log(chalk9.gray(`- next main state: ${payload.nextMainState}`));
17099
17728
  }
17100
17729
  function taskCompleteCommand(program2) {
17101
17730
  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(
@@ -17117,7 +17746,7 @@ function taskCompleteCommand(program2) {
17117
17746
  })
17118
17747
  );
17119
17748
  } else {
17120
- console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17749
+ console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
17121
17750
  printCliErrorSuggestions(suggestions, lang);
17122
17751
  }
17123
17752
  process.exitCode = 1;
@@ -17164,15 +17793,15 @@ function getBanner(opts) {
17164
17793
  ${version}
17165
17794
  ` : "\n";
17166
17795
  if (process.stdout.isTTY) {
17167
- return `${chalk8.cyan(ascii)}${chalk8.gray(footer)}`;
17796
+ return `${chalk9.cyan(ascii)}${chalk9.gray(footer)}`;
17168
17797
  }
17169
17798
  return `${ascii}${footer}`;
17170
17799
  }
17171
- var CACHE_FILE = path12.join(os.homedir(), ".lee-spec-kit-version-cache.json");
17800
+ var CACHE_FILE = path13.join(os.homedir(), ".lee-spec-kit-version-cache.json");
17172
17801
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
17173
17802
  function getCurrentVersion() {
17174
17803
  try {
17175
- const packageJsonPath = path12.join(__dirname$1, "..", "package.json");
17804
+ const packageJsonPath = path13.join(__dirname$1, "..", "package.json");
17176
17805
  if (fs.existsSync(packageJsonPath)) {
17177
17806
  const pkg = fs.readJsonSync(packageJsonPath);
17178
17807
  return pkg.version;
@@ -17206,8 +17835,8 @@ function resolveUpdateNoticeLang() {
17206
17835
  }
17207
17836
  function printUpdateNotice(current, latest, lang) {
17208
17837
  console.log();
17209
- console.log(chalk8.yellow(tr(lang, "cli", "versionCheck.noticeAvailable", { latest, current })));
17210
- console.log(chalk8.gray(tr(lang, "cli", "versionCheck.updateCommand")));
17838
+ console.log(chalk9.yellow(tr(lang, "cli", "versionCheck.noticeAvailable", { latest, current })));
17839
+ console.log(chalk9.gray(tr(lang, "cli", "versionCheck.updateCommand")));
17211
17840
  console.log();
17212
17841
  }
17213
17842
  function spawnBackgroundVersionCheck() {
@@ -17276,7 +17905,7 @@ function shouldCheckForUpdates() {
17276
17905
  if (shouldCheckForUpdates()) checkForUpdates();
17277
17906
  function getCliVersion() {
17278
17907
  try {
17279
- const packageJsonPath = path12.join(__dirname$1, "..", "package.json");
17908
+ const packageJsonPath = path13.join(__dirname$1, "..", "package.json");
17280
17909
  if (fs.existsSync(packageJsonPath)) {
17281
17910
  const pkg = fs.readJsonSync(packageJsonPath);
17282
17911
  if (pkg?.version) return String(pkg.version);
@@ -17294,6 +17923,7 @@ if (shouldShowBanner()) {
17294
17923
  }
17295
17924
  initCommand(program);
17296
17925
  featureCommand(program);
17926
+ ideaCommand(program);
17297
17927
  statusCommand(program);
17298
17928
  updateCommand(program);
17299
17929
  configCommand(program);