lee-spec-kit 0.6.25 → 0.6.27

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,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import path22 from 'path';
2
+ import path23 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { program } from 'commander';
5
- import fs16 from 'fs-extra';
5
+ import fs17 from 'fs-extra';
6
6
  import prompts from 'prompts';
7
7
  import chalk6 from 'chalk';
8
8
  import { spawn, spawnSync, execFileSync, execSync } from 'child_process';
@@ -10,7 +10,7 @@ import os from 'os';
10
10
  import { createHash, randomUUID } from 'crypto';
11
11
 
12
12
  var getFilename = () => fileURLToPath(import.meta.url);
13
- var getDirname = () => path22.dirname(getFilename());
13
+ var getDirname = () => path23.dirname(getFilename());
14
14
  var __dirname$1 = /* @__PURE__ */ getDirname();
15
15
  async function walkFiles(rootDir, options = {}) {
16
16
  const out = [];
@@ -21,9 +21,9 @@ async function walkFiles(rootDir, options = {}) {
21
21
  (options.ignoreDirs || []).map((value) => value.trim().toLowerCase()).filter(Boolean)
22
22
  );
23
23
  async function visit(current) {
24
- const entries = await fs16.readdir(current, { withFileTypes: true });
24
+ const entries = await fs17.readdir(current, { withFileTypes: true });
25
25
  for (const entry of entries) {
26
- const absolute = path22.join(current, entry.name);
26
+ const absolute = path23.join(current, entry.name);
27
27
  if (entry.isDirectory()) {
28
28
  if (ignored.has(entry.name.trim().toLowerCase())) continue;
29
29
  await visit(absolute);
@@ -31,26 +31,26 @@ async function walkFiles(rootDir, options = {}) {
31
31
  }
32
32
  if (!entry.isFile()) continue;
33
33
  if (normalizedExtensions.size > 0) {
34
- const ext = path22.extname(entry.name).toLowerCase();
34
+ const ext = path23.extname(entry.name).toLowerCase();
35
35
  if (!normalizedExtensions.has(ext)) continue;
36
36
  }
37
37
  out.push(absolute);
38
38
  }
39
39
  }
40
- if (await fs16.pathExists(rootDir)) {
40
+ if (await fs17.pathExists(rootDir)) {
41
41
  await visit(rootDir);
42
42
  }
43
43
  return out;
44
44
  }
45
45
  async function listSubdirectories(rootDir) {
46
- if (!await fs16.pathExists(rootDir)) return [];
47
- const entries = await fs16.readdir(rootDir, { withFileTypes: true });
48
- return entries.filter((entry) => entry.isDirectory()).map((entry) => path22.join(rootDir, entry.name));
46
+ if (!await fs17.pathExists(rootDir)) return [];
47
+ const entries = await fs17.readdir(rootDir, { withFileTypes: true });
48
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => path23.join(rootDir, entry.name));
49
49
  }
50
50
 
51
51
  // src/utils/template.ts
52
52
  async function copyTemplates(src, dest) {
53
- await fs16.copy(src, dest, {
53
+ await fs17.copy(src, dest, {
54
54
  overwrite: true,
55
55
  errorOnExist: false
56
56
  });
@@ -66,22 +66,22 @@ function applyReplacements(content, replacements) {
66
66
  async function replaceInFiles(dir, replacements) {
67
67
  const files = await walkFiles(dir, { extensions: [".md"] });
68
68
  for (const file of files) {
69
- let content = await fs16.readFile(file, "utf-8");
69
+ let content = await fs17.readFile(file, "utf-8");
70
70
  content = applyReplacements(content, replacements);
71
- await fs16.writeFile(file, content, "utf-8");
71
+ await fs17.writeFile(file, content, "utf-8");
72
72
  }
73
73
  const shFiles = await walkFiles(dir, { extensions: [".sh"] });
74
74
  for (const file of shFiles) {
75
- let content = await fs16.readFile(file, "utf-8");
75
+ let content = await fs17.readFile(file, "utf-8");
76
76
  content = applyReplacements(content, replacements);
77
- await fs16.writeFile(file, content, "utf-8");
77
+ await fs17.writeFile(file, content, "utf-8");
78
78
  }
79
79
  }
80
80
  var __filename2 = fileURLToPath(import.meta.url);
81
- var __dirname2 = path22.dirname(__filename2);
81
+ var __dirname2 = path23.dirname(__filename2);
82
82
  function getTemplatesDir() {
83
- const rootDir = path22.resolve(__dirname2, "..");
84
- return path22.join(rootDir, "templates");
83
+ const rootDir = path23.resolve(__dirname2, "..");
84
+ return path23.join(rootDir, "templates");
85
85
  }
86
86
 
87
87
  // src/utils/locales/ko.ts
@@ -166,11 +166,13 @@ var ko = {
166
166
  "context.autoRunUnavailable": "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC790\uB3D9 \uC2E4\uD589\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
167
167
  "context.autoRunSummary": "config \uAE30\uC900\uC73C\uB85C \uC2B9\uC778 \uD544\uC694 \uCE74\uD14C\uACE0\uB9AC \uC804\uAE4C\uC9C0 \uC5F0\uC18D \uC2E4\uD589\uD558\uC138\uC694: {categories}",
168
168
  "context.autoRunCommandHint": "\uC790\uB3D9 \uC2E4\uD589 \uBA85\uB839(config \uAC8C\uC774\uD2B8): {command}",
169
+ "context.subAgentOrchestrationHint": "\uBA54\uC778 \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158: \uC9E7\uC740 \uB2E8\uACC4\uB294 \uBA54\uC778\uC774 \uC9C1\uC811 \uC218\uD589\uD558\uACE0, \uC7A5\uC2DC\uAC04 \uB8E8\uD504(task_execute/code_review/review_fix_commit/pre_pr_review \uB610\uB294 auto)\uB294 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784\uD558\uC138\uC694.",
169
170
  "context.commandDetail.branchCreateWithWorktree": "({scope}) worktree {worktree}\uB97C \uC0AC\uC6A9\uD574 \uBE0C\uB79C\uCE58 {branch}\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
170
171
  "context.commandDetail.branchCreateWithBranch": "({scope}) \uBE0C\uB79C\uCE58 {branch}\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
171
172
  "context.commandDetail.branchCreateGeneric": "({scope}) feature \uBE0C\uB79C\uCE58\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
172
173
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) \uBA85\uC2DC\uC801 \uC2B9\uC778 \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
173
174
  "context.commandDetail.codeReviewPushFix": "({scope}) \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 push\uD558\uC138\uC694",
175
+ "context.commandDetail.prePrReviewRun": "({scope}) Pre-PR \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD558\uACE0 decisions.md\uC640 tasks.md\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
174
176
  "context.actionSummary.runDocsCommand": "\uBB38\uC11C \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
175
177
  "context.actionSummary.runProjectCommand": "\uD504\uB85C\uC81D\uD2B8 \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
176
178
  "context.actionDetail.featureFolder": "Feature \uD3F4\uB354\uC640 \uAE30\uBCF8 \uBB38\uC11C \uACE8\uACA9\uC744 \uC900\uBE44\uD558\uC138\uC694",
@@ -179,16 +181,38 @@ var ko = {
179
181
  "context.actionDetail.planWrite": "plan.md\uB97C \uC791\uC131/\uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C \uB9DE\uCD94\uC138\uC694",
180
182
  "context.actionDetail.planApprove": "plan.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
181
183
  "context.actionDetail.tasksWrite": "tasks.md\uB97C \uC791\uC131/\uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
184
+ "context.actionDetail.tasksWriteCreate": "tasks.md\uB97C \uC0DD\uC131\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694",
185
+ "context.actionDetail.tasksWriteNeedAtLeastOne": "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uCD94\uAC00\uD558\uC138\uC694",
186
+ "context.actionDetail.tasksWriteImprove": "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
182
187
  "context.actionDetail.tasksApprove": "tasks.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
183
188
  "context.actionDetail.issueCreate": "\uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 \uC774\uC288 \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
189
+ "context.actionDetail.issueCreateAndWrite": "\uC774\uC288 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC2B9\uC778(OK) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD574 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
190
+ "context.actionDetail.issueCreatePrepareFromDoc": "issue.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
191
+ "context.actionDetail.issueCreateFromDoc": "Ready \uC0C1\uD0DC issue.md\uB85C \uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
184
192
  "context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
185
193
  "context.actionDetail.reviewFixCommit": "\uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC73C\uB85C \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uB9CC\uB4DC\uC138\uC694",
186
194
  "context.actionDetail.prePrReview": "PR \uC804 \uB9AC\uBDF0\uB97C \uC218\uD589\uD558\uACE0 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694",
187
195
  "context.actionDetail.prCreate": "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 PR \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
196
+ "context.actionDetail.prCreateRequiredSequence": "PR 2\uB2E8\uACC4(\uCD08\uC548/\uC2B9\uC778 \uD6C4 \uC0DD\uC131/\uB3D9\uAE30\uD654)\uB97C \uC21C\uC11C\uB300\uB85C \uC644\uB8CC\uD558\uC138\uC694",
197
+ "context.actionDetail.prCreatePrepareFromDoc": "pr.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
198
+ "context.actionDetail.prCreateExecuteFromDoc": "Ready \uC0C1\uD0DC pr.md\uB85C PR\uC744 \uC0DD\uC131\uD558\uACE0 \uB9C1\uD06C/\uC0C1\uD0DC\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
188
199
  "context.actionDetail.prStatusUpdate": "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C \uCD5C\uC2E0\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
200
+ "context.actionDetail.prStatusUpdateSetReview": "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694",
201
+ "context.actionDetail.prStatusUpdateSyncApproved": "\uC6D0\uACA9 \uBA38\uC9C0 \uC0C1\uD0DC\uB97C \uBC18\uC601\uD574 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
189
202
  "context.actionDetail.codeReview": "\uCF54\uB4DC \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D\uC744 \uBC18\uC601\uD558\uACE0 PR \uB9AC\uBDF0 \uC815\uBCF4\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
203
+ "context.actionDetail.codeReviewNeedEvidenceField": "tasks.md\uC5D0 PR \uB9AC\uBDF0 Evidence \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
204
+ "context.actionDetail.codeReviewNeedEvidence": "PR \uB9AC\uBDF0 Evidence \uC694\uC57D\uC744 \uAE30\uB85D\uD558\uC138\uC694",
205
+ "context.actionDetail.codeReviewNeedDecisionField": "tasks.md\uC5D0 PR \uB9AC\uBDF0 Decision \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
206
+ "context.actionDetail.codeReviewNeedDecision": "PR \uB9AC\uBDF0 Decision\uC744 \uAE30\uB85D\uD558\uC138\uC694",
207
+ "context.actionDetail.codeReviewResolve": "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uBC18\uC601\uD558\uACE0 PR \uB9AC\uBDF0 \uBB38\uC11C\uB97C \uCD5C\uC2E0\uD654\uD558\uC138\uC694",
208
+ "context.actionDetail.codeReviewNeedProjectRoot": "\uB9AC\uBDF0 \uC791\uC5C5\uC744 \uACC4\uC18D\uD558\uB824\uBA74 projectRoot\uB97C \uC124\uC815\uD558\uC138\uC694",
209
+ "context.actionDetail.codeReviewRemoteBlocked": "\uC6D0\uACA9 PR \uCC28\uB2E8 \uC0AC\uC720\uB97C \uD574\uC18C\uD55C \uB4A4 \uBA38\uC9C0\uB97C \uC9C4\uD589\uD558\uC138\uC694",
210
+ "context.actionDetail.codeReviewMergeAfterOk": "\uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
211
+ "context.actionDetail.codeReviewRequestReview": "\uB9AC\uBDF0 \uC694\uCCAD\uC744 \uC9C4\uD589\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC720\uC9C0\uD558\uC138\uC694",
190
212
  "context.actionDetail.worktreeCleanup": "\uC644\uB8CC\uB41C feature worktree\uB97C \uC815\uB9AC\uD558\uC138\uC694",
191
213
  "context.actionDetail.prMetadataMigrate": "tasks.md\uC758 PR \uD56D\uBAA9 \uD615\uC2DD\uC744 \uCD5C\uC2E0 \uD15C\uD50C\uB9BF\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
214
+ "context.actionDetail.prMetadataMigratePrFields": "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
215
+ "context.actionDetail.prMetadataMigratePrePrReviewField": "tasks.md\uC5D0 PR \uC804 \uB9AC\uBDF0 \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
192
216
  "context.actionDetail.userRequestReplan": "\uC0C8 \uC0AC\uC6A9\uC790 \uC694\uAD6C\uB97C \uBA3C\uC800 \uBC18\uC601\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
193
217
  "context.actionDetail.featureDone": "\uC774 Feature\uC758 \uC644\uB8CC \uC870\uAC74\uC774 \uBAA8\uB450 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
194
218
  "context.actionDetail.fallback": "\uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
@@ -200,7 +224,7 @@ var ko = {
200
224
  "context.suggestion.showOpen": "\uC9C4\uD589 \uC911 Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
201
225
  "context.finalLabelCommandHint": "\uB77C\uBCA8\uC744 \uBC1B\uC73C\uBA74 \uC2B9\uC778 \uC120\uD0DD \uC2E4\uD589: {command}",
202
226
  "context.finalTicketCommandHint": "\uBA85\uB839 \uC2E4\uD589\uC740 \uC2B9\uC778 \uACB0\uACFC\uC758 \uD2F0\uCF13\uC73C\uB85C \uC2E4\uD589: {command}",
203
- "context.readBuiltinDocFirst": "\uC774\uBC88 \uC138\uC158\uC5D0 \uC544\uC9C1 \uC77D\uC9C0 \uC54A\uC558\uAC70\uB098 \uBCC0\uACBD \uAC00\uB2A5\uC131\uC774 \uC788\uC744 \uB54C\uB9CC \uD544\uC694\uD55C \uB0B4\uC7A5 \uBB38\uC11C\uB97C \uD655\uC778\uD558\uC138\uC694.",
227
+ "context.readBuiltinDocFirst": "\uC774\uBC88 \uC138\uC158\uC5D0 \uC544\uC9C1 \uC77D\uC9C0 \uC54A\uC558\uAC70\uB098 \uBCC0\uACBD \uAC00\uB2A5\uC131\uC774 \uC788\uC744 \uB54C\uB9CC \uD544\uC694\uD55C \uB0B4\uC7A5 \uBB38\uC11C\uB97C \uD655\uC778\uD558\uC138\uC694: {command}",
204
228
  "context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59\uC740 git-workflow \uAC00\uC774\uB4DC\uB97C \uAE30\uC900\uC73C\uB85C \uD655\uC778\uD558\uC138\uC694.",
205
229
  "context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
206
230
  "context.list.projectCommitNeeded": "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694",
@@ -209,7 +233,6 @@ var ko = {
209
233
  "context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
210
234
  "context.list.addPrePrReviewField": "Pre-PR Review \uD544\uB4DC \uCD94\uAC00",
211
235
  "context.list.completePrePrReview": "Pre-PR \uB9AC\uBDF0 \uC644\uB8CC \uCC98\uB9AC",
212
- "context.list.addPrePrFindings": "Pre-PR Findings \uAE30\uB85D",
213
236
  "context.list.addPrePrEvidence": "Pre-PR Evidence \uADFC\uAC70 \uCD94\uAC00",
214
237
  "context.list.addPrePrDecision": "Pre-PR Decision \uAE30\uB85D",
215
238
  "context.list.resolvePrePrDecision": "Pre-PR Decision\uC744 approve\uB85C \uC815\uB9AC",
@@ -547,7 +570,6 @@ var ko = {
547
570
  legacyTasksDocStatusField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. `\uBB38\uC11C \uC0C1\uD0DC` \uD544\uB4DC(Draft/Review/Approved)\uB97C \uCD94\uAC00\uD574 \uD0DC\uC2A4\uD06C \uC2B9\uC778 \uB2E8\uACC4\uB97C \uD65C\uC131\uD654\uD558\uC138\uC694.",
548
571
  legacyTasksPrFields: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR` \uBC0F `PR \uC0C1\uD0DC` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
549
572
  legacyTasksPrePrReviewField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`)",
550
- legacyTasksPrePrFindingsField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Findings` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Findings**: major=0, minor=0`)",
551
573
  legacyTasksPrePrEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
552
574
  legacyTasksPrePrDecisionField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Decision` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Decision**: \uACB0\uC815: ...`)",
553
575
  legacyTasksPrReviewEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB2E8\uACC4 \uC804\uC5D0 `PR \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
@@ -566,10 +588,9 @@ var ko = {
566
588
  workflowPrRemoteChecksPending: "\uC6D0\uACA9 PR \uCCB4\uD06C \uB300\uAE30\uAC00 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCCB4\uD06C \uC644\uB8CC \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
567
589
  workflowPrePrReviewMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC5D0 `- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`\uC744 \uCD94\uAC00\uD558\uC138\uC694.)",
568
590
  workflowPrePrReviewNotDone: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0`\uAC00 Done\uC774 \uC544\uB2D9\uB2C8\uB2E4. (\uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0 \uD6C4 Done\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
569
- workflowPrePrFindingsMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Findings`\uAC00 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`major=<n>, minor=<n>` \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D)",
570
591
  workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (path_required \uC815\uCC45\uC774\uBA74 \uC2E4\uC81C \uC874\uC7AC\uD558\uB294 \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
571
592
  workflowPrePrDecisionMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`decision: approve|changes_requested|blocked ...` \uD615\uC2DD)",
572
- workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4. Findings\uB97C \uD574\uACB0\uD558\uACE0 pre-pr-review\uB97C \uC7AC\uC2E4\uD589\uD574 `approve`\uB85C \uB9DE\uCD94\uC138\uC694."
593
+ workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB9AC\uC2A4\uD06C\uB97C \uD574\uC18C\uD55C \uB4A4 pre-pr-review\uB97C \uC7AC\uC2E4\uD589\uD574 `approve`\uB85C \uB9DE\uCD94\uC138\uC694."
573
594
  }
574
595
  };
575
596
  var ko_default = ko;
@@ -656,11 +677,13 @@ var en = {
656
677
  "context.autoRunUnavailable": "Auto-run is not available in the current context.",
657
678
  "context.autoRunSummary": "Run continuously by config until approval-required categories appear: {categories}",
658
679
  "context.autoRunCommandHint": "Auto-run command (config-based gate): {command}",
680
+ "context.subAgentOrchestrationHint": "Main-agent orchestration: keep short steps in the main agent, and delegate only long-running loops (task_execute/code_review/review_fix_commit/pre_pr_review or auto mode) to a sub-agent.",
659
681
  "context.commandDetail.branchCreateWithWorktree": "({scope}) create or reuse worktree {worktree} for branch {branch}",
660
682
  "context.commandDetail.branchCreateWithBranch": "({scope}) create or reuse worktree for branch {branch}",
661
683
  "context.commandDetail.branchCreateGeneric": "({scope}) create or reuse feature branch worktree",
662
684
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) merge PR after explicit OK",
663
685
  "context.commandDetail.codeReviewPushFix": "({scope}) push review-fix commits",
686
+ "context.commandDetail.prePrReviewRun": "({scope}) run pre-PR review and sync decisions.md + tasks.md",
664
687
  "context.actionSummary.runDocsCommand": "Run docs command",
665
688
  "context.actionSummary.runProjectCommand": "Run project command",
666
689
  "context.actionDetail.featureFolder": "Prepare feature folder and baseline docs",
@@ -669,16 +692,38 @@ var en = {
669
692
  "context.actionDetail.planWrite": "Write or refine plan.md and set status",
670
693
  "context.actionDetail.planApprove": "Approve plan.md",
671
694
  "context.actionDetail.tasksWrite": "Write or refine tasks.md and align document status",
695
+ "context.actionDetail.tasksWriteCreate": "Create tasks.md and set Doc Status to Review",
696
+ "context.actionDetail.tasksWriteNeedAtLeastOne": "Add at least one task to tasks.md",
697
+ "context.actionDetail.tasksWriteImprove": "Refine tasks.md and align Doc Status",
672
698
  "context.actionDetail.tasksApprove": "Approve tasks.md",
673
699
  "context.actionDetail.issueCreate": "Create the issue and sync issue fields in tasks.md",
700
+ "context.actionDetail.issueCreateAndWrite": "Draft issue content, get explicit OK, then create and sync Issue",
701
+ "context.actionDetail.issueCreatePrepareFromDoc": "Refine issue.md draft and set Status to Ready",
702
+ "context.actionDetail.issueCreateFromDoc": "Create GitHub Issue from ready issue.md and sync Issue",
674
703
  "context.actionDetail.taskExecute": "Proceed with the current task",
675
704
  "context.actionDetail.reviewFixCommit": "Create a review-fix commit with resolved feedback summary",
676
705
  "context.actionDetail.prePrReview": "Run pre-PR review and record results",
677
706
  "context.actionDetail.prCreate": "Create PR and sync PR fields in tasks.md",
707
+ "context.actionDetail.prCreateRequiredSequence": "Complete PR 2-step flow: prepare draft + OK, then create and sync",
708
+ "context.actionDetail.prCreatePrepareFromDoc": "Refine pr.md draft and set Status to Ready",
709
+ "context.actionDetail.prCreateExecuteFromDoc": "Create PR from ready pr.md and sync PR link/status",
678
710
  "context.actionDetail.prStatusUpdate": "Sync PR status in tasks.md with remote status",
711
+ "context.actionDetail.prStatusUpdateSetReview": "Set PR Status to Review",
712
+ "context.actionDetail.prStatusUpdateSyncApproved": "PR merged remotely; sync PR Status to Approved",
679
713
  "context.actionDetail.codeReview": "Address review feedback and update PR review fields",
714
+ "context.actionDetail.codeReviewNeedEvidenceField": "Add PR Review Evidence field in tasks.md",
715
+ "context.actionDetail.codeReviewNeedEvidence": "Record PR Review Evidence summary",
716
+ "context.actionDetail.codeReviewNeedDecisionField": "Add PR Review Decision field in tasks.md",
717
+ "context.actionDetail.codeReviewNeedDecision": "Record PR Review Decision",
718
+ "context.actionDetail.codeReviewResolve": "Address review feedback and keep PR review docs updated",
719
+ "context.actionDetail.codeReviewNeedProjectRoot": "Set projectRoot to continue review actions",
720
+ "context.actionDetail.codeReviewRemoteBlocked": "Resolve remote PR blockers before merge",
721
+ "context.actionDetail.codeReviewMergeAfterOk": "Merge PR after explicit OK",
722
+ "context.actionDetail.codeReviewRequestReview": "Request review and keep PR Status as Review",
680
723
  "context.actionDetail.worktreeCleanup": "Clean up the completed feature worktree",
681
724
  "context.actionDetail.prMetadataMigrate": "Update tasks.md PR fields to the latest template format",
725
+ "context.actionDetail.prMetadataMigratePrFields": "Update tasks.md with PR/PR Status fields",
726
+ "context.actionDetail.prMetadataMigratePrePrReviewField": "Add Pre-PR Review field in tasks.md",
682
727
  "context.actionDetail.userRequestReplan": "Handle the new user request first and re-run context",
683
728
  "context.actionDetail.featureDone": "All completion checks are satisfied for this feature",
684
729
  "context.actionDetail.fallback": "Verify current status and re-run context",
@@ -690,7 +735,7 @@ var en = {
690
735
  "context.suggestion.showOpen": "Show open features",
691
736
  "context.finalLabelCommandHint": "When a label is provided, run approval selection: {command}",
692
737
  "context.finalTicketCommandHint": "Execute commands using the ticket from approval result: {command}",
693
- "context.readBuiltinDocFirst": "Read required built-in docs only if not read in this session yet or likely changed.",
738
+ "context.readBuiltinDocFirst": "Read required built-in docs only if not read in this session yet or likely changed: {command}",
694
739
  "context.tipDocsCommitRules": "Check commit message rules against the git-workflow guide.",
695
740
  "context.list.docsCommitNeeded": "Commit docs changes",
696
741
  "context.list.projectCommitNeeded": "Commit project code changes",
@@ -699,7 +744,6 @@ var en = {
699
744
  "context.list.recordPrLink": "Record PR link",
700
745
  "context.list.addPrePrReviewField": "Add Pre-PR Review field",
701
746
  "context.list.completePrePrReview": "Complete Pre-PR review",
702
- "context.list.addPrePrFindings": "Record Pre-PR Findings",
703
747
  "context.list.addPrePrEvidence": "Add Pre-PR Evidence",
704
748
  "context.list.addPrePrDecision": "Add Pre-PR Decision",
705
749
  "context.list.resolvePrePrDecision": "Resolve Pre-PR decision to approve",
@@ -1037,7 +1081,6 @@ var en = {
1037
1081
  legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Draft/Review/Approved) to enable tasks approval.",
1038
1082
  legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
1039
1083
  legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
1040
- legacyTasksPrePrFindingsField: "Legacy tasks.md format detected. Add `Pre-PR Findings` before PR steps. (`- **Pre-PR Findings**: major=0, minor=0`)",
1041
1084
  legacyTasksPrePrEvidenceField: "Legacy tasks.md format detected. Add `Pre-PR Evidence` before PR steps.",
1042
1085
  legacyTasksPrePrDecisionField: "Legacy tasks.md format detected. Add `Pre-PR Decision` before PR steps. (`- **Pre-PR Decision**: decision: ...`)",
1043
1086
  legacyTasksPrReviewEvidenceField: "Legacy tasks.md format detected. Add `PR Review Evidence` before review iteration.",
@@ -1056,10 +1099,9 @@ var en = {
1056
1099
  workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
1057
1100
  workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
1058
1101
  workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
1059
- workflowPrePrFindingsMissing: "Implementation is done but `Pre-PR Findings` is missing/invalid. (Use `major=<n>, minor=<n>`.)",
1060
1102
  workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Record a real existing path when path_required policy is enabled.)",
1061
1103
  workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: approve|changes_requested|blocked ...`.)",
1062
- workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve findings and re-run pre-PR review until decision becomes `approve`."
1104
+ workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve review risks and re-run pre-PR review until decision becomes `approve`."
1063
1105
  }
1064
1106
  };
1065
1107
  var en_default = en;
@@ -1485,10 +1527,10 @@ var DEFAULT_STALE_MS = 2 * 6e4;
1485
1527
  var RUNTIME_GIT_DIRNAME = "lee-spec-kit.runtime";
1486
1528
  var RUNTIME_TEMP_DIRNAME = "lee-spec-kit-runtime";
1487
1529
  function toScopeKey(value) {
1488
- return createHash("sha1").update(path22.resolve(value)).digest("hex").slice(0, 16);
1530
+ return createHash("sha1").update(path23.resolve(value)).digest("hex").slice(0, 16);
1489
1531
  }
1490
1532
  function getTempRuntimeDir(scopePath) {
1491
- return path22.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1533
+ return path23.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1492
1534
  }
1493
1535
  function resolveGitRuntimeDir(cwd) {
1494
1536
  try {
@@ -1502,42 +1544,42 @@ function resolveGitRuntimeDir(cwd) {
1502
1544
  }
1503
1545
  ).trim();
1504
1546
  if (!out) return null;
1505
- return path22.isAbsolute(out) ? out : path22.resolve(cwd, out);
1547
+ return path23.isAbsolute(out) ? out : path23.resolve(cwd, out);
1506
1548
  } catch {
1507
1549
  return null;
1508
1550
  }
1509
1551
  }
1510
1552
  function getRuntimeStateDir(cwd) {
1511
- const resolved = path22.resolve(cwd);
1553
+ const resolved = path23.resolve(cwd);
1512
1554
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1513
1555
  }
1514
1556
  function getDocsLockPath(docsDir) {
1515
- return path22.join(
1557
+ return path23.join(
1516
1558
  getRuntimeStateDir(docsDir),
1517
1559
  "locks",
1518
1560
  `docs-${toScopeKey(docsDir)}.lock`
1519
1561
  );
1520
1562
  }
1521
1563
  function getInitLockPath(targetDir) {
1522
- return path22.join(
1523
- getRuntimeStateDir(path22.dirname(path22.resolve(targetDir))),
1564
+ return path23.join(
1565
+ getRuntimeStateDir(path23.dirname(path23.resolve(targetDir))),
1524
1566
  "locks",
1525
1567
  `init-${toScopeKey(targetDir)}.lock`
1526
1568
  );
1527
1569
  }
1528
1570
  function getApprovalTicketStorePath(docsDir) {
1529
- return path22.join(
1571
+ return path23.join(
1530
1572
  getRuntimeStateDir(docsDir),
1531
1573
  "tickets",
1532
1574
  `approval-${toScopeKey(docsDir)}.json`
1533
1575
  );
1534
1576
  }
1535
1577
  function getProjectExecutionLockPath(cwd) {
1536
- return path22.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1578
+ return path23.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1537
1579
  }
1538
1580
  async function isStaleLock(lockPath, staleMs) {
1539
1581
  try {
1540
- const stat = await fs16.stat(lockPath);
1582
+ const stat = await fs17.stat(lockPath);
1541
1583
  if (Date.now() - stat.mtimeMs <= staleMs) {
1542
1584
  return false;
1543
1585
  }
@@ -1552,7 +1594,7 @@ async function isStaleLock(lockPath, staleMs) {
1552
1594
  }
1553
1595
  async function readLockPayload(lockPath) {
1554
1596
  try {
1555
- const raw = await fs16.readFile(lockPath, "utf8");
1597
+ const raw = await fs17.readFile(lockPath, "utf8");
1556
1598
  const parsed = JSON.parse(raw);
1557
1599
  if (!parsed || typeof parsed !== "object") return null;
1558
1600
  return parsed;
@@ -1574,17 +1616,17 @@ function isProcessAlive(pid) {
1574
1616
  }
1575
1617
  }
1576
1618
  async function tryAcquire(lockPath, owner) {
1577
- await fs16.ensureDir(path22.dirname(lockPath));
1619
+ await fs17.ensureDir(path23.dirname(lockPath));
1578
1620
  try {
1579
- const fd = await fs16.open(lockPath, "wx");
1621
+ const fd = await fs17.open(lockPath, "wx");
1580
1622
  const payload = JSON.stringify(
1581
1623
  { pid: process.pid, owner: owner ?? "unknown", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
1582
1624
  null,
1583
1625
  2
1584
1626
  );
1585
- await fs16.writeFile(fd, `${payload}
1627
+ await fs17.writeFile(fd, `${payload}
1586
1628
  `, { encoding: "utf8" });
1587
- await fs16.close(fd);
1629
+ await fs17.close(fd);
1588
1630
  return true;
1589
1631
  } catch (error) {
1590
1632
  if (error.code === "EEXIST") {
@@ -1598,9 +1640,9 @@ async function waitForLockRelease(lockPath, options = {}) {
1598
1640
  const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
1599
1641
  const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
1600
1642
  const startedAt = Date.now();
1601
- while (await fs16.pathExists(lockPath)) {
1643
+ while (await fs17.pathExists(lockPath)) {
1602
1644
  if (await isStaleLock(lockPath, staleMs)) {
1603
- await fs16.remove(lockPath);
1645
+ await fs17.remove(lockPath);
1604
1646
  break;
1605
1647
  }
1606
1648
  if (Date.now() - startedAt > timeoutMs) {
@@ -1618,7 +1660,7 @@ async function withFileLock(lockPath, task, options = {}) {
1618
1660
  const acquired = await tryAcquire(lockPath, options.owner);
1619
1661
  if (acquired) break;
1620
1662
  if (await isStaleLock(lockPath, staleMs)) {
1621
- await fs16.remove(lockPath);
1663
+ await fs17.remove(lockPath);
1622
1664
  continue;
1623
1665
  }
1624
1666
  if (Date.now() - startedAt > timeoutMs) {
@@ -1632,7 +1674,7 @@ async function withFileLock(lockPath, task, options = {}) {
1632
1674
  try {
1633
1675
  return await task();
1634
1676
  } finally {
1635
- await fs16.remove(lockPath).catch(() => {
1677
+ await fs17.remove(lockPath).catch(() => {
1636
1678
  });
1637
1679
  }
1638
1680
  }
@@ -1651,30 +1693,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
1651
1693
  "pr-template.md"
1652
1694
  ];
1653
1695
  var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
1654
- var ENGINE_MANAGED_FEATURE_PATH = path22.join(
1696
+ var ENGINE_MANAGED_FEATURE_PATH = path23.join(
1655
1697
  "features",
1656
1698
  "feature-base"
1657
1699
  );
1658
1700
  async function pruneEngineManagedDocs(docsDir) {
1659
1701
  const removed = [];
1660
1702
  for (const file of ENGINE_MANAGED_AGENT_FILES) {
1661
- const target = path22.join(docsDir, "agents", file);
1662
- if (await fs16.pathExists(target)) {
1663
- await fs16.remove(target);
1664
- removed.push(path22.relative(docsDir, target));
1703
+ const target = path23.join(docsDir, "agents", file);
1704
+ if (await fs17.pathExists(target)) {
1705
+ await fs17.remove(target);
1706
+ removed.push(path23.relative(docsDir, target));
1665
1707
  }
1666
1708
  }
1667
1709
  for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
1668
- const target = path22.join(docsDir, "agents", dir);
1669
- if (await fs16.pathExists(target)) {
1670
- await fs16.remove(target);
1671
- removed.push(path22.relative(docsDir, target));
1710
+ const target = path23.join(docsDir, "agents", dir);
1711
+ if (await fs17.pathExists(target)) {
1712
+ await fs17.remove(target);
1713
+ removed.push(path23.relative(docsDir, target));
1672
1714
  }
1673
1715
  }
1674
- const featureBasePath = path22.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1675
- if (await fs16.pathExists(featureBasePath)) {
1676
- await fs16.remove(featureBasePath);
1677
- removed.push(path22.relative(docsDir, featureBasePath));
1716
+ const featureBasePath = path23.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1717
+ if (await fs17.pathExists(featureBasePath)) {
1718
+ await fs17.remove(featureBasePath);
1719
+ removed.push(path23.relative(docsDir, featureBasePath));
1678
1720
  }
1679
1721
  return removed;
1680
1722
  }
@@ -1831,7 +1873,7 @@ ${tr(lang2, "cli", "common.canceled")}`)
1831
1873
  }
1832
1874
  async function runInit(options) {
1833
1875
  const cwd = process.cwd();
1834
- const defaultName = path22.basename(cwd);
1876
+ const defaultName = path23.basename(cwd);
1835
1877
  let projectName = options.name || defaultName;
1836
1878
  let projectType = options.type;
1837
1879
  let components = parseComponentsOption(options.components);
@@ -1842,7 +1884,7 @@ async function runInit(options) {
1842
1884
  let docsRemote = options.docsRemote;
1843
1885
  let projectRoot;
1844
1886
  const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
1845
- const targetDir = path22.resolve(cwd, options.dir || "./docs");
1887
+ const targetDir = path23.resolve(cwd, options.dir || "./docs");
1846
1888
  const skipPrompts = !!options.yes || !!options.nonInteractive;
1847
1889
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
1848
1890
  throw createCliError(
@@ -2194,8 +2236,8 @@ async function runInit(options) {
2194
2236
  await withFileLock(
2195
2237
  initLockPath,
2196
2238
  async () => {
2197
- if (await fs16.pathExists(targetDir)) {
2198
- const files = await fs16.readdir(targetDir);
2239
+ if (await fs17.pathExists(targetDir)) {
2240
+ const files = await fs17.readdir(targetDir);
2199
2241
  if (files.length > 0) {
2200
2242
  if (options.force) {
2201
2243
  } else if (options.nonInteractive) {
@@ -2231,21 +2273,21 @@ async function runInit(options) {
2231
2273
  );
2232
2274
  console.log();
2233
2275
  const templatesDir = getTemplatesDir();
2234
- const commonPath = path22.join(templatesDir, lang, "common");
2235
- if (!await fs16.pathExists(commonPath)) {
2276
+ const commonPath = path23.join(templatesDir, lang, "common");
2277
+ if (!await fs17.pathExists(commonPath)) {
2236
2278
  throw new Error(
2237
2279
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
2238
2280
  );
2239
2281
  }
2240
2282
  await copyTemplates(commonPath, targetDir);
2241
2283
  if (projectType === "multi") {
2242
- const featuresRoot = path22.join(targetDir, "features");
2284
+ const featuresRoot = path23.join(targetDir, "features");
2243
2285
  for (const component of components) {
2244
- const componentDir = path22.join(featuresRoot, component);
2245
- await fs16.ensureDir(componentDir);
2246
- const readmePath = path22.join(componentDir, "README.md");
2247
- if (!await fs16.pathExists(readmePath)) {
2248
- await fs16.writeFile(
2286
+ const componentDir = path23.join(featuresRoot, component);
2287
+ await fs17.ensureDir(componentDir);
2288
+ const readmePath = path23.join(componentDir, "README.md");
2289
+ if (!await fs17.pathExists(readmePath)) {
2290
+ await fs17.writeFile(
2249
2291
  readmePath,
2250
2292
  getComponentFeaturesReadme(lang, component),
2251
2293
  "utf-8"
@@ -2280,7 +2322,6 @@ async function runInit(options) {
2280
2322
  skills: ["code-review-excellence"],
2281
2323
  fallback: "builtin-checklist",
2282
2324
  evidenceMode: "path_required",
2283
- findings: "required",
2284
2325
  decisionEnum: ["approve", "changes_requested", "blocked"]
2285
2326
  }
2286
2327
  },
@@ -2302,8 +2343,8 @@ async function runInit(options) {
2302
2343
  config.projectRoot = projectRoot;
2303
2344
  }
2304
2345
  }
2305
- const configPath = path22.join(targetDir, ".lee-spec-kit.json");
2306
- await fs16.writeJson(configPath, config, { spaces: 2 });
2346
+ const configPath = path23.join(targetDir, ".lee-spec-kit.json");
2347
+ await fs17.writeJson(configPath, config, { spaces: 2 });
2307
2348
  console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
2308
2349
  console.log();
2309
2350
  await initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote);
@@ -2366,7 +2407,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2366
2407
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
2367
2408
  runGitOrThrow(["init"], gitWorkdir);
2368
2409
  }
2369
- const relativePath = docsRepo === "standalone" ? "." : path22.relative(cwd, targetDir);
2410
+ const relativePath = docsRepo === "standalone" ? "." : path23.relative(cwd, targetDir);
2370
2411
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2371
2412
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2372
2413
  console.log(
@@ -2423,17 +2464,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2423
2464
  }
2424
2465
  function getAncestorDirs(startDir) {
2425
2466
  const dirs = [];
2426
- let current = path22.resolve(startDir);
2467
+ let current = path23.resolve(startDir);
2427
2468
  while (true) {
2428
2469
  dirs.push(current);
2429
- const parent = path22.dirname(current);
2470
+ const parent = path23.dirname(current);
2430
2471
  if (parent === current) break;
2431
2472
  current = parent;
2432
2473
  }
2433
2474
  return dirs;
2434
2475
  }
2435
2476
  function hasWorkspaceBoundary(dir) {
2436
- return fs16.existsSync(path22.join(dir, "package.json")) || fs16.existsSync(path22.join(dir, ".git"));
2477
+ return fs17.existsSync(path23.join(dir, "package.json")) || fs17.existsSync(path23.join(dir, ".git"));
2437
2478
  }
2438
2479
  function getSearchBaseDirs(cwd) {
2439
2480
  const ancestors = getAncestorDirs(cwd);
@@ -2449,9 +2490,9 @@ function normalizeComponentKeys(value) {
2449
2490
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2450
2491
  }
2451
2492
  async function inferComponentsFromFeaturesDir(docsDir) {
2452
- const featuresPath = path22.join(docsDir, "features");
2453
- if (!await fs16.pathExists(featuresPath)) return [];
2454
- const entries = await fs16.readdir(featuresPath, { withFileTypes: true });
2493
+ const featuresPath = path23.join(docsDir, "features");
2494
+ if (!await fs17.pathExists(featuresPath)) return [];
2495
+ const entries = await fs17.readdir(featuresPath, { withFileTypes: true });
2455
2496
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
2456
2497
  (name) => !!name && name !== "feature-base" && !FEATURE_FOLDER_PATTERN.test(name)
2457
2498
  );
@@ -2460,24 +2501,24 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2460
2501
  async function getConfig(cwd) {
2461
2502
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2462
2503
  const baseDirs = [
2463
- ...explicitDocsDir ? [path22.resolve(explicitDocsDir)] : [],
2504
+ ...explicitDocsDir ? [path23.resolve(explicitDocsDir)] : [],
2464
2505
  ...getSearchBaseDirs(cwd)
2465
2506
  ];
2466
2507
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2467
2508
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2468
2509
  for (const baseDir of baseDirs) {
2469
- const resolvedBaseDir = path22.resolve(baseDir);
2510
+ const resolvedBaseDir = path23.resolve(baseDir);
2470
2511
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2471
2512
  visitedBaseDirs.add(resolvedBaseDir);
2472
- const possibleDocsDirs = [path22.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2513
+ const possibleDocsDirs = [path23.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2473
2514
  for (const docsDir of possibleDocsDirs) {
2474
- const resolvedDocsDir = path22.resolve(docsDir);
2515
+ const resolvedDocsDir = path23.resolve(docsDir);
2475
2516
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2476
2517
  visitedDocsDirs.add(resolvedDocsDir);
2477
- const configPath = path22.join(resolvedDocsDir, ".lee-spec-kit.json");
2478
- if (await fs16.pathExists(configPath)) {
2518
+ const configPath = path23.join(resolvedDocsDir, ".lee-spec-kit.json");
2519
+ if (await fs17.pathExists(configPath)) {
2479
2520
  try {
2480
- const configFile = await fs16.readJson(configPath);
2521
+ const configFile = await fs17.readJson(configPath);
2481
2522
  const projectType = normalizeProjectType(configFile.projectType);
2482
2523
  const inferredComponents = [
2483
2524
  ...normalizeComponentKeys(configFile.projectRoot),
@@ -2504,21 +2545,21 @@ async function getConfig(cwd) {
2504
2545
  } catch {
2505
2546
  }
2506
2547
  }
2507
- const agentsPath = path22.join(resolvedDocsDir, "agents");
2508
- const featuresPath = path22.join(resolvedDocsDir, "features");
2509
- if (await fs16.pathExists(agentsPath) && await fs16.pathExists(featuresPath)) {
2548
+ const agentsPath = path23.join(resolvedDocsDir, "agents");
2549
+ const featuresPath = path23.join(resolvedDocsDir, "features");
2550
+ if (await fs17.pathExists(agentsPath) && await fs17.pathExists(featuresPath)) {
2510
2551
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2511
2552
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2512
2553
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2513
2554
  const langProbeCandidates = [
2514
- path22.join(agentsPath, "custom.md"),
2515
- path22.join(agentsPath, "constitution.md"),
2516
- path22.join(agentsPath, "agents.md")
2555
+ path23.join(agentsPath, "custom.md"),
2556
+ path23.join(agentsPath, "constitution.md"),
2557
+ path23.join(agentsPath, "agents.md")
2517
2558
  ];
2518
2559
  let lang = "en";
2519
2560
  for (const candidate of langProbeCandidates) {
2520
- if (!await fs16.pathExists(candidate)) continue;
2521
- const content = await fs16.readFile(candidate, "utf-8");
2561
+ if (!await fs17.pathExists(candidate)) continue;
2562
+ const content = await fs17.readFile(candidate, "utf-8");
2522
2563
  if (/[가-힣]/.test(content)) {
2523
2564
  lang = "ko";
2524
2565
  break;
@@ -2553,7 +2594,7 @@ function sanitizeTasksForLocal(content, lang) {
2553
2594
  )) {
2554
2595
  continue;
2555
2596
  }
2556
- if (/^\s*-\s*\*\*(Pre-PR Findings|Pre-PR Evidence|PR 전 리뷰 Findings|PR 전 리뷰 Evidence)\*\*\s*:/.test(
2597
+ if (/^\s*-\s*\*\*(Pre-PR Evidence|PR 전 리뷰 Evidence)\*\*\s*:/.test(
2557
2598
  line
2558
2599
  )) {
2559
2600
  continue;
@@ -2567,8 +2608,6 @@ function sanitizeTasksForLocal(content, lang) {
2567
2608
  if (/^\s*-\s*(예|값)\s*:/.test(line)) continue;
2568
2609
  if (/^\s*-\s*Mark\s+`?Done`?/i.test(line)) continue;
2569
2610
  if (/^\s*-\s*사전 코드리뷰 완료 후/.test(line)) continue;
2570
- if (/^\s*-\s*Update with final findings counts from pre-PR review/i.test(line))
2571
- continue;
2572
2611
  if (/^\s*-\s*Record your key review decision/i.test(line)) continue;
2573
2612
  if (/^\s*-\s*사전 리뷰 주요 판단 근거를/.test(line)) continue;
2574
2613
  if (/^\s*-\s*Example:\s*review note link/i.test(line)) continue;
@@ -2581,18 +2620,18 @@ function sanitizeTasksForLocal(content, lang) {
2581
2620
  return normalizeTrailingBlankLines(next);
2582
2621
  }
2583
2622
  async function patchMarkdownIfExists(filePath, transform) {
2584
- if (!await fs16.pathExists(filePath)) return;
2585
- const content = await fs16.readFile(filePath, "utf-8");
2586
- await fs16.writeFile(filePath, transform(content), "utf-8");
2623
+ if (!await fs17.pathExists(filePath)) return;
2624
+ const content = await fs17.readFile(filePath, "utf-8");
2625
+ await fs17.writeFile(filePath, transform(content), "utf-8");
2587
2626
  }
2588
2627
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
2589
- await patchMarkdownIfExists(path22.join(featureDir, "spec.md"), sanitizeSpecForLocal);
2628
+ await patchMarkdownIfExists(path23.join(featureDir, "spec.md"), sanitizeSpecForLocal);
2590
2629
  await patchMarkdownIfExists(
2591
- path22.join(featureDir, "tasks.md"),
2630
+ path23.join(featureDir, "tasks.md"),
2592
2631
  (content) => sanitizeTasksForLocal(content, lang)
2593
2632
  );
2594
- await fs16.remove(path22.join(featureDir, "issue.md"));
2595
- await fs16.remove(path22.join(featureDir, "pr.md"));
2633
+ await fs17.remove(path23.join(featureDir, "issue.md"));
2634
+ await fs17.remove(path23.join(featureDir, "pr.md"));
2596
2635
  }
2597
2636
 
2598
2637
  // src/commands/feature.ts
@@ -2739,29 +2778,29 @@ async function runFeature(name, options) {
2739
2778
  }
2740
2779
  let featuresDir;
2741
2780
  if (projectType === "multi") {
2742
- featuresDir = path22.join(docsDir, "features", component);
2781
+ featuresDir = path23.join(docsDir, "features", component);
2743
2782
  } else {
2744
- featuresDir = path22.join(docsDir, "features");
2783
+ featuresDir = path23.join(docsDir, "features");
2745
2784
  }
2746
2785
  const featureFolderName = `${featureId}-${name}`;
2747
- const featureDir = path22.join(featuresDir, featureFolderName);
2748
- if (await fs16.pathExists(featureDir)) {
2786
+ const featureDir = path23.join(featuresDir, featureFolderName);
2787
+ if (await fs17.pathExists(featureDir)) {
2749
2788
  throw createCliError(
2750
2789
  "INVALID_ARGUMENT",
2751
2790
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
2752
2791
  );
2753
2792
  }
2754
- const featureBasePath = path22.join(
2793
+ const featureBasePath = path23.join(
2755
2794
  getTemplatesDir(),
2756
2795
  lang,
2757
2796
  "common",
2758
2797
  "features",
2759
2798
  "feature-base"
2760
2799
  );
2761
- if (!await fs16.pathExists(featureBasePath)) {
2800
+ if (!await fs17.pathExists(featureBasePath)) {
2762
2801
  throw createCliError("DOCS_NOT_FOUND", tr(lang, "cli", "feature.baseNotFound"));
2763
2802
  }
2764
- await fs16.copy(featureBasePath, featureDir);
2803
+ await fs17.copy(featureBasePath, featureDir);
2765
2804
  const idNumber = featureId.replace("F", "");
2766
2805
  const repoName = projectType === "multi" ? `{{projectName}}-${component}` : "{{projectName}}";
2767
2806
  const replacements = {
@@ -2810,7 +2849,7 @@ async function runFeature(name, options) {
2810
2849
  featureName: name,
2811
2850
  component: projectType === "multi" ? component : void 0,
2812
2851
  featurePath: featureDir,
2813
- featurePathFromDocs: path22.relative(docsDir, featureDir)
2852
+ featurePathFromDocs: path23.relative(docsDir, featureDir)
2814
2853
  };
2815
2854
  },
2816
2855
  { owner: "feature" }
@@ -2819,9 +2858,9 @@ async function runFeature(name, options) {
2819
2858
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
2820
2859
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2821
2860
  const candidates = [
2822
- ...explicitDocsDir ? [path22.resolve(explicitDocsDir)] : [],
2823
- path22.resolve(cwd, "docs"),
2824
- path22.resolve(cwd)
2861
+ ...explicitDocsDir ? [path23.resolve(explicitDocsDir)] : [],
2862
+ path23.resolve(cwd, "docs"),
2863
+ path23.resolve(cwd)
2825
2864
  ];
2826
2865
  const endAt = Date.now() + timeoutMs;
2827
2866
  while (Date.now() < endAt) {
@@ -2832,7 +2871,7 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
2832
2871
  const initLockPath = getInitLockPath(dir);
2833
2872
  const docsLockPath = getDocsLockPath(dir);
2834
2873
  for (const lockPath of [initLockPath, docsLockPath]) {
2835
- if (await fs16.pathExists(lockPath)) {
2874
+ if (await fs17.pathExists(lockPath)) {
2836
2875
  sawLock = true;
2837
2876
  await waitForLockRelease(lockPath, {
2838
2877
  timeoutMs: Math.max(200, endAt - Date.now()),
@@ -2848,17 +2887,17 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
2848
2887
  return getConfig(cwd);
2849
2888
  }
2850
2889
  async function getNextFeatureId(docsDir, projectType, components) {
2851
- const featuresDir = path22.join(docsDir, "features");
2890
+ const featuresDir = path23.join(docsDir, "features");
2852
2891
  let max = 0;
2853
2892
  const scanDirs = [];
2854
2893
  if (projectType === "multi") {
2855
- scanDirs.push(...components.map((component) => path22.join(featuresDir, component)));
2894
+ scanDirs.push(...components.map((component) => path23.join(featuresDir, component)));
2856
2895
  } else {
2857
2896
  scanDirs.push(featuresDir);
2858
2897
  }
2859
2898
  for (const dir of scanDirs) {
2860
- if (!await fs16.pathExists(dir)) continue;
2861
- const entries = await fs16.readdir(dir, { withFileTypes: true });
2899
+ if (!await fs17.pathExists(dir)) continue;
2900
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
2862
2901
  for (const entry of entries) {
2863
2902
  if (!entry.isDirectory()) continue;
2864
2903
  const match = entry.name.match(/^F(\d+)-/);
@@ -3000,7 +3039,6 @@ function resolvePrePrReviewPolicy(workflow) {
3000
3039
  skills: configuredSkills.length > 0 ? configuredSkills : DEFAULT_PRE_PR_REVIEW_SKILLS,
3001
3040
  fallback: configured?.fallback === "builtin-checklist" ? configured.fallback : "builtin-checklist",
3002
3041
  evidenceMode: configured?.evidenceMode === "any" ? "any" : "path_required",
3003
- findings: configured?.findings === "optional" ? "optional" : "required",
3004
3042
  decisionEnum: configuredDecisionEnum.length > 0 ? configuredDecisionEnum : DEFAULT_PRE_PR_DECISION_ENUM
3005
3043
  };
3006
3044
  }
@@ -3029,9 +3067,6 @@ function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
3029
3067
  if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
3030
3068
  return false;
3031
3069
  }
3032
- if (prePrReviewPolicy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
3033
- return false;
3034
- }
3035
3070
  if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
3036
3071
  return false;
3037
3072
  }
@@ -3101,8 +3136,8 @@ function resolveProjectCommitTopic(feature) {
3101
3136
  }
3102
3137
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
3103
3138
  if (!projectGitCwd) return null;
3104
- const normalized = path22.resolve(projectGitCwd);
3105
- const marker = `${path22.sep}.worktrees${path22.sep}`;
3139
+ const normalized = path23.resolve(projectGitCwd);
3140
+ const marker = `${path23.sep}.worktrees${path23.sep}`;
3106
3141
  const markerIndex = normalized.lastIndexOf(marker);
3107
3142
  if (markerIndex <= 0) return null;
3108
3143
  const projectRoot = normalized.slice(0, markerIndex);
@@ -3146,7 +3181,7 @@ function toTaskKey(rawTitle) {
3146
3181
  function countDoneTransitionsInLatestTasksCommit(feature) {
3147
3182
  const docsGitCwd = feature.git.docsGitCwd;
3148
3183
  const tasksRelativePath = normalizeGitRelativePath(
3149
- path22.join(feature.docs.featurePathFromDocs, "tasks.md")
3184
+ path23.join(feature.docs.featurePathFromDocs, "tasks.md")
3150
3185
  );
3151
3186
  const diff = readGitText(docsGitCwd, [
3152
3187
  "diff",
@@ -3219,7 +3254,7 @@ function checkTaskCommitGate(feature) {
3219
3254
  return { pass: true };
3220
3255
  }
3221
3256
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
3222
- const relativeDocsDir = path22.relative(projectGitCwd, feature.git.docsGitCwd);
3257
+ const relativeDocsDir = path23.relative(projectGitCwd, feature.git.docsGitCwd);
3223
3258
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
3224
3259
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
3225
3260
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -3345,6 +3380,7 @@ function getStepDefinitions(lang, workflow) {
3345
3380
  {
3346
3381
  type: "instruction",
3347
3382
  category: "tasks_write",
3383
+ uiDetailKey: "context.actionDetail.tasksWriteCreate",
3348
3384
  message: tr(lang, "messages", "tasksCreate")
3349
3385
  }
3350
3386
  ];
@@ -3354,6 +3390,7 @@ function getStepDefinitions(lang, workflow) {
3354
3390
  {
3355
3391
  type: "instruction",
3356
3392
  category: "tasks_write",
3393
+ uiDetailKey: "context.actionDetail.tasksWriteNeedAtLeastOne",
3357
3394
  message: tr(lang, "messages", "tasksNeedAtLeastOne")
3358
3395
  }
3359
3396
  ];
@@ -3363,6 +3400,7 @@ function getStepDefinitions(lang, workflow) {
3363
3400
  {
3364
3401
  type: "instruction",
3365
3402
  category: "tasks_write",
3403
+ uiDetailKey: "context.actionDetail.tasksWriteImprove",
3366
3404
  message: tr(lang, "messages", "tasksImprove")
3367
3405
  }
3368
3406
  ];
@@ -3381,6 +3419,7 @@ function getStepDefinitions(lang, workflow) {
3381
3419
  {
3382
3420
  type: "instruction",
3383
3421
  category: "tasks_write",
3422
+ uiDetailKey: "context.actionDetail.tasksWriteImprove",
3384
3423
  message: tr(lang, "messages", "tasksImprove")
3385
3424
  }
3386
3425
  ];
@@ -3449,6 +3488,7 @@ function getStepDefinitions(lang, workflow) {
3449
3488
  type: "instruction",
3450
3489
  category: "issue_create",
3451
3490
  requiresUserCheck: true,
3491
+ uiDetailKey: "context.actionDetail.issueCreateAndWrite",
3452
3492
  message: tr(lang, "messages", "issueCreateAndWrite", {
3453
3493
  featureRef: f.id || f.folderName
3454
3494
  })
@@ -3461,6 +3501,7 @@ function getStepDefinitions(lang, workflow) {
3461
3501
  type: "instruction",
3462
3502
  category: "issue_create",
3463
3503
  requiresUserCheck: true,
3504
+ uiDetailKey: "context.actionDetail.issueCreateFromDoc",
3464
3505
  message: tr(lang, "messages", "issueCreateFromDoc", {
3465
3506
  featureRef: f.id || f.folderName
3466
3507
  })
@@ -3472,6 +3513,7 @@ function getStepDefinitions(lang, workflow) {
3472
3513
  type: "instruction",
3473
3514
  category: "issue_create",
3474
3515
  requiresUserCheck: true,
3516
+ uiDetailKey: "context.actionDetail.issueCreatePrepareFromDoc",
3475
3517
  message: tr(lang, "messages", "issuePrepareFromDoc", {
3476
3518
  featureRef: f.id || f.folderName
3477
3519
  })
@@ -3616,6 +3658,7 @@ function getStepDefinitions(lang, workflow) {
3616
3658
  type: "instruction",
3617
3659
  category: "pr_metadata_migrate",
3618
3660
  requiresUserCheck: true,
3661
+ uiDetailKey: "context.actionDetail.prMetadataMigratePrFields",
3619
3662
  message: tr(lang, "messages", "prLegacyAsk")
3620
3663
  });
3621
3664
  }
@@ -3851,6 +3894,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3851
3894
  type: "instruction",
3852
3895
  category: "pr_metadata_migrate",
3853
3896
  requiresUserCheck: true,
3897
+ uiDetailKey: "context.actionDetail.prMetadataMigratePrePrReviewField",
3854
3898
  message: tr(lang, "messages", "prePrReviewFieldMissing")
3855
3899
  }
3856
3900
  ];
@@ -3888,6 +3932,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3888
3932
  type: "instruction",
3889
3933
  category: "pr_metadata_migrate",
3890
3934
  requiresUserCheck: true,
3935
+ uiDetailKey: "context.actionDetail.prMetadataMigratePrFields",
3891
3936
  message: tr(lang, "messages", "prLegacyAsk")
3892
3937
  }
3893
3938
  ];
@@ -3898,6 +3943,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3898
3943
  type: "instruction",
3899
3944
  category: "pr_create",
3900
3945
  requiresUserCheck: true,
3946
+ uiDetailKey: "context.actionDetail.prCreateRequiredSequence",
3901
3947
  message: tr(lang, "messages", "prCreateRequiredSequence", {
3902
3948
  featureRef: f.id || f.folderName
3903
3949
  })
@@ -3910,6 +3956,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3910
3956
  type: "instruction",
3911
3957
  category: "pr_create",
3912
3958
  requiresUserCheck: true,
3959
+ uiDetailKey: "context.actionDetail.prCreateExecuteFromDoc",
3913
3960
  message: tr(lang, "messages", "prCreateExecuteFromDoc", {
3914
3961
  featureRef: f.id || f.folderName
3915
3962
  })
@@ -3921,6 +3968,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3921
3968
  type: "instruction",
3922
3969
  category: "pr_create",
3923
3970
  requiresUserCheck: true,
3971
+ uiDetailKey: "context.actionDetail.prCreatePrepareFromDoc",
3924
3972
  message: tr(lang, "messages", "prCreatePrepareFromDoc", {
3925
3973
  featureRef: f.id || f.folderName
3926
3974
  })
@@ -3944,6 +3992,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3944
3992
  type: "instruction",
3945
3993
  category: "pr_status_update",
3946
3994
  requiresUserCheck: true,
3995
+ uiDetailKey: "context.actionDetail.prStatusUpdateSetReview",
3947
3996
  message: tr(lang, "messages", "prFillStatus")
3948
3997
  }
3949
3998
  ];
@@ -3955,6 +4004,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3955
4004
  type: "instruction",
3956
4005
  category: "pr_status_update",
3957
4006
  requiresUserCheck: true,
4007
+ uiDetailKey: "context.actionDetail.prStatusUpdateSyncApproved",
3958
4008
  message: tr(lang, "messages", "prReviewMergedSyncStatus")
3959
4009
  }
3960
4010
  ];
@@ -3965,6 +4015,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3965
4015
  type: "instruction",
3966
4016
  category: "code_review",
3967
4017
  requiresUserCheck: true,
4018
+ uiDetailKey: "context.actionDetail.codeReviewNeedEvidenceField",
3968
4019
  message: tr(lang, "messages", "prReviewEvidenceFieldMissing")
3969
4020
  }
3970
4021
  ];
@@ -3975,6 +4026,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3975
4026
  type: "instruction",
3976
4027
  category: "code_review",
3977
4028
  requiresUserCheck: true,
4029
+ uiDetailKey: "context.actionDetail.codeReviewNeedEvidence",
3978
4030
  message: tr(lang, "messages", "prReviewEvidenceMissing")
3979
4031
  }
3980
4032
  ];
@@ -3985,6 +4037,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3985
4037
  type: "instruction",
3986
4038
  category: "code_review",
3987
4039
  requiresUserCheck: true,
4040
+ uiDetailKey: "context.actionDetail.codeReviewNeedDecisionField",
3988
4041
  message: tr(lang, "messages", "prReviewDecisionFieldMissing")
3989
4042
  }
3990
4043
  ];
@@ -3995,6 +4048,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3995
4048
  type: "instruction",
3996
4049
  category: "code_review",
3997
4050
  requiresUserCheck: true,
4051
+ uiDetailKey: "context.actionDetail.codeReviewNeedDecision",
3998
4052
  message: tr(lang, "messages", "prReviewDecisionMissing")
3999
4053
  }
4000
4054
  ];
@@ -4006,6 +4060,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4006
4060
  type: "instruction",
4007
4061
  category: "code_review",
4008
4062
  requiresUserCheck: true,
4063
+ uiDetailKey: "context.actionDetail.codeReviewResolve",
4009
4064
  message: tr(lang, "messages", "prReviewResolve")
4010
4065
  }
4011
4066
  ];
@@ -4014,6 +4069,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4014
4069
  type: "instruction",
4015
4070
  category: "code_review",
4016
4071
  requiresUserCheck: true,
4072
+ uiDetailKey: "context.actionDetail.codeReviewNeedProjectRoot",
4017
4073
  message: tr(lang, "messages", "standaloneNeedsProjectRoot")
4018
4074
  });
4019
4075
  } else if ((f.git.projectBranchAhead || 0) > 0) {
@@ -4037,6 +4093,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4037
4093
  type: "instruction",
4038
4094
  category: "code_review",
4039
4095
  requiresUserCheck: true,
4096
+ uiDetailKey: "context.actionDetail.codeReviewRemoteBlocked",
4040
4097
  message: tr(lang, "messages", "prReviewRemoteBlocked", {
4041
4098
  reasons: reasons.join("; ")
4042
4099
  })
@@ -4058,6 +4115,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4058
4115
  type: "instruction",
4059
4116
  category: "code_review",
4060
4117
  requiresUserCheck: true,
4118
+ uiDetailKey: "context.actionDetail.codeReviewMergeAfterOk",
4061
4119
  message: tr(lang, "messages", "prReviewMerge", {
4062
4120
  featureRef: f.id || f.folderName
4063
4121
  })
@@ -4069,6 +4127,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4069
4127
  {
4070
4128
  type: "instruction",
4071
4129
  category: "code_review",
4130
+ uiDetailKey: "context.actionDetail.codeReviewRequestReview",
4072
4131
  message: tr(lang, "messages", "prRequestReview")
4073
4132
  }
4074
4133
  ];
@@ -4372,7 +4431,7 @@ function getGitTopLevel(cwd) {
4372
4431
  }
4373
4432
  function listGitWorktrees(cwd) {
4374
4433
  const topLevel = getGitTopLevel(cwd) || cwd;
4375
- const cacheKey = path22.resolve(topLevel);
4434
+ const cacheKey = path23.resolve(topLevel);
4376
4435
  const cached = GIT_WORKTREE_CACHE.get(cacheKey);
4377
4436
  if (cached) return cached;
4378
4437
  try {
@@ -4531,19 +4590,6 @@ function parsePrePrReviewStatus(value) {
4531
4590
  if (/^pending$/i.test(trimmed)) return "Pending";
4532
4591
  return void 0;
4533
4592
  }
4534
- function parseReviewFindings(value) {
4535
- if (!value) return void 0;
4536
- const trimmed = value.trim();
4537
- if (!trimmed || trimmed.includes("|")) return void 0;
4538
- const majorMatch = trimmed.match(/\bmajor\s*[:=]\s*(\d+)\b/i);
4539
- const minorMatch = trimmed.match(/\bminor\s*[:=]\s*(\d+)\b/i);
4540
- if (!majorMatch || !minorMatch) return void 0;
4541
- const major = Number(majorMatch[1]);
4542
- const minor = Number(minorMatch[1]);
4543
- if (!Number.isInteger(major) || !Number.isInteger(minor)) return void 0;
4544
- if (major < 0 || minor < 0) return void 0;
4545
- return { major, minor };
4546
- }
4547
4593
  function isPlaceholderReviewEvidence(value) {
4548
4594
  if (!value) return true;
4549
4595
  const trimmed = value.trim();
@@ -4598,15 +4644,15 @@ async function isPrePrEvidenceProvided(rawValue, policy, context) {
4598
4644
  if (!evidencePath) return false;
4599
4645
  if (/^https?:\/\//i.test(evidencePath)) return false;
4600
4646
  const candidates = /* @__PURE__ */ new Set();
4601
- if (path22.isAbsolute(evidencePath)) {
4602
- candidates.add(path22.resolve(evidencePath));
4647
+ if (path23.isAbsolute(evidencePath)) {
4648
+ candidates.add(path23.resolve(evidencePath));
4603
4649
  } else {
4604
- candidates.add(path22.resolve(context.featurePath, evidencePath));
4605
- candidates.add(path22.resolve(context.docsDir, evidencePath));
4606
- candidates.add(path22.resolve(path22.dirname(context.docsDir), evidencePath));
4650
+ candidates.add(path23.resolve(context.featurePath, evidencePath));
4651
+ candidates.add(path23.resolve(context.docsDir, evidencePath));
4652
+ candidates.add(path23.resolve(path23.dirname(context.docsDir), evidencePath));
4607
4653
  }
4608
4654
  for (const candidate of candidates) {
4609
- if (await fs16.pathExists(candidate)) return true;
4655
+ if (await fs17.pathExists(candidate)) return true;
4610
4656
  }
4611
4657
  return false;
4612
4658
  }
@@ -4627,13 +4673,13 @@ function parsePrLink(value) {
4627
4673
  return trimmed;
4628
4674
  }
4629
4675
  function normalizeGitPath(value) {
4630
- return value.split(path22.sep).join("/");
4676
+ return value.split(path23.sep).join("/");
4631
4677
  }
4632
4678
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
4633
- const relativeDocsDir = path22.relative(projectGitCwd, docsDir);
4679
+ const relativeDocsDir = path23.relative(projectGitCwd, docsDir);
4634
4680
  if (!relativeDocsDir) return [];
4635
- if (path22.isAbsolute(relativeDocsDir)) return [];
4636
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path22.sep}`)) {
4681
+ if (path23.isAbsolute(relativeDocsDir)) return [];
4682
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path23.sep}`)) {
4637
4683
  return [];
4638
4684
  }
4639
4685
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(/\/+$/, "");
@@ -4812,10 +4858,10 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
4812
4858
  const normalizedCandidates = uniqueNormalizedPaths(
4813
4859
  candidates.map((candidate) => {
4814
4860
  if (!candidate) return "";
4815
- if (!path22.isAbsolute(candidate)) return candidate;
4816
- const relative = path22.relative(projectGitCwd, candidate);
4861
+ if (!path23.isAbsolute(candidate)) return candidate;
4862
+ const relative = path23.relative(projectGitCwd, candidate);
4817
4863
  if (!relative) return "";
4818
- if (relative === ".." || relative.startsWith(`..${path22.sep}`)) return "";
4864
+ if (relative === ".." || relative.startsWith(`..${path23.sep}`)) return "";
4819
4865
  return relative;
4820
4866
  }).filter(Boolean)
4821
4867
  );
@@ -4828,7 +4874,7 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
4828
4874
  if (cached) return [...cached];
4829
4875
  const existing = [];
4830
4876
  for (const candidate of normalizedCandidates) {
4831
- if (await fs16.pathExists(path22.join(projectGitCwd, candidate))) {
4877
+ if (await fs17.pathExists(path23.join(projectGitCwd, candidate))) {
4832
4878
  existing.push(candidate);
4833
4879
  }
4834
4880
  }
@@ -4902,9 +4948,6 @@ function isPrePrReviewSatisfied2(feature, policy) {
4902
4948
  if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
4903
4949
  return false;
4904
4950
  }
4905
- if (policy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
4906
- return false;
4907
- }
4908
4951
  if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
4909
4952
  return false;
4910
4953
  }
@@ -4917,20 +4960,20 @@ async function parseFeature(featurePath, type, context, options) {
4917
4960
  const lang = options.lang;
4918
4961
  const workflowPolicy = resolveWorkflowPolicy(options.workflow);
4919
4962
  const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
4920
- const folderName = path22.basename(featurePath);
4963
+ const folderName = path23.basename(featurePath);
4921
4964
  const match = folderName.match(/^(F\d+)-(.+)$/);
4922
4965
  const id = match?.[1];
4923
4966
  const slug = match?.[2] || folderName;
4924
- const specPath = path22.join(featurePath, "spec.md");
4925
- const planPath = path22.join(featurePath, "plan.md");
4926
- const tasksPath = path22.join(featurePath, "tasks.md");
4927
- const issueDocPath = path22.join(featurePath, "issue.md");
4928
- const prDocPath = path22.join(featurePath, "pr.md");
4967
+ const specPath = path23.join(featurePath, "spec.md");
4968
+ const planPath = path23.join(featurePath, "plan.md");
4969
+ const tasksPath = path23.join(featurePath, "tasks.md");
4970
+ const issueDocPath = path23.join(featurePath, "issue.md");
4971
+ const prDocPath = path23.join(featurePath, "pr.md");
4929
4972
  let specStatus;
4930
4973
  let issueNumber;
4931
- const specExists = await fs16.pathExists(specPath);
4974
+ const specExists = await fs17.pathExists(specPath);
4932
4975
  if (specExists) {
4933
- const content = await fs16.readFile(specPath, "utf-8");
4976
+ const content = await fs17.readFile(specPath, "utf-8");
4934
4977
  const statusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
4935
4978
  specStatus = parseDocStatus(statusValue);
4936
4979
  const issueValue = extractFirstSpecValue(content, ["\uC774\uC288 \uBC88\uD638", "Issue Number", "Issue"]);
@@ -4961,13 +5004,13 @@ async function parseFeature(featurePath, type, context, options) {
4961
5004
  }
4962
5005
  }
4963
5006
  let planStatus;
4964
- const planExists = await fs16.pathExists(planPath);
5007
+ const planExists = await fs17.pathExists(planPath);
4965
5008
  if (planExists) {
4966
- const content = await fs16.readFile(planPath, "utf-8");
5009
+ const content = await fs17.readFile(planPath, "utf-8");
4967
5010
  const statusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
4968
5011
  planStatus = parseDocStatus(statusValue);
4969
5012
  }
4970
- const tasksExists = await fs16.pathExists(tasksPath);
5013
+ const tasksExists = await fs17.pathExists(tasksPath);
4971
5014
  const tasksSummary = { total: 0, todo: 0, doing: 0, done: 0 };
4972
5015
  let activeTask;
4973
5016
  let lastDoneTask;
@@ -4976,14 +5019,11 @@ async function parseFeature(featurePath, type, context, options) {
4976
5019
  let tasksDocStatusFieldExists = false;
4977
5020
  let completionChecklist;
4978
5021
  let prePrReviewStatus;
4979
- let prePrFindings;
4980
- let prePrFindingsProvided = false;
4981
5022
  let prePrEvidence;
4982
5023
  let prePrEvidenceProvided = false;
4983
5024
  let prePrDecision;
4984
5025
  let prePrDecisionOutcome;
4985
5026
  let prePrDecisionProvided = false;
4986
- let prReviewFindings;
4987
5027
  let prReviewEvidence;
4988
5028
  let prReviewEvidenceProvided = false;
4989
5029
  let prReviewDecision;
@@ -5001,14 +5041,12 @@ async function parseFeature(featurePath, type, context, options) {
5001
5041
  let prDocPrFieldExists = false;
5002
5042
  let prDocReviewStatusFieldExists = false;
5003
5043
  let prePrReviewFieldExists = false;
5004
- let prePrFindingsFieldExists = false;
5005
5044
  let prePrEvidenceFieldExists = false;
5006
5045
  let prePrDecisionFieldExists = false;
5007
- let prReviewFindingsFieldExists = false;
5008
5046
  let prReviewEvidenceFieldExists = false;
5009
5047
  let prReviewDecisionFieldExists = false;
5010
5048
  if (tasksExists) {
5011
- const content = await fs16.readFile(tasksPath, "utf-8");
5049
+ const content = await fs17.readFile(tasksPath, "utf-8");
5012
5050
  const {
5013
5051
  summary,
5014
5052
  activeTask: active,
@@ -5047,16 +5085,6 @@ async function parseFeature(featurePath, type, context, options) {
5047
5085
  "Pre-PR Review"
5048
5086
  ]);
5049
5087
  prePrReviewStatus = parsePrePrReviewStatus(prePrReviewValue);
5050
- const prePrFindingsValue = extractFirstSpecValue(content, [
5051
- "PR \uC804 \uB9AC\uBDF0 Findings",
5052
- "Pre-PR Findings"
5053
- ]);
5054
- prePrFindingsFieldExists = hasAnySpecKey(content, [
5055
- "PR \uC804 \uB9AC\uBDF0 Findings",
5056
- "Pre-PR Findings"
5057
- ]);
5058
- prePrFindings = parseReviewFindings(prePrFindingsValue);
5059
- prePrFindingsProvided = !!prePrFindings;
5060
5088
  const prePrEvidenceValue = extractFirstSpecValue(content, [
5061
5089
  "PR \uC804 \uB9AC\uBDF0 Evidence",
5062
5090
  "Pre-PR Evidence"
@@ -5082,15 +5110,6 @@ async function parseFeature(featurePath, type, context, options) {
5082
5110
  prePrDecision = prePrDecisionValue?.trim();
5083
5111
  prePrDecisionOutcome = parsePrePrDecisionOutcome(prePrDecisionValue);
5084
5112
  prePrDecisionProvided = !isPlaceholderReviewEvidence(prePrDecisionValue) && hasStructuredReviewDecision(prePrDecisionValue) && !!prePrDecisionOutcome && prePrReviewPolicy.decisionEnum.includes(prePrDecisionOutcome);
5085
- const prReviewFindingsValue = extractFirstSpecValue(content, [
5086
- "PR \uB9AC\uBDF0 Findings",
5087
- "PR Review Findings"
5088
- ]);
5089
- prReviewFindingsFieldExists = hasAnySpecKey(content, [
5090
- "PR \uB9AC\uBDF0 Findings",
5091
- "PR Review Findings"
5092
- ]);
5093
- prReviewFindings = parseReviewFindings(prReviewFindingsValue);
5094
5113
  const prReviewEvidenceValue = extractFirstSpecValue(content, [
5095
5114
  "PR \uB9AC\uBDF0 Evidence",
5096
5115
  "PR Review Evidence"
@@ -5133,9 +5152,9 @@ async function parseFeature(featurePath, type, context, options) {
5133
5152
  }
5134
5153
  }
5135
5154
  }
5136
- const issueDocExists = await fs16.pathExists(issueDocPath);
5155
+ const issueDocExists = await fs17.pathExists(issueDocPath);
5137
5156
  if (issueDocExists) {
5138
- const content = await fs16.readFile(issueDocPath, "utf-8");
5157
+ const content = await fs17.readFile(issueDocPath, "utf-8");
5139
5158
  const issueDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
5140
5159
  issueDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
5141
5160
  issueDocStatus = parseWorkflowDocStatus(issueDocStatusValue);
@@ -5145,9 +5164,9 @@ async function parseFeature(featurePath, type, context, options) {
5145
5164
  "Issue"
5146
5165
  ]);
5147
5166
  }
5148
- const prDocExists = await fs16.pathExists(prDocPath);
5167
+ const prDocExists = await fs17.pathExists(prDocPath);
5149
5168
  if (prDocExists) {
5150
- const content = await fs16.readFile(prDocPath, "utf-8");
5169
+ const content = await fs17.readFile(prDocPath, "utf-8");
5151
5170
  const prDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
5152
5171
  prDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
5153
5172
  prDocStatus = parseWorkflowDocStatus(prDocStatusValue);
@@ -5167,7 +5186,7 @@ async function parseFeature(featurePath, type, context, options) {
5167
5186
  slug,
5168
5187
  folderName
5169
5188
  );
5170
- const relativeFeaturePathFromDocs = path22.relative(context.docsDir, featurePath);
5189
+ const relativeFeaturePathFromDocs = path23.relative(context.docsDir, featurePath);
5171
5190
  const normalizedFeaturePathFromDocs = normalizeGitPath(relativeFeaturePathFromDocs);
5172
5191
  const docsPathIgnored = typeof context.docsPathIgnored === "boolean" ? context.docsPathIgnored : isGitPathIgnored(context.docsGitCwd, normalizedFeaturePathFromDocs);
5173
5192
  let docsHasUncommittedChanges = typeof context.docsHasUncommittedChanges === "boolean" ? context.docsHasUncommittedChanges : false;
@@ -5256,9 +5275,6 @@ async function parseFeature(featurePath, type, context, options) {
5256
5275
  if (tasksExists && prePrReviewPolicy.enabled && !prePrReviewFieldExists) {
5257
5276
  warnings.push(tr(lang, "warnings", "legacyTasksPrePrReviewField"));
5258
5277
  }
5259
- if (tasksExists && prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && !prePrFindingsFieldExists) {
5260
- warnings.push(tr(lang, "warnings", "legacyTasksPrePrFindingsField"));
5261
- }
5262
5278
  if (tasksExists && prePrReviewPolicy.enabled && !prePrEvidenceFieldExists) {
5263
5279
  warnings.push(tr(lang, "warnings", "legacyTasksPrePrEvidenceField"));
5264
5280
  }
@@ -5303,13 +5319,11 @@ async function parseFeature(featurePath, type, context, options) {
5303
5319
  {
5304
5320
  docs: {
5305
5321
  prePrReviewFieldExists,
5306
- prePrFindingsFieldExists,
5307
5322
  prePrEvidenceFieldExists,
5308
5323
  prePrDecisionFieldExists
5309
5324
  },
5310
5325
  prePrReview: {
5311
5326
  status: prePrReviewStatus,
5312
- findingsProvided: prePrFindingsProvided,
5313
5327
  evidenceProvided: prePrEvidenceProvided,
5314
5328
  decisionOutcome: prePrDecisionOutcome,
5315
5329
  decisionProvided: prePrDecisionProvided
@@ -5352,8 +5366,6 @@ async function parseFeature(featurePath, type, context, options) {
5352
5366
  warnings.push(tr(lang, "warnings", "workflowPrePrReviewMissing"));
5353
5367
  } else if (prePrReviewStatus !== "Done") {
5354
5368
  warnings.push(tr(lang, "warnings", "workflowPrePrReviewNotDone"));
5355
- } else if (prePrReviewPolicy.findings === "required" && (!prePrFindingsFieldExists || !prePrFindingsProvided)) {
5356
- warnings.push(tr(lang, "warnings", "workflowPrePrFindingsMissing"));
5357
5369
  } else if (!prePrEvidenceFieldExists || !prePrEvidenceProvided) {
5358
5370
  warnings.push(tr(lang, "warnings", "workflowPrePrEvidenceMissing"));
5359
5371
  } else if (!prePrDecisionFieldExists || !prePrDecisionProvided) {
@@ -5388,8 +5400,6 @@ async function parseFeature(featurePath, type, context, options) {
5388
5400
  completionChecklist,
5389
5401
  prePrReview: {
5390
5402
  status: prePrReviewStatus,
5391
- findings: prePrFindings,
5392
- findingsProvided: prePrFindingsProvided,
5393
5403
  evidence: prePrEvidence,
5394
5404
  evidenceProvided: prePrEvidenceProvided,
5395
5405
  decision: prePrDecision,
@@ -5397,7 +5407,6 @@ async function parseFeature(featurePath, type, context, options) {
5397
5407
  decisionProvided: prePrDecisionProvided
5398
5408
  },
5399
5409
  prReview: {
5400
- findings: prReviewFindings,
5401
5410
  evidence: prReviewEvidence,
5402
5411
  evidenceProvided: prReviewEvidenceProvided,
5403
5412
  decision: prReviewDecision,
@@ -5437,10 +5446,8 @@ async function parseFeature(featurePath, type, context, options) {
5437
5446
  prFieldExists,
5438
5447
  prStatusFieldExists,
5439
5448
  prePrReviewFieldExists,
5440
- prePrFindingsFieldExists,
5441
5449
  prePrEvidenceFieldExists,
5442
5450
  prePrDecisionFieldExists,
5443
- prReviewFindingsFieldExists,
5444
5451
  prReviewEvidenceFieldExists,
5445
5452
  prReviewDecisionFieldExists
5446
5453
  }
@@ -5456,7 +5463,7 @@ async function parseFeature(featurePath, type, context, options) {
5456
5463
  async function listFeatureDirs(rootDir) {
5457
5464
  const dirs = await listSubdirectories(rootDir);
5458
5465
  return dirs.filter(
5459
- (value) => path22.basename(value).trim().toLowerCase() !== "feature-base"
5466
+ (value) => path23.basename(value).trim().toLowerCase() !== "feature-base"
5460
5467
  );
5461
5468
  }
5462
5469
  function normalizeRelPath(value) {
@@ -5576,21 +5583,21 @@ async function scanFeatures(config) {
5576
5583
  const allFeatureDirs = [];
5577
5584
  const componentFeatureDirs = /* @__PURE__ */ new Map();
5578
5585
  if (config.projectType === "single") {
5579
- const featureDirs = await listFeatureDirs(path22.join(config.docsDir, "features"));
5586
+ const featureDirs = await listFeatureDirs(path23.join(config.docsDir, "features"));
5580
5587
  componentFeatureDirs.set("single", featureDirs);
5581
5588
  allFeatureDirs.push(...featureDirs);
5582
5589
  } else {
5583
5590
  const components = resolveProjectComponents(config.projectType, config.components);
5584
5591
  for (const component of components) {
5585
5592
  const componentDirs = await listFeatureDirs(
5586
- path22.join(config.docsDir, "features", component)
5593
+ path23.join(config.docsDir, "features", component)
5587
5594
  );
5588
5595
  componentFeatureDirs.set(component, componentDirs);
5589
5596
  allFeatureDirs.push(...componentDirs);
5590
5597
  }
5591
5598
  }
5592
5599
  const relativeFeaturePaths = allFeatureDirs.map(
5593
- (dir) => normalizeRelPath(path22.relative(config.docsDir, dir))
5600
+ (dir) => normalizeRelPath(path23.relative(config.docsDir, dir))
5594
5601
  );
5595
5602
  const docsGitMeta = buildDocsFeatureGitMeta(config.docsDir, relativeFeaturePaths);
5596
5603
  const parseTargets = config.projectType === "single" ? [{ type: "single", dirs: componentFeatureDirs.get("single") || [] }] : resolveProjectComponents(config.projectType, config.components).map((component) => ({
@@ -5601,7 +5608,7 @@ async function scanFeatures(config) {
5601
5608
  const parsed = await Promise.all(
5602
5609
  target.dirs.map(async (dir) => {
5603
5610
  const relativeFeaturePathFromDocs = normalizeRelPath(
5604
- path22.relative(config.docsDir, dir)
5611
+ path23.relative(config.docsDir, dir)
5605
5612
  );
5606
5613
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
5607
5614
  return parseFeature(
@@ -5671,13 +5678,13 @@ async function runStatus(options) {
5671
5678
  );
5672
5679
  }
5673
5680
  const { docsDir, projectType, projectName, lang } = config;
5674
- const featuresDir = path22.join(docsDir, "features");
5681
+ const featuresDir = path23.join(docsDir, "features");
5675
5682
  const scan = await scanFeatures(config);
5676
5683
  const features = [];
5677
5684
  const idMap = /* @__PURE__ */ new Map();
5678
5685
  for (const f of scan.features) {
5679
5686
  const id = f.id || "UNKNOWN";
5680
- const relPath = path22.relative(docsDir, f.path);
5687
+ const relPath = path23.relative(docsDir, f.path);
5681
5688
  if (!idMap.has(id)) idMap.set(id, []);
5682
5689
  idMap.get(id).push(relPath);
5683
5690
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -5758,7 +5765,7 @@ async function runStatus(options) {
5758
5765
  }
5759
5766
  console.log();
5760
5767
  if (options.write) {
5761
- const outputPath = path22.join(featuresDir, "status.md");
5768
+ const outputPath = path23.join(featuresDir, "status.md");
5762
5769
  const date = getLocalDateString();
5763
5770
  const content = [
5764
5771
  "# Feature Status",
@@ -5773,7 +5780,7 @@ async function runStatus(options) {
5773
5780
  ),
5774
5781
  ""
5775
5782
  ].join("\n");
5776
- await fs16.writeFile(outputPath, content, "utf-8");
5783
+ await fs17.writeFile(outputPath, content, "utf-8");
5777
5784
  console.log(
5778
5785
  chalk6.green(
5779
5786
  tr(lang, "cli", "status.wrote", { path: outputPath })
@@ -5786,9 +5793,9 @@ function escapeRegExp2(value) {
5786
5793
  }
5787
5794
  async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
5788
5795
  try {
5789
- const specPath = path22.join(featureDir, "spec.md");
5790
- if (!await fs16.pathExists(specPath)) return fallbackSlug;
5791
- const content = await fs16.readFile(specPath, "utf-8");
5796
+ const specPath = path23.join(featureDir, "spec.md");
5797
+ if (!await fs17.pathExists(specPath)) return fallbackSlug;
5798
+ const content = await fs17.readFile(specPath, "utf-8");
5792
5799
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
5793
5800
  for (const key of keys) {
5794
5801
  const regex = new RegExp(
@@ -5869,8 +5876,8 @@ async function runUpdate(options) {
5869
5876
  console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
5870
5877
  }
5871
5878
  if (agentsMode === "all") {
5872
- const commonAgentsBase = path22.join(templatesDir, lang, "common", "agents");
5873
- const targetAgentsBase = path22.join(docsDir, "agents");
5879
+ const commonAgentsBase = path23.join(templatesDir, lang, "common", "agents");
5880
+ const targetAgentsBase = path23.join(docsDir, "agents");
5874
5881
  const commonAgents = commonAgentsBase;
5875
5882
  const targetAgents = targetAgentsBase;
5876
5883
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
@@ -5879,7 +5886,7 @@ async function runUpdate(options) {
5879
5886
  "{{projectName}}": projectName,
5880
5887
  "{{featurePath}}": featurePath
5881
5888
  };
5882
- if (await fs16.pathExists(commonAgents)) {
5889
+ if (await fs17.pathExists(commonAgents)) {
5883
5890
  const count = await updateFolder(
5884
5891
  commonAgents,
5885
5892
  targetAgents,
@@ -5964,11 +5971,11 @@ function normalizeDecisionEnumList2(raw) {
5964
5971
  return [...deduped];
5965
5972
  }
5966
5973
  async function backfillMissingConfigDefaults(docsDir) {
5967
- const configPath = path22.join(docsDir, ".lee-spec-kit.json");
5968
- if (!await fs16.pathExists(configPath)) {
5974
+ const configPath = path23.join(docsDir, ".lee-spec-kit.json");
5975
+ if (!await fs17.pathExists(configPath)) {
5969
5976
  return { changed: false, changedPaths: [] };
5970
5977
  }
5971
- const raw = await fs16.readJson(configPath);
5978
+ const raw = await fs17.readJson(configPath);
5972
5979
  if (!isPlainObject(raw)) {
5973
5980
  return { changed: false, changedPaths: [] };
5974
5981
  }
@@ -6021,14 +6028,8 @@ async function backfillMissingConfigDefaults(docsDir) {
6021
6028
  prePrReview.evidenceMode = "path_required";
6022
6029
  changedPaths.push("workflow.prePrReview.evidenceMode");
6023
6030
  }
6024
- setIfMissing(
6025
- prePrReview,
6026
- "findings",
6027
- "required",
6028
- "workflow.prePrReview.findings"
6029
- );
6030
- if (prePrReview.findings !== void 0 && prePrReview.findings !== "required" && prePrReview.findings !== "optional") {
6031
- prePrReview.findings = "required";
6031
+ if ("findings" in prePrReview) {
6032
+ delete prePrReview.findings;
6032
6033
  changedPaths.push("workflow.prePrReview.findings");
6033
6034
  }
6034
6035
  if (prePrReview.decisionEnum === void 0) {
@@ -6064,30 +6065,30 @@ async function backfillMissingConfigDefaults(docsDir) {
6064
6065
  if (changedPaths.length === 0) {
6065
6066
  return { changed: false, changedPaths: [] };
6066
6067
  }
6067
- await fs16.writeJson(configPath, raw, { spaces: 2 });
6068
+ await fs17.writeJson(configPath, raw, { spaces: 2 });
6068
6069
  return { changed: true, changedPaths };
6069
6070
  }
6070
6071
  async function updateFolder(sourceDir, targetDir, force, replacements, lang = DEFAULT_LANG, options = {}) {
6071
6072
  const protectedFiles = options.protectedFiles ?? /* @__PURE__ */ new Set(["custom.md", "constitution.md"]);
6072
6073
  const skipDirectories = options.skipDirectories ?? /* @__PURE__ */ new Set();
6073
- await fs16.ensureDir(targetDir);
6074
- const files = await fs16.readdir(sourceDir);
6074
+ await fs17.ensureDir(targetDir);
6075
+ const files = await fs17.readdir(sourceDir);
6075
6076
  let updatedCount = 0;
6076
6077
  for (const file of files) {
6077
- const sourcePath = path22.join(sourceDir, file);
6078
- const targetPath = path22.join(targetDir, file);
6079
- const stat = await fs16.stat(sourcePath);
6078
+ const sourcePath = path23.join(sourceDir, file);
6079
+ const targetPath = path23.join(targetDir, file);
6080
+ const stat = await fs17.stat(sourcePath);
6080
6081
  if (stat.isFile()) {
6081
6082
  if (protectedFiles.has(file)) {
6082
6083
  continue;
6083
6084
  }
6084
- let sourceContent = await fs16.readFile(sourcePath, "utf-8");
6085
+ let sourceContent = await fs17.readFile(sourcePath, "utf-8");
6085
6086
  if (replacements) {
6086
6087
  sourceContent = applyReplacements(sourceContent, replacements);
6087
6088
  }
6088
6089
  let shouldUpdate = true;
6089
- if (await fs16.pathExists(targetPath)) {
6090
- const targetContent = await fs16.readFile(targetPath, "utf-8");
6090
+ if (await fs17.pathExists(targetPath)) {
6091
+ const targetContent = await fs17.readFile(targetPath, "utf-8");
6091
6092
  if (sourceContent === targetContent) {
6092
6093
  continue;
6093
6094
  }
@@ -6101,7 +6102,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
6101
6102
  }
6102
6103
  }
6103
6104
  if (shouldUpdate) {
6104
- await fs16.writeFile(targetPath, sourceContent);
6105
+ await fs17.writeFile(targetPath, sourceContent);
6105
6106
  console.log(
6106
6107
  chalk6.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
6107
6108
  );
@@ -6158,7 +6159,7 @@ function extractPorcelainPaths(line) {
6158
6159
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
6159
6160
  const top = getGitTopLevel2(docsDir);
6160
6161
  if (!top) return null;
6161
- const rel = path22.relative(top, docsDir) || ".";
6162
+ const rel = path23.relative(top, docsDir) || ".";
6162
6163
  try {
6163
6164
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
6164
6165
  cwd: top,
@@ -6170,7 +6171,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
6170
6171
  }
6171
6172
  const ignoredRelPaths = new Set(
6172
6173
  ignoredAbsPaths.map(
6173
- (absPath) => normalizeGitPath2(path22.relative(top, absPath) || ".")
6174
+ (absPath) => normalizeGitPath2(path23.relative(top, absPath) || ".")
6174
6175
  )
6175
6176
  );
6176
6177
  const filtered = output.split("\n").filter((line) => {
@@ -6228,7 +6229,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
6228
6229
  }
6229
6230
  async function runConfig(options) {
6230
6231
  const cwd = process.cwd();
6231
- const targetCwd = options.dir ? path22.resolve(cwd, options.dir) : cwd;
6232
+ const targetCwd = options.dir ? path23.resolve(cwd, options.dir) : cwd;
6232
6233
  const config = await getConfig(targetCwd);
6233
6234
  if (!config) {
6234
6235
  throw createCliError(
@@ -6236,7 +6237,7 @@ async function runConfig(options) {
6236
6237
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
6237
6238
  );
6238
6239
  }
6239
- const configPath = path22.join(config.docsDir, ".lee-spec-kit.json");
6240
+ const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
6240
6241
  if (!options.projectRoot) {
6241
6242
  console.log();
6242
6243
  console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -6247,7 +6248,7 @@ async function runConfig(options) {
6247
6248
  )
6248
6249
  );
6249
6250
  console.log();
6250
- const configFile = await fs16.readJson(configPath);
6251
+ const configFile = await fs17.readJson(configPath);
6251
6252
  console.log(JSON.stringify(configFile, null, 2));
6252
6253
  console.log();
6253
6254
  return;
@@ -6256,7 +6257,7 @@ async function runConfig(options) {
6256
6257
  await withFileLock(
6257
6258
  getDocsLockPath(config.docsDir),
6258
6259
  async () => {
6259
- const configFile = await fs16.readJson(configPath);
6260
+ const configFile = await fs17.readJson(configPath);
6260
6261
  if (configFile.docsRepo !== "standalone") {
6261
6262
  console.log(
6262
6263
  chalk6.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
@@ -6332,7 +6333,7 @@ async function runConfig(options) {
6332
6333
  )
6333
6334
  );
6334
6335
  }
6335
- await fs16.writeJson(configPath, configFile, { spaces: 2 });
6336
+ await fs17.writeJson(configPath, configFile, { spaces: 2 });
6336
6337
  console.log();
6337
6338
  },
6338
6339
  { owner: "config" }
@@ -6413,6 +6414,14 @@ function annotateActions(actions) {
6413
6414
  return actions.map((action) => annotateActionOperationType(action));
6414
6415
  }
6415
6416
  function getActionSummary(action, lang) {
6417
+ if (action.uiSummaryKey) {
6418
+ const localized = tr(lang, "cli", action.uiSummaryKey);
6419
+ if (localized !== `cli.${action.uiSummaryKey}`) return localized;
6420
+ }
6421
+ if (action.uiDetailKey) {
6422
+ const localized = tr(lang, "cli", action.uiDetailKey);
6423
+ if (localized !== `cli.${action.uiDetailKey}`) return localized;
6424
+ }
6416
6425
  const detailKey = action.category ? ACTION_DETAIL_KEY_BY_CATEGORY[action.category] : void 0;
6417
6426
  if (detailKey) {
6418
6427
  const localized = tr(lang, "cli", detailKey);
@@ -6434,6 +6443,10 @@ function toOneLine(text) {
6434
6443
  return `${normalized.slice(0, 157).trimEnd()}...`;
6435
6444
  }
6436
6445
  function buildActionDetail(action, lang) {
6446
+ if (action.uiDetailKey) {
6447
+ const localized = tr(lang, "cli", action.uiDetailKey);
6448
+ if (localized !== `cli.${action.uiDetailKey}`) return localized;
6449
+ }
6437
6450
  const formatBranchCreateDetail = (command) => {
6438
6451
  const worktreeMatch = command.match(/\.worktrees\/([A-Za-z0-9._-]+)/);
6439
6452
  const branchMatch = command.match(/\bfeat\/([A-Za-z0-9._-]+)/);
@@ -6489,6 +6502,11 @@ function buildActionDetail(action, lang) {
6489
6502
  });
6490
6503
  }
6491
6504
  }
6505
+ if (action.category === "pre_pr_review") {
6506
+ return tr(lang, "cli", "context.commandDetail.prePrReviewRun", {
6507
+ scope: action.scope
6508
+ });
6509
+ }
6492
6510
  if (action.category === "worktree_cleanup") {
6493
6511
  return `(${action.scope}) ${tr(lang, "cli", "context.actionDetail.worktreeCleanup")}`;
6494
6512
  }
@@ -6537,7 +6555,9 @@ function buildActionSnapshot(actionOptions) {
6537
6555
  cmd: action.cmd,
6538
6556
  category: action.category,
6539
6557
  operationType: action.operationType,
6540
- requiresUserCheck: !!action.requiresUserCheck
6558
+ requiresUserCheck: !!action.requiresUserCheck,
6559
+ uiSummaryKey: action.uiSummaryKey,
6560
+ uiDetailKey: action.uiDetailKey
6541
6561
  };
6542
6562
  }
6543
6563
  return {
@@ -6546,7 +6566,9 @@ function buildActionSnapshot(actionOptions) {
6546
6566
  message: action.message,
6547
6567
  category: action.category,
6548
6568
  operationType: action.operationType,
6549
- requiresUserCheck: !!action.requiresUserCheck
6569
+ requiresUserCheck: !!action.requiresUserCheck,
6570
+ uiSummaryKey: action.uiSummaryKey,
6571
+ uiDetailKey: action.uiDetailKey
6550
6572
  };
6551
6573
  });
6552
6574
  }
@@ -6732,13 +6754,13 @@ function getApprovalSessionId() {
6732
6754
  function getApprovalTicketPaths(config) {
6733
6755
  return {
6734
6756
  runtimePath: getApprovalTicketStorePath(config.docsDir),
6735
- legacyPath: path22.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
6757
+ legacyPath: path23.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
6736
6758
  };
6737
6759
  }
6738
6760
  async function loadApprovalTicketStore(storePath) {
6739
- if (!await fs16.pathExists(storePath)) return { tickets: [] };
6761
+ if (!await fs17.pathExists(storePath)) return { tickets: [] };
6740
6762
  try {
6741
- const parsed = await fs16.readJson(storePath);
6763
+ const parsed = await fs17.readJson(storePath);
6742
6764
  if (!parsed || !Array.isArray(parsed.tickets)) return { tickets: [] };
6743
6765
  return { tickets: parsed.tickets };
6744
6766
  } catch {
@@ -6746,8 +6768,8 @@ async function loadApprovalTicketStore(storePath) {
6746
6768
  }
6747
6769
  }
6748
6770
  async function saveApprovalTicketStore(storePath, payload) {
6749
- await fs16.ensureDir(path22.dirname(storePath));
6750
- await fs16.writeJson(storePath, payload, { spaces: 2 });
6771
+ await fs17.ensureDir(path23.dirname(storePath));
6772
+ await fs17.writeJson(storePath, payload, { spaces: 2 });
6751
6773
  }
6752
6774
  function pruneApprovalTickets(tickets, nowMs) {
6753
6775
  return tickets.filter((ticket) => {
@@ -6759,13 +6781,13 @@ function pruneApprovalTickets(tickets, nowMs) {
6759
6781
  }
6760
6782
  async function resolveApprovalTicketStoreAndPath(config, nowMs) {
6761
6783
  const { runtimePath, legacyPath } = getApprovalTicketPaths(config);
6762
- if (await fs16.pathExists(runtimePath)) {
6784
+ if (await fs17.pathExists(runtimePath)) {
6763
6785
  return {
6764
6786
  storePath: runtimePath,
6765
6787
  store: await loadApprovalTicketStore(runtimePath)
6766
6788
  };
6767
6789
  }
6768
- if (!await fs16.pathExists(legacyPath)) {
6790
+ if (!await fs17.pathExists(legacyPath)) {
6769
6791
  return {
6770
6792
  storePath: runtimePath,
6771
6793
  store: { tickets: [] }
@@ -6781,7 +6803,7 @@ async function resolveApprovalTicketStoreAndPath(config, nowMs) {
6781
6803
  migratedFrom: legacyPath
6782
6804
  }
6783
6805
  );
6784
- await fs16.remove(legacyPath).catch(() => {
6806
+ await fs17.remove(legacyPath).catch(() => {
6785
6807
  });
6786
6808
  return {
6787
6809
  storePath: runtimePath,
@@ -6913,42 +6935,42 @@ var BUILTIN_DOC_DEFINITIONS = [
6913
6935
  {
6914
6936
  id: "agents",
6915
6937
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
6916
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "agents.md")
6938
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "agents.md")
6917
6939
  },
6918
6940
  {
6919
6941
  id: "git-workflow",
6920
6942
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
6921
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "git-workflow.md")
6943
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "git-workflow.md")
6922
6944
  },
6923
6945
  {
6924
6946
  id: "issue-doc",
6925
6947
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
6926
- relativePath: (_, lang) => path22.join(lang, "common", "features", "feature-base", "issue.md")
6948
+ relativePath: (_, lang) => path23.join(lang, "common", "features", "feature-base", "issue.md")
6927
6949
  },
6928
6950
  {
6929
6951
  id: "pr-doc",
6930
6952
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
6931
- relativePath: (_, lang) => path22.join(lang, "common", "features", "feature-base", "pr.md")
6953
+ relativePath: (_, lang) => path23.join(lang, "common", "features", "feature-base", "pr.md")
6932
6954
  },
6933
6955
  {
6934
6956
  id: "create-feature",
6935
6957
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
6936
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "skills", "create-feature.md")
6958
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-feature.md")
6937
6959
  },
6938
6960
  {
6939
6961
  id: "execute-task",
6940
6962
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
6941
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "skills", "execute-task.md")
6963
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "execute-task.md")
6942
6964
  },
6943
6965
  {
6944
6966
  id: "create-issue",
6945
6967
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
6946
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "skills", "create-issue.md")
6968
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-issue.md")
6947
6969
  },
6948
6970
  {
6949
6971
  id: "create-pr",
6950
6972
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
6951
- relativePath: (_, lang) => path22.join(lang, "common", "agents", "skills", "create-pr.md")
6973
+ relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-pr.md")
6952
6974
  }
6953
6975
  ];
6954
6976
  var DOC_FOLLOWUPS = {
@@ -7037,7 +7059,7 @@ function listBuiltinDocs(projectType, lang) {
7037
7059
  id: doc.id,
7038
7060
  title: doc.title[lang],
7039
7061
  relativePath,
7040
- absolutePath: path22.join(templatesDir, relativePath)
7062
+ absolutePath: path23.join(templatesDir, relativePath)
7041
7063
  };
7042
7064
  });
7043
7065
  }
@@ -7046,7 +7068,7 @@ async function getBuiltinDoc(docId, projectType, lang) {
7046
7068
  if (!entry) {
7047
7069
  throw new Error(`Unknown builtin doc: ${docId}`);
7048
7070
  }
7049
- const content = await fs16.readFile(entry.absolutePath, "utf-8");
7071
+ const content = await fs17.readFile(entry.absolutePath, "utf-8");
7050
7072
  const hash = createHash("sha256").update(content).digest("hex").slice(0, 12);
7051
7073
  return {
7052
7074
  entry,
@@ -7057,6 +7079,56 @@ async function getBuiltinDoc(docId, projectType, lang) {
7057
7079
  }
7058
7080
 
7059
7081
  // src/commands/context.ts
7082
+ var LONG_RUNNING_DELEGATION_CATEGORIES = [
7083
+ "task_execute",
7084
+ "code_review",
7085
+ "review_fix_commit",
7086
+ "pre_pr_review"
7087
+ ];
7088
+ function shouldDelegateCurrentAction(actionOptions, autoRunAvailable) {
7089
+ const primaryCategory = actionOptions[0]?.action?.category || null;
7090
+ const longRunningSet = new Set(LONG_RUNNING_DELEGATION_CATEGORIES);
7091
+ const shouldDelegate = autoRunAvailable || !!primaryCategory && longRunningSet.has(primaryCategory);
7092
+ return {
7093
+ shouldDelegate,
7094
+ category: primaryCategory
7095
+ };
7096
+ }
7097
+ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable) {
7098
+ const delegation = shouldDelegateCurrentAction(actionOptions, autoRunAvailable);
7099
+ return {
7100
+ mode: "main_orchestrates_subagent_execution",
7101
+ delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
7102
+ delegateCommandExecution: "long_running_only",
7103
+ delegateAutoRunExecution: true,
7104
+ fallbackToMainAgentWhenSubAgentUnavailable: true,
7105
+ longRunningCategories: [...LONG_RUNNING_DELEGATION_CATEGORIES],
7106
+ currentActionShouldDelegate: delegation.shouldDelegate,
7107
+ currentActionCategory: delegation.category,
7108
+ mainAgentResponsibilities: [
7109
+ "Keep user conversation state and approval boundaries",
7110
+ "Run the same execution loop directly when sub-agent is unavailable",
7111
+ "Delegate only long-running command/auto loops to sub-agents",
7112
+ "Report only on approval/manual/error boundaries"
7113
+ ],
7114
+ subAgentResponsibilities: [
7115
+ "Run flow/context command loops",
7116
+ "Execute only currently selected atomic command actions",
7117
+ "Return structured status to main agent"
7118
+ ],
7119
+ pauseAndReportWhen: [
7120
+ "approvalRequest.required=true",
7121
+ "AUTO_GATE_REACHED",
7122
+ "AUTO_MANUAL_REQUIRED",
7123
+ "command execution error"
7124
+ ],
7125
+ resumePriority: [
7126
+ "flow --resume <RUN_ID>",
7127
+ "autoRun.resume.flowCommand",
7128
+ "context --json-compact"
7129
+ ]
7130
+ };
7131
+ }
7060
7132
  async function resolveContextState(config, featureName, options) {
7061
7133
  if (!config) {
7062
7134
  throw createCliError(
@@ -7381,9 +7453,6 @@ function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
7381
7453
  if (prePrReviewPolicy.enabled && f.prePrReview.status !== "Done") {
7382
7454
  return tr(lang, "cli", "context.list.completePrePrReview");
7383
7455
  }
7384
- if (prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && (!f.docs.prePrFindingsFieldExists || !f.prePrReview.findingsProvided)) {
7385
- return tr(lang, "cli", "context.list.addPrePrFindings");
7386
- }
7387
7456
  if (prePrReviewPolicy.enabled && (!f.docs.prePrEvidenceFieldExists || !f.prePrReview.evidenceProvided)) {
7388
7457
  return tr(lang, "cli", "context.list.addPrePrEvidence");
7389
7458
  }
@@ -7448,14 +7517,11 @@ function toCompactFeature(feature) {
7448
7517
  tasks: feature.tasks,
7449
7518
  prePrReview: {
7450
7519
  status: feature.prePrReview.status,
7451
- findings: feature.prePrReview.findings,
7452
- findingsProvided: feature.prePrReview.findingsProvided,
7453
7520
  evidenceProvided: feature.prePrReview.evidenceProvided,
7454
7521
  decisionOutcome: feature.prePrReview.decisionOutcome,
7455
7522
  decisionProvided: feature.prePrReview.decisionProvided
7456
7523
  },
7457
7524
  prReview: {
7458
- findings: feature.prReview.findings,
7459
7525
  evidenceProvided: feature.prReview.evidenceProvided,
7460
7526
  decisionProvided: feature.prReview.decisionProvided
7461
7527
  },
@@ -7487,10 +7553,8 @@ function toCompactFeature(feature) {
7487
7553
  prFieldExists: feature.docs.prFieldExists,
7488
7554
  prStatusFieldExists: feature.docs.prStatusFieldExists,
7489
7555
  prePrReviewFieldExists: feature.docs.prePrReviewFieldExists,
7490
- prePrFindingsFieldExists: feature.docs.prePrFindingsFieldExists,
7491
7556
  prePrEvidenceFieldExists: feature.docs.prePrEvidenceFieldExists,
7492
7557
  prePrDecisionFieldExists: feature.docs.prePrDecisionFieldExists,
7493
- prReviewFindingsFieldExists: feature.docs.prReviewFindingsFieldExists,
7494
7558
  prReviewEvidenceFieldExists: feature.docs.prReviewEvidenceFieldExists,
7495
7559
  prReviewDecisionFieldExists: feature.docs.prReviewDecisionFieldExists
7496
7560
  },
@@ -7612,6 +7676,10 @@ async function runContext(featureName, options) {
7612
7676
  config.approval,
7613
7677
  approvalRequired
7614
7678
  );
7679
+ const agentOrchestration = buildAgentOrchestrationPolicy(
7680
+ state.actionOptions,
7681
+ autoRunPlan.available
7682
+ );
7615
7683
  if (options.approve || options.execute) {
7616
7684
  await runApprovedOption(
7617
7685
  state,
@@ -7687,6 +7755,7 @@ async function runContext(featureName, options) {
7687
7755
  contextVersion: state.contextVersion,
7688
7756
  config: config.approval ?? { mode: "builtin" }
7689
7757
  },
7758
+ agentOrchestration,
7690
7759
  autoRun: {
7691
7760
  available: autoRunPlan.available,
7692
7761
  reasonCode: autoRunPlan.reasonCode,
@@ -7768,12 +7837,13 @@ async function runContext(featureName, options) {
7768
7837
  "actionOptions[].detail",
7769
7838
  "actionOptions[].approvalPrompt"
7770
7839
  ] : [],
7771
- 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.",
7840
+ 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, and delegate only long-running command/auto loops to sub-agents.",
7772
7841
  oneApprovalPerAction: approvalRequired,
7773
7842
  requireFreshContext: true,
7774
7843
  contextVersion: state.contextVersion,
7775
7844
  config: config.approval ?? { mode: "builtin" }
7776
7845
  },
7846
+ agentOrchestration,
7777
7847
  autoRun: {
7778
7848
  available: autoRunPlan.available,
7779
7849
  reasonCode: autoRunPlan.reasonCode,
@@ -7784,7 +7854,7 @@ async function runContext(featureName, options) {
7784
7854
  guidance: "Use auto-run only when `autoRun.available=true`. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories."
7785
7855
  },
7786
7856
  approvalRequest: {
7787
- 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`.",
7857
+ 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 and delegate only long-running command/auto loops to sub-agents.",
7788
7858
  required: approvalRequired,
7789
7859
  finalPrompt: finalApprovalPrompt,
7790
7860
  userFacingLines: approvalUserFacingLines,
@@ -8006,7 +8076,7 @@ async function runContext(featureName, options) {
8006
8076
  if (f.issueNumber) {
8007
8077
  console.log(` \u2022 Issue: #${f.issueNumber}`);
8008
8078
  }
8009
- console.log(` \u2022 Path: ${path22.relative(cwd, f.path)}`);
8079
+ console.log(` \u2022 Path: ${path23.relative(cwd, f.path)}`);
8010
8080
  if (f.git.projectBranch) {
8011
8081
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
8012
8082
  }
@@ -8040,6 +8110,11 @@ async function runContext(featureName, options) {
8040
8110
  return;
8041
8111
  }
8042
8112
  const actionOptions = state.actionOptions;
8113
+ const hasCommandOption = actionOptions.some((option) => option.action.type === "command");
8114
+ const longRunningDelegation = shouldDelegateCurrentAction(
8115
+ actionOptions,
8116
+ autoRunPlan.available
8117
+ );
8043
8118
  console.log(chalk6.green(chalk6.bold("\u{1F449} Next Options (Atomic):")));
8044
8119
  let hasDocsCommand = false;
8045
8120
  actionOptions.forEach((option) => {
@@ -8053,6 +8128,9 @@ async function runContext(featureName, options) {
8053
8128
  if (hasDocsCommand) {
8054
8129
  console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
8055
8130
  }
8131
+ if (hasCommandOption && longRunningDelegation.shouldDelegate) {
8132
+ console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`));
8133
+ }
8056
8134
  if (hasCheckAction) {
8057
8135
  console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`));
8058
8136
  console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`));
@@ -8370,7 +8448,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
8370
8448
  ]);
8371
8449
  function formatPath(cwd, p) {
8372
8450
  if (!p) return "";
8373
- return path22.isAbsolute(p) ? path22.relative(cwd, p) : p;
8451
+ return path23.isAbsolute(p) ? path23.relative(cwd, p) : p;
8374
8452
  }
8375
8453
  function detectPlaceholders(content) {
8376
8454
  const patterns = [
@@ -8519,7 +8597,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
8519
8597
  const placeholderContext = {
8520
8598
  projectName: config.projectName,
8521
8599
  featureName: f.slug,
8522
- featurePath: f.docs.featurePathFromDocs || path22.relative(config.docsDir, f.path),
8600
+ featurePath: f.docs.featurePathFromDocs || path23.relative(config.docsDir, f.path),
8523
8601
  repoType: f.type,
8524
8602
  featureNumber
8525
8603
  };
@@ -8529,9 +8607,9 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
8529
8607
  "tasks.md"
8530
8608
  ];
8531
8609
  for (const file of files) {
8532
- const fullPath = path22.join(f.path, file);
8533
- if (!await fs16.pathExists(fullPath)) continue;
8534
- const original = await fs16.readFile(fullPath, "utf-8");
8610
+ const fullPath = path23.join(f.path, file);
8611
+ if (!await fs17.pathExists(fullPath)) continue;
8612
+ const original = await fs17.readFile(fullPath, "utf-8");
8535
8613
  let next = original;
8536
8614
  const changes = [];
8537
8615
  const placeholderFix = applyPlaceholderFixes(next, placeholderContext, config.lang);
@@ -8555,7 +8633,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
8555
8633
  }
8556
8634
  if (next === original) continue;
8557
8635
  if (!dryRun) {
8558
- await fs16.writeFile(fullPath, next, "utf-8");
8636
+ await fs17.writeFile(fullPath, next, "utf-8");
8559
8637
  }
8560
8638
  entries.push({
8561
8639
  path: formatPath(cwd, fullPath),
@@ -8574,8 +8652,8 @@ async function checkDocsStructure(config, cwd) {
8574
8652
  const issues = [];
8575
8653
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
8576
8654
  for (const dir of requiredDirs) {
8577
- const p = path22.join(config.docsDir, dir);
8578
- if (!await fs16.pathExists(p)) {
8655
+ const p = path23.join(config.docsDir, dir);
8656
+ if (!await fs17.pathExists(p)) {
8579
8657
  issues.push({
8580
8658
  level: "error",
8581
8659
  code: "missing_dir",
@@ -8584,8 +8662,8 @@ async function checkDocsStructure(config, cwd) {
8584
8662
  });
8585
8663
  }
8586
8664
  }
8587
- const configPath = path22.join(config.docsDir, ".lee-spec-kit.json");
8588
- if (!await fs16.pathExists(configPath)) {
8665
+ const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
8666
+ if (!await fs17.pathExists(configPath)) {
8589
8667
  issues.push({
8590
8668
  level: "warn",
8591
8669
  code: "missing_config",
@@ -8607,7 +8685,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8607
8685
  }
8608
8686
  const idMap = /* @__PURE__ */ new Map();
8609
8687
  for (const f of features) {
8610
- const rel = f.docs.featurePathFromDocs || path22.relative(config.docsDir, f.path);
8688
+ const rel = f.docs.featurePathFromDocs || path23.relative(config.docsDir, f.path);
8611
8689
  const id = f.id || "UNKNOWN";
8612
8690
  if (!idMap.has(id)) idMap.set(id, []);
8613
8691
  idMap.get(id).push(rel);
@@ -8615,9 +8693,9 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8615
8693
  if (!isInitialTemplateState) {
8616
8694
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
8617
8695
  for (const file of featureDocs) {
8618
- const p = path22.join(f.path, file);
8619
- if (!await fs16.pathExists(p)) continue;
8620
- const content = await fs16.readFile(p, "utf-8");
8696
+ const p = path23.join(f.path, file);
8697
+ if (!await fs17.pathExists(p)) continue;
8698
+ const content = await fs17.readFile(p, "utf-8");
8621
8699
  const placeholders = detectPlaceholders(content);
8622
8700
  if (placeholders.length === 0) continue;
8623
8701
  issues.push({
@@ -8630,9 +8708,9 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8630
8708
  });
8631
8709
  }
8632
8710
  if (decisionsPlaceholderMode !== "off") {
8633
- const decisionsPath = path22.join(f.path, "decisions.md");
8634
- if (await fs16.pathExists(decisionsPath)) {
8635
- const content = await fs16.readFile(decisionsPath, "utf-8");
8711
+ const decisionsPath = path23.join(f.path, "decisions.md");
8712
+ if (await fs17.pathExists(decisionsPath)) {
8713
+ const content = await fs17.readFile(decisionsPath, "utf-8");
8636
8714
  const placeholders = detectPlaceholders(content);
8637
8715
  if (placeholders.length > 0) {
8638
8716
  issues.push({
@@ -8659,7 +8737,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8659
8737
  level: "warn",
8660
8738
  code: "spec_status_unset",
8661
8739
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
8662
- path: formatPath(cwd, path22.join(f.path, "spec.md"))
8740
+ path: formatPath(cwd, path23.join(f.path, "spec.md"))
8663
8741
  });
8664
8742
  }
8665
8743
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -8667,7 +8745,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8667
8745
  level: "warn",
8668
8746
  code: "plan_status_unset",
8669
8747
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
8670
- path: formatPath(cwd, path22.join(f.path, "plan.md"))
8748
+ path: formatPath(cwd, path23.join(f.path, "plan.md"))
8671
8749
  });
8672
8750
  }
8673
8751
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -8675,7 +8753,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8675
8753
  level: "warn",
8676
8754
  code: "tasks_empty",
8677
8755
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
8678
- path: formatPath(cwd, path22.join(f.path, "tasks.md"))
8756
+ path: formatPath(cwd, path23.join(f.path, "tasks.md"))
8679
8757
  });
8680
8758
  }
8681
8759
  if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
@@ -8683,7 +8761,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8683
8761
  level: "warn",
8684
8762
  code: "tasks_doc_status_missing",
8685
8763
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
8686
- path: formatPath(cwd, path22.join(f.path, "tasks.md"))
8764
+ path: formatPath(cwd, path23.join(f.path, "tasks.md"))
8687
8765
  });
8688
8766
  }
8689
8767
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -8691,7 +8769,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8691
8769
  level: "warn",
8692
8770
  code: "tasks_doc_status_unset",
8693
8771
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
8694
- path: formatPath(cwd, path22.join(f.path, "tasks.md"))
8772
+ path: formatPath(cwd, path23.join(f.path, "tasks.md"))
8695
8773
  });
8696
8774
  }
8697
8775
  }
@@ -8715,7 +8793,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
8715
8793
  level: "warn",
8716
8794
  code: "missing_feature_id",
8717
8795
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
8718
- path: formatPath(cwd, path22.join(config.docsDir, p))
8796
+ path: formatPath(cwd, path23.join(config.docsDir, p))
8719
8797
  });
8720
8798
  }
8721
8799
  return issues;
@@ -8838,7 +8916,7 @@ function doctorCommand(program2) {
8838
8916
  }
8839
8917
  console.log();
8840
8918
  console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
8841
- console.log(chalk6.gray(`- Docs: ${path22.relative(cwd, docsDir)}`));
8919
+ console.log(chalk6.gray(`- Docs: ${path23.relative(cwd, docsDir)}`));
8842
8920
  console.log(chalk6.gray(`- Type: ${projectType}`));
8843
8921
  console.log(chalk6.gray(`- Lang: ${lang}`));
8844
8922
  console.log();
@@ -9010,7 +9088,7 @@ async function runView(featureName, options) {
9010
9088
  }
9011
9089
  console.log();
9012
9090
  console.log(chalk6.bold("\u{1F4CA} Workflow View"));
9013
- console.log(chalk6.gray(`- Docs: ${path22.relative(cwd, config.docsDir)}`));
9091
+ console.log(chalk6.gray(`- Docs: ${path23.relative(cwd, config.docsDir)}`));
9014
9092
  console.log(
9015
9093
  chalk6.gray(
9016
9094
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
@@ -9080,9 +9158,145 @@ async function runView(featureName, options) {
9080
9158
  }
9081
9159
  console.log();
9082
9160
  }
9161
+ function normalizeRunId(raw) {
9162
+ const value = raw.trim();
9163
+ if (!/^[A-Za-z0-9_-]{6,64}$/.test(value)) {
9164
+ throw createCliError(
9165
+ "INVALID_ARGUMENT",
9166
+ "Invalid flow run id format. Use an id returned by `flow --start-auto --json`."
9167
+ );
9168
+ }
9169
+ return value;
9170
+ }
9171
+ function getFlowRunBaseDir(cwd) {
9172
+ return path23.join(getRuntimeStateDir(cwd), "flow-runs");
9173
+ }
9174
+ function getFlowRunPath(cwd, runId) {
9175
+ return path23.join(getFlowRunBaseDir(cwd), `${runId}.json`);
9176
+ }
9177
+ function getFlowRunLockPath(cwd, runId) {
9178
+ return path23.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
9179
+ }
9180
+ async function readFlowRunRecordUnsafe(cwd, runId) {
9181
+ const normalized = normalizeRunId(runId);
9182
+ const filePath = getFlowRunPath(cwd, normalized);
9183
+ if (!await fs17.pathExists(filePath)) {
9184
+ throw createCliError(
9185
+ "INVALID_ARGUMENT",
9186
+ `Unknown flow run id: ${normalized}. Start with \`flow <feature> --auto-... --start-auto\`.`
9187
+ );
9188
+ }
9189
+ try {
9190
+ const parsed = await fs17.readJson(filePath);
9191
+ if (!parsed || typeof parsed !== "object" || parsed.runId !== normalized) {
9192
+ throw new Error("invalid payload");
9193
+ }
9194
+ return parsed;
9195
+ } catch {
9196
+ throw createCliError(
9197
+ "INVALID_ARGUMENT",
9198
+ `Cannot load flow run record: ${normalized}`
9199
+ );
9200
+ }
9201
+ }
9202
+ async function writeFlowRunRecord(cwd, record) {
9203
+ const filePath = getFlowRunPath(cwd, record.runId);
9204
+ await fs17.ensureDir(path23.dirname(filePath));
9205
+ await fs17.writeJson(filePath, record, { spaces: 2 });
9206
+ }
9207
+ async function createFlowRunRecord(cwd, input) {
9208
+ const runId = randomUUID().replace(/-/g, "").slice(0, 16);
9209
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
9210
+ const record = {
9211
+ runId,
9212
+ featureName: input.featureName,
9213
+ selection: {
9214
+ component: input.selection.component || void 0,
9215
+ all: !!input.selection.all,
9216
+ done: !!input.selection.done
9217
+ },
9218
+ auto: {
9219
+ untilCategories: [...input.auto.untilCategories],
9220
+ requestText: input.auto.requestText?.trim() || void 0,
9221
+ requestPending: input.auto.requestPending,
9222
+ preset: input.auto.preset ?? null,
9223
+ source: input.auto.source ?? null
9224
+ },
9225
+ status: "running",
9226
+ createdAt: nowIso,
9227
+ updatedAt: nowIso
9228
+ };
9229
+ const lockPath = getFlowRunLockPath(cwd, runId);
9230
+ return withFileLock(
9231
+ lockPath,
9232
+ async () => {
9233
+ await writeFlowRunRecord(cwd, record);
9234
+ return record;
9235
+ },
9236
+ { owner: "flow-run:create" }
9237
+ );
9238
+ }
9239
+ async function getFlowRunRecord(cwd, runId) {
9240
+ const normalized = normalizeRunId(runId);
9241
+ return readFlowRunRecordUnsafe(cwd, normalized);
9242
+ }
9243
+ async function updateFlowRunRecord(cwd, runId, updater) {
9244
+ const normalized = normalizeRunId(runId);
9245
+ const lockPath = getFlowRunLockPath(cwd, normalized);
9246
+ return withFileLock(
9247
+ lockPath,
9248
+ async () => {
9249
+ const current = await readFlowRunRecordUnsafe(cwd, normalized);
9250
+ const next = updater(current);
9251
+ const updated = {
9252
+ ...next,
9253
+ runId: normalized,
9254
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9255
+ };
9256
+ await writeFlowRunRecord(cwd, updated);
9257
+ return updated;
9258
+ },
9259
+ { owner: "flow-run:update" }
9260
+ );
9261
+ }
9262
+
9263
+ // src/commands/flow.ts
9264
+ var LONG_RUNNING_DELEGATION_CATEGORIES2 = [
9265
+ "task_execute",
9266
+ "code_review",
9267
+ "review_fix_commit",
9268
+ "pre_pr_review"
9269
+ ];
9083
9270
  var BUILTIN_AUTO_PRESETS = {
9084
9271
  "pr-handoff": ["pr_create", "code_review", "pr_status_update"]
9085
9272
  };
9273
+ function shellEscape(arg) {
9274
+ if (/^[A-Za-z0-9_./:@%-]+$/.test(arg)) return arg;
9275
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
9276
+ }
9277
+ function toLeeSpecKitCommand(args) {
9278
+ return ["npx", "lee-spec-kit", ...args].map((arg) => shellEscape(arg)).join(" ");
9279
+ }
9280
+ function buildResumeRunCommand(runId) {
9281
+ return toLeeSpecKitCommand(["flow", "--resume", runId]);
9282
+ }
9283
+ function buildAutoResume(featureName, selectionOptions, untilCategories, requestText, requestPending) {
9284
+ const selectionArgs = buildSelectionArgs(featureName, selectionOptions);
9285
+ const flowArgs = ["flow", ...selectionArgs];
9286
+ if (requestPending && requestText) {
9287
+ flowArgs.push("--request", requestText);
9288
+ }
9289
+ flowArgs.push("--auto-until-category", untilCategories.join(","));
9290
+ const contextArgs = ["context", ...selectionArgs];
9291
+ return {
9292
+ flowArgs,
9293
+ flowCommand: toLeeSpecKitCommand(flowArgs),
9294
+ contextArgs,
9295
+ contextCommand: toLeeSpecKitCommand(contextArgs),
9296
+ requiresFreshContext: true,
9297
+ requestPending
9298
+ };
9299
+ }
9086
9300
  function runSelfCli(args) {
9087
9301
  const entry = process.argv[1];
9088
9302
  const result = spawnSync(process.execPath, [entry, "--no-banner", ...args], {
@@ -9271,6 +9485,153 @@ function toAutoReasonCode(status) {
9271
9485
  function isAutoRunFailureStatus(status) {
9272
9486
  return status === "manual_required" || status === "selection_required" || status === "no_progress" || status === "request_label_missing" || status === "request_failed" || status === "execution_failed";
9273
9487
  }
9488
+ function toFlowRunStatus(status) {
9489
+ switch (status) {
9490
+ case "gate_reached":
9491
+ case "manual_required":
9492
+ return "paused";
9493
+ case "no_action_options":
9494
+ return "completed";
9495
+ case "selection_required":
9496
+ case "no_progress":
9497
+ case "request_label_missing":
9498
+ case "request_failed":
9499
+ case "execution_failed":
9500
+ default:
9501
+ return "failed";
9502
+ }
9503
+ }
9504
+ function buildAgentOrchestrationPolicy2(autoRun) {
9505
+ const preferredResumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
9506
+ return {
9507
+ mode: "main_orchestrates_subagent_execution",
9508
+ delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
9509
+ delegateCommandExecution: "long_running_only",
9510
+ delegateAutoRunExecution: true,
9511
+ fallbackToMainAgentWhenSubAgentUnavailable: true,
9512
+ longRunningCategories: [...LONG_RUNNING_DELEGATION_CATEGORIES2],
9513
+ mainAgentResponsibilities: [
9514
+ "Keep user conversation state and approval boundaries",
9515
+ "Run the same execution loop directly when sub-agent is unavailable",
9516
+ "Delegate only long-running command/auto loops to sub-agents",
9517
+ "Report only on approval/manual/error boundaries"
9518
+ ],
9519
+ subAgentResponsibilities: [
9520
+ "Run flow/context command loops",
9521
+ "Execute selected atomic command actions",
9522
+ "Return structured status and errors to main agent"
9523
+ ],
9524
+ pauseAndReportWhen: [
9525
+ "approvalRequest.required=true",
9526
+ "AUTO_GATE_REACHED",
9527
+ "AUTO_MANUAL_REQUIRED",
9528
+ "command execution error"
9529
+ ],
9530
+ preferredResumeCommand
9531
+ };
9532
+ }
9533
+ function getFeatureRef2(feature) {
9534
+ return feature.folderName;
9535
+ }
9536
+ function toCompactFlowFeature(feature) {
9537
+ if (!feature) return null;
9538
+ return {
9539
+ ref: getFeatureRef2(feature),
9540
+ id: feature.id,
9541
+ slug: feature.slug,
9542
+ type: feature.type,
9543
+ issueNumber: feature.issueNumber,
9544
+ specStatus: feature.specStatus,
9545
+ planStatus: feature.planStatus,
9546
+ tasksDocStatus: feature.tasksDocStatus,
9547
+ currentStep: feature.currentStep,
9548
+ completion: {
9549
+ implementationDone: feature.completion.implementationDone,
9550
+ workflowDone: feature.completion.workflowDone
9551
+ },
9552
+ tasks: feature.tasks,
9553
+ completionChecklist: feature.completionChecklist,
9554
+ warnings: feature.warnings
9555
+ };
9556
+ }
9557
+ function toCompactFlowActionOption(option) {
9558
+ const base = {
9559
+ label: option.label,
9560
+ summary: option.summary,
9561
+ detail: option.detail,
9562
+ approvalPrompt: option.approvalPrompt,
9563
+ requiresRequestText: option.requiresRequestText,
9564
+ replyExample: option.replyExample,
9565
+ actionType: option.action.type,
9566
+ category: option.action.category,
9567
+ operationType: option.action.operationType,
9568
+ requiresUserCheck: !!option.action.requiresUserCheck
9569
+ };
9570
+ if (option.action.type === "command") {
9571
+ base.scope = option.action.scope;
9572
+ base.cwd = option.action.cwd;
9573
+ base.cmd = option.action.cmd;
9574
+ return base;
9575
+ }
9576
+ base.message = option.action.message;
9577
+ return base;
9578
+ }
9579
+ function toCompactFlowContextSnapshot(state) {
9580
+ const primaryAction = state.actionOptions[0] ?? null;
9581
+ return {
9582
+ status: state.status,
9583
+ reasonCode: toReasonCode(state.status),
9584
+ selectionMode: state.selectionMode,
9585
+ selectionFallback: state.selectionFallback,
9586
+ branches: state.branches,
9587
+ warnings: state.warnings,
9588
+ contextVersion: state.contextVersion,
9589
+ matchedFeature: toCompactFlowFeature(state.matchedFeature),
9590
+ candidateRefs: state.targetFeatures.length > 1 ? state.targetFeatures.map((feature) => getFeatureRef2(feature)) : [],
9591
+ completedCandidateRefs: state.selectionMode === "open" ? state.doneFeatures.map((feature) => getFeatureRef2(feature)) : [],
9592
+ openCandidateRefs: state.selectionMode === "open" ? state.openFeatures.map((feature) => getFeatureRef2(feature)) : [],
9593
+ inProgressCandidateRefs: state.selectionMode === "open" ? state.inProgressFeatures.map((feature) => getFeatureRef2(feature)) : [],
9594
+ readyToCloseCandidateRefs: state.selectionMode === "open" ? state.readyToCloseFeatures.map((feature) => getFeatureRef2(feature)) : [],
9595
+ actionOptions: state.actionOptions.map(
9596
+ (option) => toCompactFlowActionOption(option)
9597
+ ),
9598
+ primaryActionLabel: primaryAction?.label ?? null,
9599
+ primaryActionType: primaryAction?.action.type ?? null,
9600
+ primaryActionCategory: primaryAction?.action.category ?? null,
9601
+ primaryActionOperationType: primaryAction?.action.operationType ?? null
9602
+ };
9603
+ }
9604
+ function toCompactAutoRun(autoRun) {
9605
+ if (!autoRun) return null;
9606
+ const lastExecution = autoRun.executions.length > 0 ? autoRun.executions[autoRun.executions.length - 1] : null;
9607
+ return {
9608
+ enabled: autoRun.enabled,
9609
+ status: autoRun.status,
9610
+ reasonCode: autoRun.reasonCode,
9611
+ untilCategories: autoRun.untilCategories,
9612
+ request: autoRun.request,
9613
+ preset: autoRun.preset ?? null,
9614
+ source: autoRun.source ?? null,
9615
+ iterations: autoRun.iterations,
9616
+ executionCount: autoRun.executions.length,
9617
+ lastExecution,
9618
+ gate: autoRun.gate ?? null,
9619
+ manual: autoRun.manual ?? null,
9620
+ resume: autoRun.resume,
9621
+ run: autoRun.run ?? null,
9622
+ error: autoRun.error ?? null
9623
+ };
9624
+ }
9625
+ function toCompactStatusReport(report) {
9626
+ if (!report || typeof report !== "object") return null;
9627
+ const payload = report;
9628
+ return {
9629
+ status: payload.status ?? null,
9630
+ reasonCode: payload.reasonCode ?? null,
9631
+ counts: payload.counts ?? null,
9632
+ recommendation: payload.recommendation ?? null
9633
+ };
9634
+ }
9274
9635
  async function runAutoUntilCategory(config, featureName, selectionOptions, untilCategories, requestText, metadata) {
9275
9636
  const contextArgs = ["context", ...buildSelectionArgs(featureName, selectionOptions)];
9276
9637
  const gateSet = new Set(untilCategories);
@@ -9281,6 +9642,13 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9281
9642
  let requestHandled = !requestText;
9282
9643
  let iterations = 0;
9283
9644
  while (true) {
9645
+ const resume = buildAutoResume(
9646
+ featureName,
9647
+ selectionOptions,
9648
+ untilCategories,
9649
+ requestText,
9650
+ !requestHandled
9651
+ );
9284
9652
  iterations += 1;
9285
9653
  const state = await resolveContextSelection(config, featureName, selectionOptions);
9286
9654
  const actionOptions = state.actionOptions;
@@ -9306,6 +9674,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9306
9674
  request: requestText,
9307
9675
  preset: metadata?.preset ?? null,
9308
9676
  source: metadata?.source ?? null,
9677
+ resume,
9309
9678
  status: "no_progress",
9310
9679
  reasonCode: toAutoReasonCode("no_progress"),
9311
9680
  iterations,
@@ -9322,6 +9691,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9322
9691
  request: requestText,
9323
9692
  preset: metadata?.preset ?? null,
9324
9693
  source: metadata?.source ?? null,
9694
+ resume,
9325
9695
  status: "selection_required",
9326
9696
  reasonCode: toAutoReasonCode("selection_required"),
9327
9697
  iterations,
@@ -9338,6 +9708,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9338
9708
  request: requestText,
9339
9709
  preset: metadata?.preset ?? null,
9340
9710
  source: metadata?.source ?? null,
9711
+ resume,
9341
9712
  status: "no_action_options",
9342
9713
  reasonCode: toAutoReasonCode("no_action_options"),
9343
9714
  iterations,
@@ -9357,6 +9728,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9357
9728
  request: requestText,
9358
9729
  preset: metadata?.preset ?? null,
9359
9730
  source: metadata?.source ?? null,
9731
+ resume,
9360
9732
  status: "request_label_missing",
9361
9733
  reasonCode: toAutoReasonCode("request_label_missing"),
9362
9734
  iterations,
@@ -9390,6 +9762,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9390
9762
  request: requestText,
9391
9763
  preset: metadata?.preset ?? null,
9392
9764
  source: metadata?.source ?? null,
9765
+ resume,
9393
9766
  status: "request_failed",
9394
9767
  reasonCode: toAutoReasonCode("request_failed"),
9395
9768
  iterations,
@@ -9413,6 +9786,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9413
9786
  request: requestText,
9414
9787
  preset: metadata?.preset ?? null,
9415
9788
  source: metadata?.source ?? null,
9789
+ resume,
9416
9790
  status: "gate_reached",
9417
9791
  reasonCode: toAutoReasonCode("gate_reached"),
9418
9792
  iterations,
@@ -9435,6 +9809,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9435
9809
  request: requestText,
9436
9810
  preset: metadata?.preset ?? null,
9437
9811
  source: metadata?.source ?? null,
9812
+ resume,
9438
9813
  status: "manual_required",
9439
9814
  reasonCode: toAutoReasonCode("manual_required"),
9440
9815
  iterations,
@@ -9470,6 +9845,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9470
9845
  request: requestText,
9471
9846
  preset: metadata?.preset ?? null,
9472
9847
  source: metadata?.source ?? null,
9848
+ resume,
9473
9849
  status: "execution_failed",
9474
9850
  reasonCode: toAutoReasonCode("execution_failed"),
9475
9851
  iterations,
@@ -9505,6 +9881,9 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9505
9881
  enabled: true,
9506
9882
  untilCategories,
9507
9883
  request: requestText,
9884
+ preset: metadata?.preset ?? null,
9885
+ source: metadata?.source ?? null,
9886
+ resume,
9508
9887
  status: "execution_failed",
9509
9888
  reasonCode: toAutoReasonCode("execution_failed"),
9510
9889
  iterations,
@@ -9517,7 +9896,10 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
9517
9896
  }
9518
9897
  }
9519
9898
  function flowCommand(program2) {
9520
- program2.command("flow [feature-name]").description("Run combined workflow checks (context + status + doctor)").option("--json", "Output in JSON format for agents").option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option(
9899
+ program2.command("flow [feature-name]").description("Run combined workflow checks (context + status + doctor)").option("--json", "Output in JSON format for agents").option(
9900
+ "--json-compact",
9901
+ "Output compact JSON for agents (implies --json, reduced duplication)"
9902
+ ).option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option(
9521
9903
  "--request <text>",
9522
9904
  "Apply a new user request first via user_request_replan when auto mode is enabled"
9523
9905
  ).option(
@@ -9526,6 +9908,12 @@ function flowCommand(program2) {
9526
9908
  ).option(
9527
9909
  "--auto-until-category <categories>",
9528
9910
  "Auto-run command actions until one of categories appears (comma-separated)"
9911
+ ).option(
9912
+ "--start-auto",
9913
+ "Persist auto-run checkpoint and emit resumable run id in JSON output"
9914
+ ).option(
9915
+ "--resume <run-id>",
9916
+ "Resume previously started auto-run checkpoint by run id"
9529
9917
  ).option(
9530
9918
  "--approve <reply>",
9531
9919
  "Approve one labeled context option (examples: A, A OK, A proceed, A \uC9C4\uD589\uD574)"
@@ -9540,7 +9928,7 @@ function flowCommand(program2) {
9540
9928
  const lang = config?.lang ?? DEFAULT_LANG;
9541
9929
  const cliError = toCliError(error);
9542
9930
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
9543
- if (options.json) {
9931
+ if (options.json || options.jsonCompact) {
9544
9932
  console.log(
9545
9933
  JSON.stringify({
9546
9934
  status: "error",
@@ -9581,14 +9969,79 @@ async function runFlow(featureName, options) {
9581
9969
  "`--execute` requires `--approve <reply>`."
9582
9970
  );
9583
9971
  }
9584
- const requestText = options.request?.trim() || void 0;
9972
+ const resumeRunId = (options.resume || "").trim() || void 0;
9973
+ if (options.startAuto && resumeRunId) {
9974
+ throw createCliError(
9975
+ "INVALID_ARGUMENT",
9976
+ "`--start-auto` cannot be combined with `--resume`."
9977
+ );
9978
+ }
9979
+ if (resumeRunId) {
9980
+ if (featureName) {
9981
+ throw createCliError(
9982
+ "INVALID_ARGUMENT",
9983
+ "`--resume` cannot be combined with <feature-name>."
9984
+ );
9985
+ }
9986
+ if (options.autoPreset || options.autoUntilCategory || options.request) {
9987
+ throw createCliError(
9988
+ "INVALID_ARGUMENT",
9989
+ "`--resume` cannot be combined with `--auto-*` or `--request`."
9990
+ );
9991
+ }
9992
+ if (options.component || options.all || options.done) {
9993
+ throw createCliError(
9994
+ "INVALID_ARGUMENT",
9995
+ "`--resume` cannot be combined with `--component`, `--all`, or `--done`."
9996
+ );
9997
+ }
9998
+ }
9999
+ let requestText = options.request?.trim() || void 0;
9585
10000
  if (options.autoPreset && options.autoUntilCategory) {
9586
10001
  throw createCliError(
9587
10002
  "INVALID_ARGUMENT",
9588
10003
  "`--auto-preset` cannot be combined with `--auto-until-category`."
9589
10004
  );
9590
10005
  }
9591
- const autoMode = resolveAutoMode(config, options, requestText);
10006
+ let resolvedFeatureName = featureName;
10007
+ let selectedComponent = resolveComponentOption(options.component);
10008
+ const selectionOptions = {
10009
+ component: selectedComponent,
10010
+ all: options.all,
10011
+ done: options.done
10012
+ };
10013
+ let autoMode = resolveAutoMode(config, options, requestText);
10014
+ let flowRunRecord = null;
10015
+ let flowRunMode = null;
10016
+ if (resumeRunId) {
10017
+ flowRunRecord = await getFlowRunRecord(process.cwd(), resumeRunId);
10018
+ if (flowRunRecord.status === "completed") {
10019
+ throw createCliError(
10020
+ "INVALID_ARGUMENT",
10021
+ `Flow run ${resumeRunId} is already completed.`
10022
+ );
10023
+ }
10024
+ resolvedFeatureName = flowRunRecord.featureName;
10025
+ selectedComponent = resolveComponentOption(flowRunRecord.selection.component);
10026
+ selectionOptions.component = selectedComponent;
10027
+ selectionOptions.all = !!flowRunRecord.selection.all;
10028
+ selectionOptions.done = !!flowRunRecord.selection.done;
10029
+ requestText = flowRunRecord.auto.requestPending ? flowRunRecord.auto.requestText?.trim() || void 0 : void 0;
10030
+ autoMode = {
10031
+ untilCategories: [...flowRunRecord.auto.untilCategories],
10032
+ preset: flowRunRecord.auto.preset ?? null,
10033
+ source: `resume:${resumeRunId}`
10034
+ };
10035
+ flowRunMode = "resumed";
10036
+ flowRunRecord = await updateFlowRunRecord(
10037
+ process.cwd(),
10038
+ resumeRunId,
10039
+ (current) => ({
10040
+ ...current,
10041
+ status: "running"
10042
+ })
10043
+ );
10044
+ }
9592
10045
  if (autoMode && options.approve) {
9593
10046
  throw createCliError(
9594
10047
  "INVALID_ARGUMENT",
@@ -9608,26 +10061,46 @@ async function runFlow(featureName, options) {
9608
10061
  );
9609
10062
  }
9610
10063
  if (autoMode && !featureName) {
10064
+ if (!resolvedFeatureName) {
10065
+ throw createCliError(
10066
+ "CONTEXT_SELECTION_REQUIRED",
10067
+ "Auto mode requires explicit <feature-name> (e.g. F004)."
10068
+ );
10069
+ }
10070
+ }
10071
+ if (options.startAuto && !autoMode) {
9611
10072
  throw createCliError(
9612
- "CONTEXT_SELECTION_REQUIRED",
9613
- "Auto mode requires explicit <feature-name> (e.g. F004)."
10073
+ "INVALID_ARGUMENT",
10074
+ "`--start-auto` requires auto mode (`--auto-until-category` or `--auto-preset`)."
9614
10075
  );
9615
10076
  }
9616
- const selectedComponent = resolveComponentOption(options.component);
9617
- const selectionOptions = {
9618
- component: selectedComponent,
9619
- all: options.all,
9620
- done: options.done
9621
- };
10077
+ if (options.startAuto && autoMode && !flowRunRecord && resolvedFeatureName) {
10078
+ flowRunRecord = await createFlowRunRecord(process.cwd(), {
10079
+ featureName: resolvedFeatureName,
10080
+ selection: {
10081
+ component: selectedComponent || void 0,
10082
+ all: !!selectionOptions.all,
10083
+ done: !!selectionOptions.done
10084
+ },
10085
+ auto: {
10086
+ untilCategories: [...autoMode.untilCategories],
10087
+ requestText,
10088
+ requestPending: !!requestText,
10089
+ preset: autoMode.preset ?? null,
10090
+ source: autoMode.source
10091
+ }
10092
+ });
10093
+ flowRunMode = "started";
10094
+ }
9622
10095
  const componentHint = selectedComponent ? ` --component ${selectedComponent}` : "";
9623
- const before = await resolveContextSelection(config, featureName, selectionOptions);
10096
+ const before = await resolveContextSelection(config, resolvedFeatureName, selectionOptions);
9624
10097
  let approvalResult = null;
9625
10098
  let autoRun = null;
9626
- const contextArgs = ["context", ...buildSelectionArgs(featureName, selectionOptions)];
10099
+ const contextArgs = ["context", ...buildSelectionArgs(resolvedFeatureName, selectionOptions)];
9627
10100
  if (autoMode) {
9628
10101
  autoRun = await runAutoUntilCategory(
9629
10102
  config,
9630
- featureName,
10103
+ resolvedFeatureName,
9631
10104
  selectionOptions,
9632
10105
  autoMode.untilCategories,
9633
10106
  requestText,
@@ -9659,7 +10132,31 @@ async function runFlow(featureName, options) {
9659
10132
  approvalResult = selected;
9660
10133
  }
9661
10134
  }
9662
- const after = await resolveContextSelection(config, featureName, selectionOptions);
10135
+ if (autoRun && flowRunRecord && flowRunMode) {
10136
+ const runStatus2 = toFlowRunStatus(autoRun.status);
10137
+ flowRunRecord = await updateFlowRunRecord(
10138
+ process.cwd(),
10139
+ flowRunRecord.runId,
10140
+ (current) => ({
10141
+ ...current,
10142
+ status: runStatus2,
10143
+ auto: {
10144
+ ...current.auto,
10145
+ requestPending: autoRun.resume.requestPending
10146
+ },
10147
+ lastAutoStatus: autoRun.status,
10148
+ lastReasonCode: autoRun.reasonCode,
10149
+ lastError: autoRun.error
10150
+ })
10151
+ );
10152
+ autoRun.run = {
10153
+ runId: flowRunRecord.runId,
10154
+ mode: flowRunMode,
10155
+ status: flowRunRecord.status,
10156
+ resumeCommand: buildResumeRunCommand(flowRunRecord.runId)
10157
+ };
10158
+ }
10159
+ const after = await resolveContextSelection(config, resolvedFeatureName, selectionOptions);
9663
10160
  const statusReport = runSelfCliJson(["status"]);
9664
10161
  const doctorReport = runSelfCliJson(["doctor"]);
9665
10162
  let strictChecks = null;
@@ -9678,11 +10175,38 @@ async function runFlow(featureName, options) {
9678
10175
  );
9679
10176
  }
9680
10177
  }
9681
- if (options.json) {
10178
+ const jsonMode = !!options.json || !!options.jsonCompact;
10179
+ if (jsonMode) {
9682
10180
  const autoRunFailed = !!(autoRun && isAutoRunFailureStatus(autoRun.status));
10181
+ const agentOrchestration2 = buildAgentOrchestrationPolicy2(autoRun);
10182
+ const status = autoRunFailed ? "error" : "ok";
10183
+ const reasonCode = autoRunFailed ? autoRun?.reasonCode || "AUTO_EXECUTION_FAILED" : "FLOW_SUMMARY";
10184
+ if (options.jsonCompact) {
10185
+ const compactPayload = {
10186
+ schema: "flow.v2.compact",
10187
+ status,
10188
+ reasonCode,
10189
+ context: {
10190
+ before: toCompactFlowContextSnapshot(before),
10191
+ after: toCompactFlowContextSnapshot(after)
10192
+ },
10193
+ approval: approvalResult,
10194
+ autoRun: toCompactAutoRun(autoRun),
10195
+ agentOrchestration: agentOrchestration2,
10196
+ statusReport: toCompactStatusReport(statusReport),
10197
+ doctorReport: toCompactStatusReport(doctorReport),
10198
+ strictChecks,
10199
+ suggestion: after.matchedFeature ? `npx lee-spec-kit context ${after.matchedFeature.folderName}${componentHint}` : `npx lee-spec-kit context${componentHint}`
10200
+ };
10201
+ console.log(JSON.stringify(compactPayload, null, 2));
10202
+ if (autoRunFailed) {
10203
+ process.exitCode = 1;
10204
+ }
10205
+ return;
10206
+ }
9683
10207
  const payload = {
9684
- status: autoRunFailed ? "error" : "ok",
9685
- reasonCode: autoRunFailed ? autoRun?.reasonCode || "AUTO_EXECUTION_FAILED" : "FLOW_SUMMARY",
10208
+ status,
10209
+ reasonCode,
9686
10210
  context: {
9687
10211
  before: {
9688
10212
  status: before.status,
@@ -9705,6 +10229,7 @@ async function runFlow(featureName, options) {
9705
10229
  },
9706
10230
  approval: approvalResult,
9707
10231
  autoRun,
10232
+ agentOrchestration: agentOrchestration2,
9708
10233
  statusReport,
9709
10234
  doctorReport,
9710
10235
  strictChecks,
@@ -9738,7 +10263,22 @@ async function runFlow(featureName, options) {
9738
10263
  `- Auto: ${autoRun.status} (${autoRun.reasonCode}), iterations ${autoRun.iterations}, executions ${autoRun.executions.length}${presetSuffix}`
9739
10264
  )
9740
10265
  );
10266
+ console.log(chalk6.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
10267
+ if (autoRun.run) {
10268
+ console.log(
10269
+ chalk6.gray(
10270
+ `- Auto run: ${autoRun.run.mode} ${autoRun.run.runId} (${autoRun.run.status})`
10271
+ )
10272
+ );
10273
+ console.log(chalk6.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
10274
+ }
9741
10275
  }
10276
+ const agentOrchestration = buildAgentOrchestrationPolicy2(autoRun);
10277
+ console.log(
10278
+ chalk6.gray(
10279
+ `- Orchestration: ${agentOrchestration.mode}, delegate long-running loops to sub-agent`
10280
+ )
10281
+ );
9742
10282
  const statusCounts = statusReport.counts;
9743
10283
  const doctorCounts = doctorReport.counts;
9744
10284
  console.log(chalk6.gray(`- Status features: ${statusCounts?.features ?? 0}`));
@@ -9886,40 +10426,40 @@ function tg(lang, key, vars = {}) {
9886
10426
  }
9887
10427
  function detectGithubCliLangSync(cwd) {
9888
10428
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
9889
- const startDirs = [explicitDocsDir ? path22.resolve(explicitDocsDir) : "", path22.resolve(cwd)].filter(Boolean);
10429
+ const startDirs = [explicitDocsDir ? path23.resolve(explicitDocsDir) : "", path23.resolve(cwd)].filter(Boolean);
9890
10430
  const scanOrder = [];
9891
10431
  const seen = /* @__PURE__ */ new Set();
9892
10432
  for (const start of startDirs) {
9893
10433
  let current = start;
9894
10434
  while (true) {
9895
- const abs = path22.resolve(current);
10435
+ const abs = path23.resolve(current);
9896
10436
  if (!seen.has(abs)) {
9897
10437
  scanOrder.push(abs);
9898
10438
  seen.add(abs);
9899
10439
  }
9900
- const parent = path22.dirname(abs);
10440
+ const parent = path23.dirname(abs);
9901
10441
  if (parent === abs) break;
9902
10442
  current = parent;
9903
10443
  }
9904
10444
  }
9905
10445
  for (const base of scanOrder) {
9906
- for (const docsDir of [path22.join(base, "docs"), base]) {
9907
- const configPath = path22.join(docsDir, ".lee-spec-kit.json");
9908
- if (fs16.existsSync(configPath)) {
10446
+ for (const docsDir of [path23.join(base, "docs"), base]) {
10447
+ const configPath = path23.join(docsDir, ".lee-spec-kit.json");
10448
+ if (fs17.existsSync(configPath)) {
9909
10449
  try {
9910
- const parsed = fs16.readJsonSync(configPath);
10450
+ const parsed = fs17.readJsonSync(configPath);
9911
10451
  if (parsed?.lang === "ko" || parsed?.lang === "en") return parsed.lang;
9912
10452
  } catch {
9913
10453
  }
9914
10454
  }
9915
- const agentsPath = path22.join(docsDir, "agents");
9916
- const featuresPath = path22.join(docsDir, "features");
9917
- if (!fs16.existsSync(agentsPath) || !fs16.existsSync(featuresPath)) continue;
10455
+ const agentsPath = path23.join(docsDir, "agents");
10456
+ const featuresPath = path23.join(docsDir, "features");
10457
+ if (!fs17.existsSync(agentsPath) || !fs17.existsSync(featuresPath)) continue;
9918
10458
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
9919
- const file = path22.join(agentsPath, probe);
9920
- if (!fs16.existsSync(file)) continue;
10459
+ const file = path23.join(agentsPath, probe);
10460
+ if (!fs17.existsSync(file)) continue;
9921
10461
  try {
9922
- const content = fs16.readFileSync(file, "utf-8");
10462
+ const content = fs17.readFileSync(file, "utf-8");
9923
10463
  if (/[가-힣]/.test(content)) return "ko";
9924
10464
  } catch {
9925
10465
  }
@@ -9996,8 +10536,8 @@ async function prepareGithubBody(params) {
9996
10536
  kindLabel,
9997
10537
  lang
9998
10538
  } = params;
9999
- if (create && explicitBodyFile && await fs16.pathExists(defaultBodyFile)) {
10000
- const body = await fs16.readFile(defaultBodyFile, "utf-8");
10539
+ if (create && explicitBodyFile && await fs17.pathExists(defaultBodyFile)) {
10540
+ const body = await fs17.readFile(defaultBodyFile, "utf-8");
10001
10541
  ensureSections(body, requiredSections, kindLabel, lang);
10002
10542
  return {
10003
10543
  body,
@@ -10005,8 +10545,8 @@ async function prepareGithubBody(params) {
10005
10545
  source: "explicit"
10006
10546
  };
10007
10547
  }
10008
- if (create && !explicitBodyFile && await fs16.pathExists(workflowDraftPath)) {
10009
- const body = await fs16.readFile(workflowDraftPath, "utf-8");
10548
+ if (create && !explicitBodyFile && await fs17.pathExists(workflowDraftPath)) {
10549
+ const body = await fs17.readFile(workflowDraftPath, "utf-8");
10010
10550
  const draftMetadata = parseWorkflowDraftMetadata(body);
10011
10551
  if (draftMetadata.status === "ready") {
10012
10552
  ensureSections(body, requiredSections, kindLabel, lang);
@@ -10018,8 +10558,8 @@ async function prepareGithubBody(params) {
10018
10558
  };
10019
10559
  }
10020
10560
  }
10021
- await fs16.ensureDir(path22.dirname(defaultBodyFile));
10022
- await fs16.writeFile(defaultBodyFile, generatedBody, "utf-8");
10561
+ await fs17.ensureDir(path23.dirname(defaultBodyFile));
10562
+ await fs17.writeFile(defaultBodyFile, generatedBody, "utf-8");
10023
10563
  return {
10024
10564
  body: generatedBody,
10025
10565
  bodyFile: defaultBodyFile,
@@ -10079,7 +10619,7 @@ function ensureSections(body, sections, kind, lang) {
10079
10619
  }
10080
10620
  function ensureDocsExist(docsDir, relativePaths, lang) {
10081
10621
  const missing = relativePaths.filter(
10082
- (relativePath) => !fs16.existsSync(path22.join(docsDir, relativePath))
10622
+ (relativePath) => !fs17.existsSync(path23.join(docsDir, relativePath))
10083
10623
  );
10084
10624
  if (missing.length > 0) {
10085
10625
  throw createCliError(
@@ -10089,18 +10629,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
10089
10629
  }
10090
10630
  }
10091
10631
  function buildDefaultBodyFileName(kind, docsDir, component) {
10092
- const key = `${path22.resolve(docsDir)}::${component.trim().toLowerCase()}`;
10632
+ const key = `${path23.resolve(docsDir)}::${component.trim().toLowerCase()}`;
10093
10633
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
10094
10634
  return `lee-spec-kit.${digest}.${kind}.md`;
10095
10635
  }
10096
10636
  function toBodyFilePath(raw, kind, docsDir, component, lang) {
10097
- const selected = raw?.trim() || path22.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
10637
+ const selected = raw?.trim() || path23.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
10098
10638
  assertValid(
10099
10639
  validatePathWithLang(selected, lang),
10100
10640
  `github.${kind}.bodyFile`,
10101
10641
  lang
10102
10642
  );
10103
- return path22.resolve(selected);
10643
+ return path23.resolve(selected);
10104
10644
  }
10105
10645
  function toProjectRootDocsPath(relativePathFromDocs) {
10106
10646
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -10867,10 +11407,10 @@ function insertFieldInGithubIssueSection(content, key, value) {
10867
11407
  return { content: lines.join("\n"), changed: true };
10868
11408
  }
10869
11409
  function syncTasksPrMetadata(tasksPath, prUrl, nextStatus, lang) {
10870
- if (!fs16.existsSync(tasksPath)) {
11410
+ if (!fs17.existsSync(tasksPath)) {
10871
11411
  throw createCliError("DOCS_NOT_FOUND", tg(lang, "tasksNotFound", { path: tasksPath }));
10872
11412
  }
10873
- const original = fs16.readFileSync(tasksPath, "utf-8");
11413
+ const original = fs17.readFileSync(tasksPath, "utf-8");
10874
11414
  let next = original;
10875
11415
  let changed = false;
10876
11416
  const prReplaced = replaceListField(next, ["PR", "Pull Request"], prUrl);
@@ -10894,7 +11434,7 @@ function syncTasksPrMetadata(tasksPath, prUrl, nextStatus, lang) {
10894
11434
  changed = changed || inserted.changed;
10895
11435
  }
10896
11436
  if (changed) {
10897
- fs16.writeFileSync(tasksPath, next, "utf-8");
11437
+ fs17.writeFileSync(tasksPath, next, "utf-8");
10898
11438
  }
10899
11439
  return { changed, path: tasksPath };
10900
11440
  }
@@ -10922,7 +11462,7 @@ function ensureCleanWorktree(cwd, lang) {
10922
11462
  }
10923
11463
  }
10924
11464
  function commitAndPushPath(cwd, absPath, message, lang, options) {
10925
- const relativePath = path22.relative(cwd, absPath) || absPath;
11465
+ const relativePath = path23.relative(cwd, absPath) || absPath;
10926
11466
  const status = runProcessOrThrow(
10927
11467
  "git",
10928
11468
  ["status", "--porcelain=v1", "--", relativePath],
@@ -11091,9 +11631,9 @@ function githubCommand(program2) {
11091
11631
  [paths.specPath, paths.planPath, paths.tasksPath],
11092
11632
  config.lang
11093
11633
  );
11094
- const specContent = await fs16.readFile(path22.join(config.docsDir, paths.specPath), "utf-8");
11095
- const planContent = await fs16.readFile(path22.join(config.docsDir, paths.planPath), "utf-8");
11096
- const tasksContent = await fs16.readFile(path22.join(config.docsDir, paths.tasksPath), "utf-8");
11634
+ const specContent = await fs17.readFile(path23.join(config.docsDir, paths.specPath), "utf-8");
11635
+ const planContent = await fs17.readFile(path23.join(config.docsDir, paths.planPath), "utf-8");
11636
+ const tasksContent = await fs17.readFile(path23.join(config.docsDir, paths.tasksPath), "utf-8");
11097
11637
  const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
11098
11638
  const defaultTitle = tg(config.lang, "issueDefaultTitle", {
11099
11639
  slug: feature.slug,
@@ -11126,7 +11666,7 @@ function githubCommand(program2) {
11126
11666
  create: options.create,
11127
11667
  explicitBodyFile,
11128
11668
  defaultBodyFile,
11129
- workflowDraftPath: path22.join(config.docsDir, paths.issuePath),
11669
+ workflowDraftPath: path23.join(config.docsDir, paths.issuePath),
11130
11670
  generatedBody,
11131
11671
  requiredSections: getRequiredIssueSections(config.lang),
11132
11672
  kindLabel: tg(config.lang, "kindIssue"),
@@ -11237,10 +11777,10 @@ function githubCommand(program2) {
11237
11777
  const generatedLabels = parseLabels(optionLabels || void 0, config.lang);
11238
11778
  const paths = getFeatureDocPaths(feature);
11239
11779
  ensureDocsExist(config.docsDir, [paths.specPath, paths.tasksPath], config.lang);
11240
- const specContent = await fs16.readFile(path22.join(config.docsDir, paths.specPath), "utf-8");
11241
- const planPath = path22.join(config.docsDir, paths.planPath);
11242
- const planContent = await fs16.pathExists(planPath) ? await fs16.readFile(planPath, "utf-8") : "";
11243
- const tasksContent = await fs16.readFile(path22.join(config.docsDir, paths.tasksPath), "utf-8");
11780
+ const specContent = await fs17.readFile(path23.join(config.docsDir, paths.specPath), "utf-8");
11781
+ const planPath = path23.join(config.docsDir, paths.planPath);
11782
+ const planContent = await fs17.pathExists(planPath) ? await fs17.readFile(planPath, "utf-8") : "";
11783
+ const tasksContent = await fs17.readFile(path23.join(config.docsDir, paths.tasksPath), "utf-8");
11244
11784
  const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
11245
11785
  const defaultTitle = feature.issueNumber ? tg(config.lang, "prDefaultTitleWithIssue", {
11246
11786
  issue: feature.issueNumber,
@@ -11279,7 +11819,7 @@ function githubCommand(program2) {
11279
11819
  create: options.create,
11280
11820
  explicitBodyFile,
11281
11821
  defaultBodyFile,
11282
- workflowDraftPath: path22.join(config.docsDir, paths.prPath),
11822
+ workflowDraftPath: path23.join(config.docsDir, paths.prPath),
11283
11823
  generatedBody,
11284
11824
  requiredSections: getRequiredPrSections(config.lang),
11285
11825
  kindLabel: tg(config.lang, "kindPr"),
@@ -11350,7 +11890,7 @@ function githubCommand(program2) {
11350
11890
  }
11351
11891
  if (prUrl && options.syncTasks !== false) {
11352
11892
  const synced = syncTasksPrMetadata(
11353
- path22.join(config.docsDir, paths.tasksPath),
11893
+ path23.join(config.docsDir, paths.tasksPath),
11354
11894
  prUrl,
11355
11895
  "Review",
11356
11896
  config.lang
@@ -11386,7 +11926,7 @@ function githubCommand(program2) {
11386
11926
  mergeAlreadyMerged = merged.alreadyMerged;
11387
11927
  if (prUrl && options.syncTasks !== false) {
11388
11928
  const mergedSync = syncTasksPrMetadata(
11389
- path22.join(config.docsDir, paths.tasksPath),
11929
+ path23.join(config.docsDir, paths.tasksPath),
11390
11930
  prUrl,
11391
11931
  "Approved",
11392
11932
  config.lang
@@ -11630,7 +12170,7 @@ function docsCommand(program2) {
11630
12170
  );
11631
12171
  return;
11632
12172
  }
11633
- const relativeFromCwd = path22.relative(process.cwd(), loaded.entry.absolutePath);
12173
+ const relativeFromCwd = path23.relative(process.cwd(), loaded.entry.absolutePath);
11634
12174
  console.log();
11635
12175
  console.log(chalk6.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
11636
12176
  console.log(
@@ -11710,7 +12250,7 @@ function detectCommand(program2) {
11710
12250
  }
11711
12251
  async function runDetect(options) {
11712
12252
  const cwd = process.cwd();
11713
- const targetCwd = options.dir ? path22.resolve(cwd, options.dir) : cwd;
12253
+ const targetCwd = options.dir ? path23.resolve(cwd, options.dir) : cwd;
11714
12254
  const config = await getConfig(targetCwd);
11715
12255
  const detected = !!config;
11716
12256
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
@@ -11737,8 +12277,8 @@ async function runDetect(options) {
11737
12277
  );
11738
12278
  return;
11739
12279
  }
11740
- const configPath2 = path22.join(config.docsDir, ".lee-spec-kit.json");
11741
- const configFilePresent2 = await fs16.pathExists(configPath2);
12280
+ const configPath2 = path23.join(config.docsDir, ".lee-spec-kit.json");
12281
+ const configFilePresent2 = await fs17.pathExists(configPath2);
11742
12282
  const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
11743
12283
  console.log(
11744
12284
  JSON.stringify(
@@ -11771,8 +12311,8 @@ async function runDetect(options) {
11771
12311
  console.log();
11772
12312
  return;
11773
12313
  }
11774
- const configPath = path22.join(config.docsDir, ".lee-spec-kit.json");
11775
- const configFilePresent = await fs16.pathExists(configPath);
12314
+ const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
12315
+ const configFilePresent = await fs17.pathExists(configPath);
11776
12316
  const detectionSource = configFilePresent ? "config" : "heuristic";
11777
12317
  console.log(chalk6.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
11778
12318
  console.log(chalk6.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
@@ -11848,28 +12388,28 @@ function hasTemplateMarkers(content) {
11848
12388
  return patterns.some((pattern) => pattern.test(content));
11849
12389
  }
11850
12390
  async function countFeatureDirs(docsDir, projectType) {
11851
- const featuresRoot = path22.join(docsDir, "features");
12391
+ const featuresRoot = path23.join(docsDir, "features");
11852
12392
  if (projectType === "single") {
11853
12393
  const dirs = await listSubdirectories(featuresRoot);
11854
- return dirs.filter((value) => path22.basename(value) !== "feature-base").length;
12394
+ return dirs.filter((value) => path23.basename(value) !== "feature-base").length;
11855
12395
  }
11856
12396
  const components = await listSubdirectories(featuresRoot);
11857
12397
  let total = 0;
11858
12398
  for (const componentDir of components) {
11859
- const componentName = path22.basename(componentDir).trim().toLowerCase();
12399
+ const componentName = path23.basename(componentDir).trim().toLowerCase();
11860
12400
  if (!componentName || componentName === "feature-base") continue;
11861
12401
  const dirs = await listSubdirectories(componentDir);
11862
- total += dirs.filter((value) => path22.basename(value) !== "feature-base").length;
12402
+ total += dirs.filter((value) => path23.basename(value) !== "feature-base").length;
11863
12403
  }
11864
12404
  return total;
11865
12405
  }
11866
12406
  async function hasUserPrdFile(prdDir) {
11867
- if (!await fs16.pathExists(prdDir)) return false;
12407
+ if (!await fs17.pathExists(prdDir)) return false;
11868
12408
  const files = await walkFiles(prdDir, {
11869
12409
  extensions: [".md"],
11870
12410
  ignoreDirs: ["node_modules"]
11871
12411
  });
11872
- return files.some((absolutePath) => path22.basename(absolutePath).toLowerCase() !== "readme.md");
12412
+ return files.some((absolutePath) => path23.basename(absolutePath).toLowerCase() !== "readme.md");
11873
12413
  }
11874
12414
  function finalizeChecks(checks) {
11875
12415
  const summary = checks.reduce(
@@ -11998,8 +12538,8 @@ async function runOnboardChecks(config) {
11998
12538
  });
11999
12539
  }
12000
12540
  }
12001
- const constitutionPath = path22.join(docsDir, "agents", "constitution.md");
12002
- if (!await fs16.pathExists(constitutionPath)) {
12541
+ const constitutionPath = path23.join(docsDir, "agents", "constitution.md");
12542
+ if (!await fs17.pathExists(constitutionPath)) {
12003
12543
  checks.push({
12004
12544
  id: "constitution_exists",
12005
12545
  status: "block",
@@ -12013,7 +12553,7 @@ async function runOnboardChecks(config) {
12013
12553
  suggestedCommand: `npx lee-spec-kit update --agents`
12014
12554
  });
12015
12555
  } else {
12016
- const content = await fs16.readFile(constitutionPath, "utf-8");
12556
+ const content = await fs17.readFile(constitutionPath, "utf-8");
12017
12557
  if (hasTemplateMarkers(content)) {
12018
12558
  checks.push({
12019
12559
  id: "constitution_filled",
@@ -12036,9 +12576,9 @@ async function runOnboardChecks(config) {
12036
12576
  });
12037
12577
  }
12038
12578
  }
12039
- const customPath = path22.join(docsDir, "agents", "custom.md");
12040
- if (await fs16.pathExists(customPath)) {
12041
- const content = await fs16.readFile(customPath, "utf-8");
12579
+ const customPath = path23.join(docsDir, "agents", "custom.md");
12580
+ if (await fs17.pathExists(customPath)) {
12581
+ const content = await fs17.readFile(customPath, "utf-8");
12042
12582
  if (hasTemplateMarkers(content)) {
12043
12583
  checks.push({
12044
12584
  id: "custom_optional",
@@ -12061,7 +12601,7 @@ async function runOnboardChecks(config) {
12061
12601
  });
12062
12602
  }
12063
12603
  }
12064
- const prdDir = path22.join(docsDir, "prd");
12604
+ const prdDir = path23.join(docsDir, "prd");
12065
12605
  const featureCount = await countFeatureDirs(docsDir, config.projectType);
12066
12606
  const prdReady = await hasUserPrdFile(prdDir);
12067
12607
  if (!prdReady) {
@@ -12079,7 +12619,7 @@ async function runOnboardChecks(config) {
12079
12619
  "PRD is empty. If features already exist, fill PRD as soon as possible."
12080
12620
  ),
12081
12621
  path: prdDir,
12082
- suggestedCommand: `touch ${quotePath(path22.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
12622
+ suggestedCommand: `touch ${quotePath(path23.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
12083
12623
  });
12084
12624
  } else {
12085
12625
  checks.push({
@@ -12263,17 +12803,6 @@ function normalizeDecision(raw) {
12263
12803
  if (value === "blocked" || value === "block") return "blocked";
12264
12804
  return null;
12265
12805
  }
12266
- function parseNonNegativeInt(raw, label) {
12267
- if (raw === void 0) return null;
12268
- const value = Number(raw);
12269
- if (!Number.isInteger(value) || value < 0) {
12270
- throw createCliError(
12271
- "INVALID_ARGUMENT",
12272
- `\`${label}\` must be a non-negative integer.`
12273
- );
12274
- }
12275
- return value;
12276
- }
12277
12806
  function findSpecLineIndex(lines, keys) {
12278
12807
  const escaped = keys.map((key) => escapeRegExp4(key));
12279
12808
  const re = new RegExp(`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`);
@@ -12320,7 +12849,6 @@ function getPreferredKeys(lang) {
12320
12849
  if (lang === "ko") {
12321
12850
  return {
12322
12851
  review: "PR \uC804 \uB9AC\uBDF0",
12323
- findings: "PR \uC804 \uB9AC\uBDF0 Findings",
12324
12852
  evidence: "PR \uC804 \uB9AC\uBDF0 Evidence",
12325
12853
  decision: "PR \uC804 \uB9AC\uBDF0 Decision",
12326
12854
  prStatus: "PR \uC0C1\uD0DC"
@@ -12328,7 +12856,6 @@ function getPreferredKeys(lang) {
12328
12856
  }
12329
12857
  return {
12330
12858
  review: "Pre-PR Review",
12331
- findings: "Pre-PR Findings",
12332
12859
  evidence: "Pre-PR Evidence",
12333
12860
  decision: "Pre-PR Decision",
12334
12861
  prStatus: "PR Status"
@@ -12336,28 +12863,30 @@ function getPreferredKeys(lang) {
12336
12863
  }
12337
12864
  function buildReportContent(input) {
12338
12865
  const skills = input.skills.length > 0 ? input.skills.join(", ") : "code-review-excellence";
12339
- return `# Pre-PR Review Report: ${input.folderName}
12866
+ return `## Pre-PR Review Log (${input.date})
12340
12867
 
12341
- - Date: ${input.date}
12342
- - Baseline: ${input.fallback}
12343
- - Skills: ${skills}
12344
- - Findings: major=${input.major}, minor=${input.minor}
12345
- - Decision: ${input.decision}
12346
- - Note: ${input.note}
12347
-
12348
- ## Baseline Checklist
12868
+ - **Feature**: ${input.folderName}
12869
+ - **Baseline**: ${input.fallback}
12870
+ - **Skills**: ${skills}
12871
+ - **Decision**: ${input.decision}
12872
+ - **Note**: ${input.note}
12873
+ - **Trace**: pre-pr-review command executed and synced with tasks.md
12874
+ `;
12875
+ }
12876
+ function appendDecisionLog(content, entry) {
12877
+ const normalized = content.trimEnd();
12878
+ if (!normalized) return `${entry.trim()}
12879
+ `;
12880
+ return `${normalized}
12349
12881
 
12350
- - [x] Checked alignment with spec/plan/tasks.
12351
- - [x] Reviewed risk areas (regression, security, side effects, release readiness).
12352
- - [x] Reviewed maintainability (structure, reuse, obsolete code cleanup).
12353
- - [x] Reviewed test/verification coverage (or recorded reason if not run).
12882
+ ${entry.trim()}
12354
12883
  `;
12355
12884
  }
12356
12885
  function prePrReviewCommand(program2) {
12357
12886
  program2.command("pre-pr-review [feature-name]").description("Run and record pre-PR review evidence for a feature").option("--component <component>", "Component name for multi projects").option(
12358
12887
  "--decision <outcome>",
12359
12888
  "Decision outcome: approve | changes_requested | blocked"
12360
- ).option("--major <count>", "Pre-PR major findings count").option("--minor <count>", "Pre-PR minor findings count").option("--note <text>", "Decision note text").option("--json", "Output JSON").action(async (featureName, options) => {
12889
+ ).option("--note <text>", "Decision note text").option("--json", "Output JSON").action(async (featureName, options) => {
12361
12890
  try {
12362
12891
  await runPrePrReview(featureName, options);
12363
12892
  } catch (error) {
@@ -12407,13 +12936,11 @@ async function runPrePrReview(featureName, options) {
12407
12936
  `tasks.md not found for feature: ${feature.folderName}`
12408
12937
  );
12409
12938
  }
12410
- const tasksPath = path22.join(feature.path, "tasks.md");
12411
- const tasksContent = await fs16.readFile(tasksPath, "utf-8");
12939
+ const tasksPath = path23.join(feature.path, "tasks.md");
12940
+ const tasksContent = await fs17.readFile(tasksPath, "utf-8");
12412
12941
  const policy = resolvePrePrReviewPolicy(config.workflow);
12413
12942
  const preferred = getPreferredKeys(config.lang);
12414
12943
  const date = getLocalDateString();
12415
- const major = parseNonNegativeInt(options.major, "--major") ?? feature.prePrReview.findings?.major ?? 0;
12416
- const minor = parseNonNegativeInt(options.minor, "--minor") ?? feature.prePrReview.findings?.minor ?? 0;
12417
12944
  const explicitDecision = normalizeDecision(options.decision);
12418
12945
  if (options.decision && !explicitDecision) {
12419
12946
  throw createCliError(
@@ -12421,31 +12948,32 @@ async function runPrePrReview(featureName, options) {
12421
12948
  "`--decision` must be one of: approve, changes_requested, blocked."
12422
12949
  );
12423
12950
  }
12424
- const inferredDecision = major > 0 ? "changes_requested" : "approve";
12425
- const decision = explicitDecision || inferredDecision;
12951
+ const decision = explicitDecision || feature.prePrReview.decisionOutcome || "approve";
12426
12952
  if (!policy.decisionEnum.includes(decision)) {
12427
12953
  throw createCliError(
12428
12954
  "INVALID_ARGUMENT",
12429
12955
  `Decision "${decision}" is not allowed by workflow.prePrReview.decisionEnum.`
12430
12956
  );
12431
12957
  }
12432
- const note = options.note?.trim() || (decision === "approve" ? "baseline review completed without blocking findings" : decision === "changes_requested" ? "resolve findings before PR creation" : "blocked until prerequisite risk is resolved");
12433
- const reportPath = path22.join(feature.path, "pre-pr-review.md");
12434
- const reportContent = buildReportContent({
12958
+ const note = options.note?.trim() || (decision === "approve" ? "baseline review completed" : decision === "changes_requested" ? "follow-up changes are required before PR creation" : "blocked until prerequisite risk is resolved");
12959
+ const decisionsPath = path23.join(feature.path, "decisions.md");
12960
+ const decisionLogEntry = buildReportContent({
12435
12961
  folderName: feature.folderName,
12436
12962
  date,
12437
12963
  decision,
12438
- major,
12439
- minor,
12440
12964
  note,
12441
12965
  fallback: policy.fallback,
12442
12966
  skills: policy.skills
12443
12967
  });
12444
- await fs16.writeFile(reportPath, reportContent, "utf-8");
12445
- const reportPathFromDocs = normalizePathForDoc(
12446
- path22.join(feature.docs.featurePathFromDocs, "pre-pr-review.md")
12968
+ const decisionsContent = await fs17.pathExists(decisionsPath) ? await fs17.readFile(decisionsPath, "utf-8") : "";
12969
+ const nextDecisions = appendDecisionLog(decisionsContent, decisionLogEntry);
12970
+ if (nextDecisions !== decisionsContent) {
12971
+ await fs17.writeFile(decisionsPath, nextDecisions, "utf-8");
12972
+ }
12973
+ const decisionsPathFromDocs = normalizePathForDoc(
12974
+ path23.join(feature.docs.featurePathFromDocs, "decisions.md")
12447
12975
  );
12448
- const evidencePath = path22.basename(config.docsDir) === "docs" ? normalizePathForDoc(path22.join("docs", reportPathFromDocs)) : reportPathFromDocs;
12976
+ const evidencePath = path23.basename(config.docsDir) === "docs" ? normalizePathForDoc(path23.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
12449
12977
  let nextTasks = tasksContent;
12450
12978
  nextTasks = upsertSpecLine(
12451
12979
  nextTasks,
@@ -12454,19 +12982,12 @@ async function runPrePrReview(featureName, options) {
12454
12982
  "Done",
12455
12983
  ["PR \uC0C1\uD0DC", "PR Status"]
12456
12984
  );
12457
- nextTasks = upsertSpecLine(
12458
- nextTasks,
12459
- ["PR \uC804 \uB9AC\uBDF0 Findings", "Pre-PR Findings"],
12460
- preferred.findings,
12461
- `major=${major}, minor=${minor}`,
12462
- ["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
12463
- );
12464
12985
  nextTasks = upsertSpecLine(
12465
12986
  nextTasks,
12466
12987
  ["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"],
12467
12988
  preferred.evidence,
12468
12989
  evidencePath,
12469
- ["PR \uC804 \uB9AC\uBDF0 Findings", "Pre-PR Findings", "PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
12990
+ ["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
12470
12991
  );
12471
12992
  nextTasks = upsertSpecLine(
12472
12993
  nextTasks,
@@ -12476,7 +12997,7 @@ async function runPrePrReview(featureName, options) {
12476
12997
  ["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"]
12477
12998
  );
12478
12999
  if (nextTasks !== tasksContent) {
12479
- await fs16.writeFile(tasksPath, nextTasks, "utf-8");
13000
+ await fs17.writeFile(tasksPath, nextTasks, "utf-8");
12480
13001
  }
12481
13002
  if (options.json) {
12482
13003
  console.log(
@@ -12485,10 +13006,10 @@ async function runPrePrReview(featureName, options) {
12485
13006
  status: "ok",
12486
13007
  reasonCode: "PRE_PR_REVIEW_RECORDED",
12487
13008
  feature: feature.folderName,
12488
- reportPath: normalizePathForDoc(reportPath),
13009
+ reportPath: normalizePathForDoc(decisionsPath),
13010
+ decisionsPath: normalizePathForDoc(decisionsPath),
12489
13011
  evidencePath,
12490
13012
  decision,
12491
- findings: { major, minor },
12492
13013
  tasksUpdated: nextTasks !== tasksContent
12493
13014
  },
12494
13015
  null,
@@ -12500,8 +13021,7 @@ async function runPrePrReview(featureName, options) {
12500
13021
  console.log();
12501
13022
  console.log(chalk6.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
12502
13023
  console.log(chalk6.gray(`- Decision: ${decision}`));
12503
- console.log(chalk6.gray(`- Findings: major=${major}, minor=${minor}`));
12504
- console.log(chalk6.gray(`- Report: ${reportPath}`));
13024
+ console.log(chalk6.gray(`- Decisions log: ${decisionsPath}`));
12505
13025
  if (nextTasks !== tasksContent) {
12506
13026
  console.log(chalk6.gray(`- tasks.md updated: ${tasksPath}`));
12507
13027
  }
@@ -12550,13 +13070,13 @@ ${version}
12550
13070
  }
12551
13071
  return `${ascii}${footer}`;
12552
13072
  }
12553
- var CACHE_FILE = path22.join(os.homedir(), ".lee-spec-kit-version-cache.json");
13073
+ var CACHE_FILE = path23.join(os.homedir(), ".lee-spec-kit-version-cache.json");
12554
13074
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
12555
13075
  function getCurrentVersion() {
12556
13076
  try {
12557
- const packageJsonPath = path22.join(__dirname$1, "..", "package.json");
12558
- if (fs16.existsSync(packageJsonPath)) {
12559
- const pkg = fs16.readJsonSync(packageJsonPath);
13077
+ const packageJsonPath = path23.join(__dirname$1, "..", "package.json");
13078
+ if (fs17.existsSync(packageJsonPath)) {
13079
+ const pkg = fs17.readJsonSync(packageJsonPath);
12560
13080
  return pkg.version;
12561
13081
  }
12562
13082
  } catch {
@@ -12565,8 +13085,8 @@ function getCurrentVersion() {
12565
13085
  }
12566
13086
  function readCache() {
12567
13087
  try {
12568
- if (fs16.existsSync(CACHE_FILE)) {
12569
- return fs16.readJsonSync(CACHE_FILE);
13088
+ if (fs17.existsSync(CACHE_FILE)) {
13089
+ return fs17.readJsonSync(CACHE_FILE);
12570
13090
  }
12571
13091
  } catch {
12572
13092
  }
@@ -12658,9 +13178,9 @@ function shouldCheckForUpdates() {
12658
13178
  if (shouldCheckForUpdates()) checkForUpdates();
12659
13179
  function getCliVersion() {
12660
13180
  try {
12661
- const packageJsonPath = path22.join(__dirname$1, "..", "package.json");
12662
- if (fs16.existsSync(packageJsonPath)) {
12663
- const pkg = fs16.readJsonSync(packageJsonPath);
13181
+ const packageJsonPath = path23.join(__dirname$1, "..", "package.json");
13182
+ if (fs17.existsSync(packageJsonPath)) {
13183
+ const pkg = fs17.readJsonSync(packageJsonPath);
12664
13184
  if (pkg?.version) return String(pkg.version);
12665
13185
  }
12666
13186
  } catch {