lee-spec-kit 0.4.12 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.en.md +48 -1
  2. package/README.md +52 -1
  3. package/dist/index.js +305 -124
  4. package/package.json +1 -1
  5. package/templates/en/common/agents/pr-template.md +7 -1
  6. package/templates/en/common/agents/skills/create-feature.md +1 -1
  7. package/templates/en/common/agents/skills/create-pr.md +18 -1
  8. package/templates/en/common/agents/skills/execute-task.md +5 -2
  9. package/templates/en/fullstack/README.md +5 -2
  10. package/templates/en/fullstack/agents/agents.md +2 -1
  11. package/templates/en/fullstack/features/feature-base/plan.md +1 -1
  12. package/templates/en/fullstack/features/feature-base/spec.md +1 -1
  13. package/templates/en/fullstack/features/feature-base/tasks.md +10 -1
  14. package/templates/en/single/README.md +5 -2
  15. package/templates/en/single/agents/agents.md +2 -1
  16. package/templates/en/single/features/feature-base/plan.md +1 -1
  17. package/templates/en/single/features/feature-base/spec.md +1 -1
  18. package/templates/en/single/features/feature-base/tasks.md +10 -1
  19. package/templates/ko/common/agents/pr-template.md +7 -1
  20. package/templates/ko/common/agents/skills/create-feature.md +3 -3
  21. package/templates/ko/common/agents/skills/create-pr.md +18 -1
  22. package/templates/ko/common/agents/skills/execute-task.md +7 -5
  23. package/templates/ko/fullstack/README.md +5 -2
  24. package/templates/ko/fullstack/agents/agents.md +2 -1
  25. package/templates/ko/fullstack/features/feature-base/plan.md +1 -1
  26. package/templates/ko/fullstack/features/feature-base/spec.md +1 -1
  27. package/templates/ko/fullstack/features/feature-base/tasks.md +11 -2
  28. package/templates/ko/single/README.md +5 -2
  29. package/templates/ko/single/agents/agents.md +2 -1
  30. package/templates/ko/single/features/feature-base/plan.md +1 -1
  31. package/templates/ko/single/features/feature-base/spec.md +1 -1
  32. package/templates/ko/single/features/feature-base/tasks.md +11 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import path4 from 'path';
2
+ import path11 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { program } from 'commander';
5
5
  import fs8 from 'fs-extra';
@@ -10,7 +10,7 @@ import { spawn, execSync, execFileSync } from 'child_process';
10
10
  import os from 'os';
11
11
 
12
12
  var getFilename = () => fileURLToPath(import.meta.url);
13
- var getDirname = () => path4.dirname(getFilename());
13
+ var getDirname = () => path11.dirname(getFilename());
14
14
  var __dirname$1 = /* @__PURE__ */ getDirname();
15
15
  async function copyTemplates(src, dest) {
16
16
  await fs8.copy(src, dest, {
@@ -41,10 +41,10 @@ async function replaceInFiles(dir, replacements) {
41
41
  }
42
42
  }
43
43
  var __filename2 = fileURLToPath(import.meta.url);
44
- var __dirname2 = path4.dirname(__filename2);
44
+ var __dirname2 = path11.dirname(__filename2);
45
45
  function getTemplatesDir() {
46
- const rootDir = path4.resolve(__dirname2, "..");
47
- return path4.join(rootDir, "templates");
46
+ const rootDir = path11.resolve(__dirname2, "..");
47
+ return path11.join(rootDir, "templates");
48
48
  }
49
49
 
50
50
  // src/utils/i18n.ts
@@ -113,9 +113,12 @@ var I18N = {
113
113
  "doctor.issue.specStatusUnset": "spec.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
114
114
  "doctor.issue.planStatusUnset": "plan.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
115
115
  "doctor.issue.tasksEmpty": "tasks.md\uC5D0 \uD0DC\uC2A4\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
116
+ "doctor.issue.tasksDocStatusUnset": "tasks.md\uC758 \uBB38\uC11C \uC0C1\uD0DC(Doc Status)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
117
+ "doctor.issue.tasksDocStatusMissing": "tasks.md\uC5D0 \uBB38\uC11C \uC0C1\uD0DC(Doc Status) \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **\uBB38\uC11C \uC0C1\uD0DC**: Review | Approved`\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
116
118
  "doctor.issue.duplicateFeatureId": "\uC911\uBCF5 Feature ID \uAC10\uC9C0: {id} ({count}\uAC1C)",
117
119
  "doctor.issue.missingFeatureId": "Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
118
120
  "context.noActiveFeatures": "\u26A0\uFE0F \uC9C4\uD589 \uC911\uC778 Feature\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
121
+ "context.header": "\u{1F4CD} \uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD655\uC778",
119
122
  "context.envWarnings": "\u26A0\uFE0F \uD658\uACBD \uACBD\uACE0:",
120
123
  "context.openFallbackSummary": "(\uBE0C\uB79C\uCE58\uB85C Feature\uB97C \uD2B9\uC815\uD558\uC9C0 \uBABB\uD574 \uBBF8\uC644\uB8CC Feature\uB9CC \uD45C\uC2DC\uD569\uB2C8\uB2E4. \uC9C4\uD589 \uC911: {inProgress}\uAC1C / \uC885\uB8CC \uB300\uAE30: {readyToClose}\uAC1C / \uC644\uB8CC: {done}\uAC1C)",
121
124
  "context.sectionInProgress": "\uC9C4\uD589 \uC911",
@@ -123,7 +126,9 @@ var I18N = {
123
126
  "context.tipDetails": "Tip: \uD2B9\uC815 Feature\uC758 \uC0C1\uC138 \uC815\uBCF4\uB97C \uBCF4\uB824\uBA74:",
124
127
  "context.tipShowAll": "\uC804\uCCB4 \uBCF4\uAE30",
125
128
  "context.tipShowDone": "\uC644\uB8CC\uB9CC \uBCF4\uAE30",
126
- "context.okRequired": "[OK \uD544\uC694] ",
129
+ "context.checkRequired": "[\uD655\uC778 \uD544\uC694] ",
130
+ "context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uADDC\uCE59: /docs/agents/agents.md \uCC38\uACE0 (git push/merge/merge commit \uD3EC\uD568) \u2014 [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC815\uD655\uD788 OK \uC751\uB2F5\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589 (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
131
+ "context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59: /docs/agents/git-workflow.md \uCC38\uACE0",
127
132
  "context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
128
133
  "context.list.issueNumberNeeded": "\uC774\uC288 \uBC88\uD638 \uAE30\uB85D \uD544\uC694",
129
134
  "context.list.addPrMetadata": "PR \uBA54\uD0C0\uB370\uC774\uD130(PR/PR \uC0C1\uD0DC) \uCD94\uAC00",
@@ -187,7 +192,7 @@ var I18N = {
187
192
  specApprove: "spec.md \uC2B9\uC778",
188
193
  planWrite: "plan.md \uC791\uC131",
189
194
  planApprove: "plan.md \uC2B9\uC778",
190
- tasksWrite: "tasks.md \uC791\uC131",
195
+ tasksWrite: "tasks.md \uC791\uC131/\uC2B9\uC778",
191
196
  docsInitialCommit: "\uCD08\uAE30 \uBB38\uC11C \uCEE4\uBC0B",
192
197
  docsCommitPlanning: "\uBB38\uC11C \uCEE4\uBC0B(\uB3D9\uAE30\uD654)",
193
198
  issueCreate: "GitHub Issue \uC0DD\uC131",
@@ -207,6 +212,8 @@ var I18N = {
207
212
  planApproval: "plan.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
208
213
  tasksCreate: "tasks.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694. (features/feature-base/tasks.md \uCC38\uACE0)",
209
214
  tasksNeedAtLeastOne: "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694.",
215
+ tasksImprove: "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
216
+ tasksApproval: "tasks.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC9C4\uD589 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694. (\uC2B9\uC778 \uD6C4 \uBB38\uC11C \uC0C1\uD0DC\uB97C Approved\uB85C \uBCC0\uACBD)",
210
217
  docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
211
218
  issueCreateAndWrite: "GitHub Issue\uB97C \uC0DD\uC131\uD55C \uB4A4, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694. (skills/create-issue.md \uCC38\uACE0)",
212
219
  docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
@@ -215,12 +222,12 @@ var I18N = {
215
222
  createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
216
223
  tasksAllDoneButNoChecklist: '\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC139\uC158\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC744 \uCD94\uAC00/\uD655\uC778\uD558\uC138\uC694.',
217
224
  tasksAllDoneButChecklist: "\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uAC00 \uC644\uC804\uD788 \uCCB4\uD06C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ({checked}/{total})",
218
- finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
219
- startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
225
+ finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC644\uB8CC \uC804 \uACB0\uACFC/\uAC80\uC99D \uACF5\uC720 + \uC2B9\uC778(OK) \uD6C4 DONE \uCC98\uB9AC) (skills/execute-task.md \uCC38\uACE0)',
226
+ startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC2DC\uC791 \uC804 \uC81C\uBAA9 \uACF5\uC720 + \uC2B9\uC778(OK) \uD6C4 DOING \uCC98\uB9AC) (skills/execute-task.md \uCC38\uACE0)',
220
227
  checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)",
221
- prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (OK \uD544\uC694)",
228
+ prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (\uD655\uC778 \uD544\uC694)",
222
229
  prCreate: "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694. (skills/create-pr.md \uCC38\uACE0)",
223
- prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694. (merge \uD6C4 Approved\uB85C \uC5C5\uB370\uC774\uD2B8)",
230
+ prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694. (merge \uD6C4 Approved\uB85C \uC5C5\uB370\uC774\uD2B8)",
224
231
  prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uACE0 PR \uC0C1\uD0DC\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. (PR \uC0C1\uD0DC: Review \u2192 Approved)",
225
232
  prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.",
226
233
  featureDone: "PR\uC774 Approved\uC774\uACE0 \uBAA8\uB4E0 \uD0DC\uC2A4\uD06C/\uC644\uB8CC \uC870\uAC74\uC774 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uB8CC \uC0C1\uD0DC\uC785\uB2C8\uB2E4.",
@@ -229,12 +236,13 @@ var I18N = {
229
236
  warnings: {
230
237
  projectBranchUnavailable: "\uD504\uB85C\uC81D\uD2B8 \uBE0C\uB79C\uCE58\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.)",
231
238
  docsGitUnavailable: "docs \uB808\uD3EC\uC758 git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uB808\uD3EC \uC704\uCE58 / git init \uD655\uC778)",
232
- docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694)",
239
+ docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694) \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59: /docs/agents/git-workflow.md \uCC38\uACE0",
240
+ legacyTasksDocStatusField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. `\uBB38\uC11C \uC0C1\uD0DC` \uD544\uB4DC(Review/Approved)\uB97C \uCD94\uAC00\uD574 \uD0DC\uC2A4\uD06C \uC2B9\uC778 \uB2E8\uACC4\uB97C \uD65C\uC131\uD654\uD558\uC138\uC694.",
233
241
  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.",
234
242
  workflowSpecNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC spec.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (spec.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
235
243
  workflowPlanNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC plan.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (plan.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
236
244
  workflowPrLinkMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uB9C1\uD06C\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uD544\uB4DC\uB97C \uCC44\uC6B0\uC138\uC694.)",
237
- workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
245
+ workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
238
246
  workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (merge \uD6C4 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)"
239
247
  }
240
248
  },
@@ -291,9 +299,12 @@ var I18N = {
291
299
  "doctor.issue.specStatusUnset": "spec.md Status is not set. (May still be a template)",
292
300
  "doctor.issue.planStatusUnset": "plan.md Status is not set. (May still be a template)",
293
301
  "doctor.issue.tasksEmpty": "tasks.md has no tasks.",
302
+ "doctor.issue.tasksDocStatusUnset": "tasks.md Doc Status is not set. (Set it to Review or Approved.)",
303
+ "doctor.issue.tasksDocStatusMissing": "tasks.md is missing the Doc Status field. Add `- **Doc Status**: Review | Approved`.",
294
304
  "doctor.issue.duplicateFeatureId": "Duplicate Feature ID detected: {id} ({count})",
295
305
  "doctor.issue.missingFeatureId": "Feature folder name is not in F001-... format. (Cannot extract ID)",
296
306
  "context.noActiveFeatures": "\u26A0\uFE0F No active features found.",
307
+ "context.header": "\u{1F4CD} Current Context Check",
297
308
  "context.envWarnings": "\u26A0\uFE0F Environment warnings:",
298
309
  "context.openFallbackSummary": "(Could not detect a feature from the branch, so showing only open features. In Progress: {inProgress} / Ready To Close: {readyToClose} / Done: {done})",
299
310
  "context.sectionInProgress": "In Progress",
@@ -301,7 +312,9 @@ var I18N = {
301
312
  "context.tipDetails": "Tip: To view details for a feature:",
302
313
  "context.tipShowAll": "Show all",
303
314
  "context.tipShowDone": "Show done only",
304
- "context.okRequired": "[OK required] ",
315
+ "context.checkRequired": "[CHECK required] ",
316
+ "context.checkPolicyHint": "\u2139\uFE0F User check policy: see /docs/agents/agents.md (includes git push/merge and merge commits) \u2014 if you see [CHECK required], wait for explicit OK before proceeding (config: approval can override)",
317
+ "context.tipDocsCommitRules": "Commit message rules: /docs/agents/git-workflow.md",
305
318
  "context.list.docsCommitNeeded": "Commit docs changes",
306
319
  "context.list.issueNumberNeeded": "Fill issue number in docs",
307
320
  "context.list.addPrMetadata": "Add PR metadata (PR/PR Status)",
@@ -365,7 +378,7 @@ var I18N = {
365
378
  specApprove: "Approve spec.md",
366
379
  planWrite: "Write plan.md",
367
380
  planApprove: "Approve plan.md",
368
- tasksWrite: "Write tasks.md",
381
+ tasksWrite: "Write/approve tasks.md",
369
382
  docsInitialCommit: "Initial docs commit",
370
383
  docsCommitPlanning: "Commit docs (sync)",
371
384
  issueCreate: "Create GitHub Issue",
@@ -385,6 +398,8 @@ var I18N = {
385
398
  planApproval: "Share plan.md with the user and get approval (OK).",
386
399
  tasksCreate: "Create tasks.md by copying the template. (See features/feature-base/tasks.md)",
387
400
  tasksNeedAtLeastOne: "Write at least 1 task in tasks.md.",
401
+ tasksImprove: "Improve tasks.md and change Doc Status to Review.",
402
+ tasksApproval: "Share tasks.md with the user and get progress approval (OK). (Then set Doc Status to Approved)",
388
403
  docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
389
404
  issueCreateAndWrite: "Create a GitHub Issue, fill the issue number in spec.md/tasks.md, then prepare a docs commit. (See skills/create-issue.md)",
390
405
  docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
@@ -393,12 +408,12 @@ var I18N = {
393
408
  createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
394
409
  tasksAllDoneButNoChecklist: 'All tasks are DONE, but no completion checklist section was found. Add/verify the "Completion Criteria" section in tasks.md.',
395
410
  tasksAllDoneButChecklist: "All tasks are DONE, but the completion checklist is not fully checked. ({checked}/{total})",
396
- finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
397
- startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
411
+ finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) (Share outcome/verification + get OK before marking DONE) (See skills/execute-task.md)',
412
+ startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (Share title + get OK before marking DOING) (See skills/execute-task.md)',
398
413
  checkTaskStatuses: "Check task statuses. ({done}/{total}) (See skills/execute-task.md)",
399
- prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (OK required)",
414
+ prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
400
415
  prCreate: "Create a PR and record the PR link in tasks.md. (See skills/create-pr.md)",
401
- prFillStatus: "Set PR Status in tasks.md to Draft/Review/Approved. (After merge, update it to Approved.)",
416
+ prFillStatus: "Set PR Status in tasks.md to Review/Approved. (After merge, update it to Approved.)",
402
417
  prResolveReview: "Resolve review comments and update PR Status. (PR Status: Review \u2192 Approved)",
403
418
  prRequestReview: "Request review and update PR Status to Review.",
404
419
  featureDone: "PR is Approved and all tasks/completion criteria are satisfied. This feature is done.",
@@ -407,12 +422,13 @@ var I18N = {
407
422
  warnings: {
408
423
  projectBranchUnavailable: "Cannot determine project branch. (In standalone mode, projectRoot is required.)",
409
424
  docsGitUnavailable: "Cannot read git status for the docs repo. (Check repo location / git init.)",
410
- docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.)",
425
+ docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.) Commit message rules: /docs/agents/git-workflow.md",
426
+ legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Review/Approved) to enable tasks approval.",
411
427
  legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
412
428
  workflowSpecNotApproved: "Implementation is done but spec.md Status is not Approved. (Update spec.md Status to Approved.)",
413
429
  workflowPlanNotApproved: "Implementation is done but plan.md Status is not Approved. (Update plan.md Status to Approved.)",
414
430
  workflowPrLinkMissing: "Implementation is done but PR link is missing. (Fill the PR field in tasks.md.)",
415
- workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Draft/Review/Approved in tasks.md.)",
431
+ workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Review/Approved in tasks.md.)",
416
432
  workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (After merge, update PR Status to Approved in tasks.md.)"
417
433
  }
418
434
  }
@@ -548,7 +564,7 @@ ${tr(lang, "cli", "common.canceled")}`)
548
564
  }
549
565
  async function runInit(options) {
550
566
  const cwd = process.cwd();
551
- const defaultName = path4.basename(cwd);
567
+ const defaultName = path11.basename(cwd);
552
568
  let projectName = options.name || defaultName;
553
569
  let projectType = options.type;
554
570
  let lang = options.lang || "en";
@@ -556,7 +572,7 @@ async function runInit(options) {
556
572
  let pushDocs;
557
573
  let docsRemote;
558
574
  let projectRoot;
559
- const targetDir = path4.resolve(cwd, options.dir || "./docs");
575
+ const targetDir = path11.resolve(cwd, options.dir || "./docs");
560
576
  const isInsideGitRepo = checkGitRepo(cwd);
561
577
  if (!options.yes) {
562
578
  if (!options.lang) {
@@ -798,8 +814,8 @@ async function runInit(options) {
798
814
  );
799
815
  console.log();
800
816
  const templatesDir = getTemplatesDir();
801
- const commonPath = path4.join(templatesDir, lang, "common");
802
- const typePath = path4.join(templatesDir, lang, projectType);
817
+ const commonPath = path11.join(templatesDir, lang, "common");
818
+ const typePath = path11.join(templatesDir, lang, projectType);
803
819
  if (await fs8.pathExists(commonPath)) {
804
820
  await copyTemplates(commonPath, targetDir);
805
821
  }
@@ -819,7 +835,12 @@ async function runInit(options) {
819
835
  projectType,
820
836
  lang,
821
837
  createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
822
- docsRepo
838
+ docsRepo,
839
+ // Approval policy for "requiresUserCheck" actions shown by `context`.
840
+ // - builtin (default): Use requiresUserCheck embedded in steps/actions.
841
+ // - category: Override by action category (recommended for automation).
842
+ // - steps: Override by step number (fragile; not recommended).
843
+ approval: { mode: "builtin" }
823
844
  };
824
845
  if (docsRepo === "standalone") {
825
846
  config.pushDocs = pushDocs;
@@ -830,7 +851,7 @@ async function runInit(options) {
830
851
  config.projectRoot = projectRoot;
831
852
  }
832
853
  }
833
- const configPath = path4.join(targetDir, ".lee-spec-kit.json");
854
+ const configPath = path11.join(targetDir, ".lee-spec-kit.json");
834
855
  await fs8.writeJson(configPath, config, { spaces: 2 });
835
856
  console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
836
857
  console.log();
@@ -867,7 +888,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
867
888
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
868
889
  runGit(["init"], cwd);
869
890
  }
870
- const relativePath = path4.relative(cwd, targetDir);
891
+ const relativePath = path11.relative(cwd, targetDir);
871
892
  const stagedBeforeAdd = getCachedStagedFiles(cwd);
872
893
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
873
894
  console.log(
@@ -905,10 +926,10 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
905
926
  }
906
927
  function getAncestorDirs(startDir) {
907
928
  const dirs = [];
908
- let current = path4.resolve(startDir);
929
+ let current = path11.resolve(startDir);
909
930
  while (true) {
910
931
  dirs.push(current);
911
- const parent = path4.dirname(current);
932
+ const parent = path11.dirname(current);
912
933
  if (parent === current) break;
913
934
  current = parent;
914
935
  }
@@ -917,21 +938,21 @@ function getAncestorDirs(startDir) {
917
938
  async function getConfig(cwd) {
918
939
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
919
940
  const baseDirs = [
920
- ...explicitDocsDir ? [path4.resolve(explicitDocsDir)] : [],
941
+ ...explicitDocsDir ? [path11.resolve(explicitDocsDir)] : [],
921
942
  ...getAncestorDirs(cwd)
922
943
  ];
923
944
  const visitedBaseDirs = /* @__PURE__ */ new Set();
924
945
  const visitedDocsDirs = /* @__PURE__ */ new Set();
925
946
  for (const baseDir of baseDirs) {
926
- const resolvedBaseDir = path4.resolve(baseDir);
947
+ const resolvedBaseDir = path11.resolve(baseDir);
927
948
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
928
949
  visitedBaseDirs.add(resolvedBaseDir);
929
- const possibleDocsDirs = [path4.join(resolvedBaseDir, "docs"), resolvedBaseDir];
950
+ const possibleDocsDirs = [path11.join(resolvedBaseDir, "docs"), resolvedBaseDir];
930
951
  for (const docsDir of possibleDocsDirs) {
931
- const resolvedDocsDir = path4.resolve(docsDir);
952
+ const resolvedDocsDir = path11.resolve(docsDir);
932
953
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
933
954
  visitedDocsDirs.add(resolvedDocsDir);
934
- const configPath = path4.join(resolvedDocsDir, ".lee-spec-kit.json");
955
+ const configPath = path11.join(resolvedDocsDir, ".lee-spec-kit.json");
935
956
  if (await fs8.pathExists(configPath)) {
936
957
  try {
937
958
  const configFile = await fs8.readJson(configPath);
@@ -943,18 +964,19 @@ async function getConfig(cwd) {
943
964
  docsRepo: configFile.docsRepo,
944
965
  pushDocs: configFile.pushDocs,
945
966
  docsRemote: configFile.docsRemote,
946
- projectRoot: configFile.projectRoot
967
+ projectRoot: configFile.projectRoot,
968
+ approval: configFile.approval
947
969
  };
948
970
  } catch {
949
971
  }
950
972
  }
951
- const agentsPath = path4.join(resolvedDocsDir, "agents");
952
- const featuresPath = path4.join(resolvedDocsDir, "features");
973
+ const agentsPath = path11.join(resolvedDocsDir, "agents");
974
+ const featuresPath = path11.join(resolvedDocsDir, "features");
953
975
  if (await fs8.pathExists(agentsPath) && await fs8.pathExists(featuresPath)) {
954
- const bePath = path4.join(featuresPath, "be");
955
- const fePath = path4.join(featuresPath, "fe");
976
+ const bePath = path11.join(featuresPath, "be");
977
+ const fePath = path11.join(featuresPath, "fe");
956
978
  const projectType = await fs8.pathExists(bePath) || await fs8.pathExists(fePath) ? "fullstack" : "single";
957
- const agentsMdPath = path4.join(agentsPath, "agents.md");
979
+ const agentsMdPath = path11.join(agentsPath, "agents.md");
958
980
  let lang = "en";
959
981
  if (await fs8.pathExists(agentsMdPath)) {
960
982
  const content = await fs8.readFile(agentsMdPath, "utf-8");
@@ -1031,12 +1053,12 @@ async function runFeature(name, options) {
1031
1053
  }
1032
1054
  let featuresDir;
1033
1055
  if (projectType === "fullstack" && repo) {
1034
- featuresDir = path4.join(docsDir, "features", repo);
1056
+ featuresDir = path11.join(docsDir, "features", repo);
1035
1057
  } else {
1036
- featuresDir = path4.join(docsDir, "features");
1058
+ featuresDir = path11.join(docsDir, "features");
1037
1059
  }
1038
1060
  const featureFolderName = `${featureId}-${name}`;
1039
- const featureDir = path4.join(featuresDir, featureFolderName);
1061
+ const featureDir = path11.join(featuresDir, featureFolderName);
1040
1062
  if (await fs8.pathExists(featureDir)) {
1041
1063
  console.error(
1042
1064
  chalk6.red(
@@ -1045,7 +1067,7 @@ async function runFeature(name, options) {
1045
1067
  );
1046
1068
  process.exit(1);
1047
1069
  }
1048
- const featureBasePath = path4.join(docsDir, "features", "feature-base");
1070
+ const featureBasePath = path11.join(docsDir, "features", "feature-base");
1049
1071
  if (!await fs8.pathExists(featureBasePath)) {
1050
1072
  console.error(
1051
1073
  chalk6.red(
@@ -1103,12 +1125,12 @@ async function runFeature(name, options) {
1103
1125
  console.log();
1104
1126
  }
1105
1127
  async function getNextFeatureId(docsDir, projectType) {
1106
- const featuresDir = path4.join(docsDir, "features");
1128
+ const featuresDir = path11.join(docsDir, "features");
1107
1129
  let max = 0;
1108
1130
  const scanDirs = [];
1109
1131
  if (projectType === "fullstack") {
1110
- scanDirs.push(path4.join(featuresDir, "be"));
1111
- scanDirs.push(path4.join(featuresDir, "fe"));
1132
+ scanDirs.push(path11.join(featuresDir, "be"));
1133
+ scanDirs.push(path11.join(featuresDir, "fe"));
1112
1134
  } else {
1113
1135
  scanDirs.push(featuresDir);
1114
1136
  }
@@ -1133,14 +1155,17 @@ async function getNextFeatureId(docsDir, projectType) {
1133
1155
  function isCompletionChecklistDone(feature) {
1134
1156
  return !!feature.completionChecklist && feature.completionChecklist.total > 0 && feature.completionChecklist.checked === feature.completionChecklist.total;
1135
1157
  }
1158
+ function isTasksDocApproved(feature) {
1159
+ return !feature.docs.tasksDocStatusFieldExists || feature.tasksDocStatus === "Approved";
1160
+ }
1136
1161
  function isImplementationDone(feature) {
1137
- return feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature);
1162
+ return feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature);
1138
1163
  }
1139
1164
  function isPrMetadataConfigured(feature) {
1140
1165
  return feature.docs.prFieldExists && feature.docs.prStatusFieldExists;
1141
1166
  }
1142
1167
  function isFeatureDone(feature) {
1143
- return feature.specStatus === "Approved" && feature.planStatus === "Approved" && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isPrMetadataConfigured(feature) && !!feature.pr.link && feature.pr.status === "Approved";
1168
+ return feature.specStatus === "Approved" && feature.planStatus === "Approved" && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && isPrMetadataConfigured(feature) && !!feature.pr.link && feature.pr.status === "Approved";
1144
1169
  }
1145
1170
  function getStepDefinitions(lang) {
1146
1171
  return [
@@ -1160,6 +1185,7 @@ function getStepDefinitions(lang) {
1160
1185
  actions: (f) => [
1161
1186
  {
1162
1187
  type: "instruction",
1188
+ category: "spec_write",
1163
1189
  message: !f.docs.specExists ? tr(lang, "messages", "specCreate") : tr(lang, "messages", "specImprove")
1164
1190
  }
1165
1191
  ]
@@ -1174,7 +1200,8 @@ function getStepDefinitions(lang) {
1174
1200
  actions: () => [
1175
1201
  {
1176
1202
  type: "instruction",
1177
- requiresUserOk: true,
1203
+ category: "spec_approve",
1204
+ requiresUserCheck: true,
1178
1205
  message: tr(lang, "messages", "specApproval")
1179
1206
  }
1180
1207
  ]
@@ -1191,6 +1218,7 @@ function getStepDefinitions(lang) {
1191
1218
  actions: (f) => [
1192
1219
  {
1193
1220
  type: "instruction",
1221
+ category: "plan_write",
1194
1222
  message: !f.docs.planExists ? tr(lang, "messages", "planCreate") : tr(lang, "messages", "planImprove")
1195
1223
  }
1196
1224
  ]
@@ -1205,7 +1233,8 @@ function getStepDefinitions(lang) {
1205
1233
  actions: () => [
1206
1234
  {
1207
1235
  type: "instruction",
1208
- requiresUserOk: true,
1236
+ category: "plan_approve",
1237
+ requiresUserCheck: true,
1209
1238
  message: tr(lang, "messages", "planApproval")
1210
1239
  }
1211
1240
  ]
@@ -1215,24 +1244,54 @@ function getStepDefinitions(lang) {
1215
1244
  step: 6,
1216
1245
  name: tr(lang, "steps", "tasksWrite"),
1217
1246
  checklist: {
1218
- done: (f) => f.docs.tasksExists && f.tasks.total > 0,
1247
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && isTasksDocApproved(f),
1219
1248
  detail: (f) => f.tasks.total > 0 ? `(${f.tasks.total})` : ""
1220
1249
  },
1221
1250
  current: {
1222
- when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0),
1251
+ when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0 || f.docs.tasksDocStatusFieldExists && (!f.tasksDocStatus || f.tasksDocStatus === "Draft" || f.tasksDocStatus === "Review")),
1223
1252
  actions: (f) => {
1224
1253
  if (!f.docs.tasksExists) {
1225
1254
  return [
1226
1255
  {
1227
1256
  type: "instruction",
1257
+ category: "tasks_write",
1228
1258
  message: tr(lang, "messages", "tasksCreate")
1229
1259
  }
1230
1260
  ];
1231
1261
  }
1262
+ if (f.tasks.total === 0) {
1263
+ return [
1264
+ {
1265
+ type: "instruction",
1266
+ category: "tasks_write",
1267
+ message: tr(lang, "messages", "tasksNeedAtLeastOne")
1268
+ }
1269
+ ];
1270
+ }
1271
+ if (f.docs.tasksDocStatusFieldExists && (!f.tasksDocStatus || f.tasksDocStatus === "Draft")) {
1272
+ return [
1273
+ {
1274
+ type: "instruction",
1275
+ category: "tasks_write",
1276
+ message: tr(lang, "messages", "tasksImprove")
1277
+ }
1278
+ ];
1279
+ }
1280
+ if (f.docs.tasksDocStatusFieldExists && f.tasksDocStatus === "Review") {
1281
+ return [
1282
+ {
1283
+ type: "instruction",
1284
+ category: "tasks_approve",
1285
+ requiresUserCheck: true,
1286
+ message: tr(lang, "messages", "tasksApproval")
1287
+ }
1288
+ ];
1289
+ }
1232
1290
  return [
1233
1291
  {
1234
1292
  type: "instruction",
1235
- message: tr(lang, "messages", "tasksNeedAtLeastOne")
1293
+ category: "tasks_write",
1294
+ message: tr(lang, "messages", "tasksImprove")
1236
1295
  }
1237
1296
  ];
1238
1297
  }
@@ -1242,16 +1301,17 @@ function getStepDefinitions(lang) {
1242
1301
  step: 7,
1243
1302
  name: tr(lang, "steps", "docsInitialCommit"),
1244
1303
  checklist: {
1245
- done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && f.git.docsEverCommitted
1304
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && f.git.docsEverCommitted
1246
1305
  },
1247
1306
  current: {
1248
- when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && !f.activeTask && !f.git.docsEverCommitted && f.git.docsHasUncommittedChanges,
1307
+ when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && !f.activeTask && !f.git.docsEverCommitted && f.git.docsHasUncommittedChanges,
1249
1308
  actions: (f) => {
1250
1309
  if (f.issueNumber) {
1251
1310
  return [
1252
1311
  {
1253
1312
  type: "command",
1254
- requiresUserOk: true,
1313
+ category: "docs_commit",
1314
+ requiresUserCheck: true,
1255
1315
  scope: "docs",
1256
1316
  cwd: f.git.docsGitCwd,
1257
1317
  cmd: tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1266,7 +1326,8 @@ function getStepDefinitions(lang) {
1266
1326
  return [
1267
1327
  {
1268
1328
  type: "command",
1269
- requiresUserOk: true,
1329
+ category: "docs_commit",
1330
+ requiresUserCheck: true,
1270
1331
  scope: "docs",
1271
1332
  cwd: f.git.docsGitCwd,
1272
1333
  cmd: tr(lang, "messages", "docsCommitPlanning", {
@@ -1286,12 +1347,13 @@ function getStepDefinitions(lang) {
1286
1347
  done: (f) => !!f.issueNumber
1287
1348
  },
1288
1349
  current: {
1289
- when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && !f.issueNumber,
1350
+ when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && !f.issueNumber,
1290
1351
  actions: (f) => {
1291
1352
  return [
1292
1353
  {
1293
1354
  type: "instruction",
1294
- requiresUserOk: true,
1355
+ category: "issue_create",
1356
+ requiresUserCheck: true,
1295
1357
  message: tr(lang, "messages", "issueCreateAndWrite")
1296
1358
  }
1297
1359
  ];
@@ -1309,6 +1371,7 @@ function getStepDefinitions(lang) {
1309
1371
  return [
1310
1372
  {
1311
1373
  type: "instruction",
1374
+ category: "branch_create",
1312
1375
  message: tr(lang, "messages", "standaloneNeedsProjectRoot")
1313
1376
  }
1314
1377
  ];
@@ -1316,6 +1379,7 @@ function getStepDefinitions(lang) {
1316
1379
  return [
1317
1380
  {
1318
1381
  type: "command",
1382
+ category: "branch_create",
1319
1383
  scope: "project",
1320
1384
  cwd: f.git.projectGitCwd,
1321
1385
  cmd: tr(lang, "messages", "createBranch", {
@@ -1332,17 +1396,18 @@ function getStepDefinitions(lang) {
1332
1396
  step: 10,
1333
1397
  name: tr(lang, "steps", "tasksExecute"),
1334
1398
  checklist: {
1335
- done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f),
1399
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && isTasksDocApproved(f),
1336
1400
  detail: (f) => f.tasks.total > 0 ? `(${f.tasks.done}/${f.tasks.total})` : ""
1337
1401
  },
1338
1402
  current: {
1339
- when: (f) => f.docs.tasksExists && f.tasks.total > 0 && (f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && (f.git.onExpectedBranch || f.tasks.done === f.tasks.total),
1403
+ when: (f) => f.docs.tasksExists && f.tasks.total > 0 && (f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && isTasksDocApproved(f) && (f.git.onExpectedBranch || f.tasks.done === f.tasks.total),
1340
1404
  actions: (f) => {
1341
1405
  if (f.tasks.total === f.tasks.done && !isCompletionChecklistDone(f)) {
1342
1406
  const actions = [
1343
1407
  {
1344
1408
  type: "instruction",
1345
- requiresUserOk: true,
1409
+ category: "task_execute",
1410
+ requiresUserCheck: true,
1346
1411
  message: !f.completionChecklist ? tr(lang, "messages", "tasksAllDoneButNoChecklist") : tr(lang, "messages", "tasksAllDoneButChecklist", {
1347
1412
  checked: f.completionChecklist.checked,
1348
1413
  total: f.completionChecklist.total
@@ -1352,7 +1417,8 @@ function getStepDefinitions(lang) {
1352
1417
  if (!isPrMetadataConfigured(f)) {
1353
1418
  actions.push({
1354
1419
  type: "instruction",
1355
- requiresUserOk: true,
1420
+ category: "pr_metadata_migrate",
1421
+ requiresUserCheck: true,
1356
1422
  message: tr(lang, "messages", "prLegacyAsk")
1357
1423
  });
1358
1424
  }
@@ -1362,7 +1428,8 @@ function getStepDefinitions(lang) {
1362
1428
  return [
1363
1429
  {
1364
1430
  type: "instruction",
1365
- requiresUserOk: true,
1431
+ category: "task_execute",
1432
+ requiresUserCheck: true,
1366
1433
  message: tr(lang, "messages", "finishDoingTask", {
1367
1434
  title: f.activeTask.title,
1368
1435
  done: f.tasks.done,
@@ -1376,7 +1443,8 @@ function getStepDefinitions(lang) {
1376
1443
  return [
1377
1444
  {
1378
1445
  type: "command",
1379
- requiresUserOk: true,
1446
+ category: "docs_commit",
1447
+ requiresUserCheck: true,
1380
1448
  scope: "docs",
1381
1449
  cwd: f.git.docsGitCwd,
1382
1450
  cmd: f.issueNumber ? tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1395,7 +1463,8 @@ function getStepDefinitions(lang) {
1395
1463
  return [
1396
1464
  {
1397
1465
  type: "instruction",
1398
- requiresUserOk: true,
1466
+ category: "task_execute",
1467
+ requiresUserCheck: true,
1399
1468
  message: tr(lang, "messages", "startNextTodoTask", {
1400
1469
  title: f.nextTodoTask.title,
1401
1470
  done: f.tasks.done,
@@ -1407,7 +1476,8 @@ function getStepDefinitions(lang) {
1407
1476
  return [
1408
1477
  {
1409
1478
  type: "instruction",
1410
- requiresUserOk: true,
1479
+ category: "task_execute",
1480
+ requiresUserCheck: true,
1411
1481
  message: tr(lang, "messages", "checkTaskStatuses", {
1412
1482
  done: f.tasks.done,
1413
1483
  total: f.tasks.total
@@ -1428,7 +1498,8 @@ function getStepDefinitions(lang) {
1428
1498
  actions: (f) => [
1429
1499
  {
1430
1500
  type: "command",
1431
- requiresUserOk: true,
1501
+ category: "docs_commit",
1502
+ requiresUserCheck: true,
1432
1503
  scope: "docs",
1433
1504
  cwd: f.git.docsGitCwd,
1434
1505
  cmd: f.issueNumber ? tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1456,7 +1527,8 @@ function getStepDefinitions(lang) {
1456
1527
  return [
1457
1528
  {
1458
1529
  type: "instruction",
1459
- requiresUserOk: true,
1530
+ category: "pr_metadata_migrate",
1531
+ requiresUserCheck: true,
1460
1532
  message: tr(lang, "messages", "prLegacyAsk")
1461
1533
  }
1462
1534
  ];
@@ -1464,7 +1536,8 @@ function getStepDefinitions(lang) {
1464
1536
  return [
1465
1537
  {
1466
1538
  type: "instruction",
1467
- requiresUserOk: true,
1539
+ category: "pr_create",
1540
+ requiresUserCheck: true,
1468
1541
  message: tr(lang, "messages", "prCreate")
1469
1542
  }
1470
1543
  ];
@@ -1484,7 +1557,8 @@ function getStepDefinitions(lang) {
1484
1557
  return [
1485
1558
  {
1486
1559
  type: "instruction",
1487
- requiresUserOk: true,
1560
+ category: "pr_status_update",
1561
+ requiresUserCheck: true,
1488
1562
  message: tr(lang, "messages", "prFillStatus")
1489
1563
  }
1490
1564
  ];
@@ -1493,6 +1567,7 @@ function getStepDefinitions(lang) {
1493
1567
  return [
1494
1568
  {
1495
1569
  type: "instruction",
1570
+ category: "code_review",
1496
1571
  message: tr(lang, "messages", "prResolveReview")
1497
1572
  }
1498
1573
  ];
@@ -1500,6 +1575,7 @@ function getStepDefinitions(lang) {
1500
1575
  return [
1501
1576
  {
1502
1577
  type: "instruction",
1578
+ category: "code_review",
1503
1579
  message: tr(lang, "messages", "prRequestReview")
1504
1580
  }
1505
1581
  ];
@@ -1515,6 +1591,7 @@ function getStepDefinitions(lang) {
1515
1591
  actions: () => [
1516
1592
  {
1517
1593
  type: "instruction",
1594
+ category: "feature_done",
1518
1595
  message: tr(lang, "messages", "featureDone")
1519
1596
  }
1520
1597
  ]
@@ -1529,12 +1606,53 @@ getStepDefinitions("ko");
1529
1606
  getStepsMap("ko");
1530
1607
 
1531
1608
  // src/utils/context/progress.ts
1532
- function resolveFeatureProgress(feature, stepDefinitions, lang) {
1609
+ function normalizeApprovalToken(value) {
1610
+ return (value ?? "").trim().toLowerCase();
1611
+ }
1612
+ function applyApprovalPolicy(step, actions, approval) {
1613
+ if (!approval) return actions;
1614
+ const mode = approval.mode ?? "builtin";
1615
+ if (mode === "builtin") return actions;
1616
+ if (mode === "steps") {
1617
+ const required = new Set(
1618
+ (approval.requireCheckSteps ?? approval.requireOkSteps ?? []).map((n) => typeof n === "number" ? n : Number(n)).filter((n) => Number.isFinite(n))
1619
+ );
1620
+ const requiresUserCheck = required.has(step);
1621
+ return actions.map((a) => ({ ...a, requiresUserCheck }));
1622
+ }
1623
+ const requiredCategories = new Set(
1624
+ (approval.requireCheckCategories ?? approval.requireOkCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
1625
+ );
1626
+ const skippedCategories = new Set(
1627
+ (approval.skipCheckCategories ?? approval.skipOkCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
1628
+ );
1629
+ const defaultPolicy = approval.default ?? "keep";
1630
+ return actions.map((a) => {
1631
+ const builtin = Boolean(a.requiresUserCheck);
1632
+ const category = normalizeApprovalToken(a.category ?? "uncategorized");
1633
+ let requiresUserCheck = builtin;
1634
+ if (requiredCategories.has("*") || requiredCategories.has(category)) {
1635
+ requiresUserCheck = true;
1636
+ } else if (skippedCategories.has("*") || skippedCategories.has(category)) {
1637
+ requiresUserCheck = false;
1638
+ } else if (defaultPolicy === "require") {
1639
+ requiresUserCheck = true;
1640
+ } else if (defaultPolicy === "skip") {
1641
+ requiresUserCheck = false;
1642
+ }
1643
+ return { ...a, requiresUserCheck };
1644
+ });
1645
+ }
1646
+ function resolveFeatureProgress(feature, stepDefinitions, lang, approval) {
1533
1647
  const ordered = [...stepDefinitions].sort((a, b) => a.step - b.step);
1534
1648
  for (const definition of ordered) {
1535
1649
  if (!definition.current) continue;
1536
1650
  if (definition.current.when(feature)) {
1537
- const actions = definition.current.actions(feature);
1651
+ const actions = applyApprovalPolicy(
1652
+ definition.step,
1653
+ definition.current.actions(feature),
1654
+ approval
1655
+ );
1538
1656
  return {
1539
1657
  currentStep: definition.step,
1540
1658
  actions,
@@ -1548,6 +1666,7 @@ function resolveFeatureProgress(feature, stepDefinitions, lang) {
1548
1666
  actions: [
1549
1667
  {
1550
1668
  type: "instruction",
1669
+ category: "fallback",
1551
1670
  message: tr(lang, "messages", "fallbackRerunContext")
1552
1671
  }
1553
1672
  ],
@@ -1654,6 +1773,16 @@ function extractSpecValue(content, key) {
1654
1773
  const match = content.match(regex);
1655
1774
  return match ? match[1].trim() : void 0;
1656
1775
  }
1776
+ function hasSpecKey(content, key) {
1777
+ const regex = new RegExp(
1778
+ `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:`,
1779
+ "m"
1780
+ );
1781
+ return regex.test(content);
1782
+ }
1783
+ function hasAnySpecKey(content, keys) {
1784
+ return keys.some((key) => hasSpecKey(content, key));
1785
+ }
1657
1786
  function extractFirstSpecValue(content, keys) {
1658
1787
  for (const key of keys) {
1659
1788
  const value = extractSpecValue(content, key);
@@ -1693,7 +1822,13 @@ function parseTasks(content) {
1693
1822
  let activeTask;
1694
1823
  let nextTodoTask;
1695
1824
  const lines = content.split("\n");
1825
+ let inCodeBlock = false;
1696
1826
  for (const line of lines) {
1827
+ if (/^\s*(```|~~~)/.test(line)) {
1828
+ inCodeBlock = !inCodeBlock;
1829
+ continue;
1830
+ }
1831
+ if (inCodeBlock) continue;
1697
1832
  const match = line.match(/^\s*-\s*\[([A-Z]+)\]((?:\[[^\]]+\])*)\s*(.+?)\s*$/);
1698
1833
  if (!match) continue;
1699
1834
  const status = match[1].toUpperCase();
@@ -1737,13 +1872,13 @@ function isPrMetadataConfigured2(feature) {
1737
1872
  }
1738
1873
  async function parseFeature(featurePath, type, context, options) {
1739
1874
  const lang = options.lang;
1740
- const folderName = path4.basename(featurePath);
1875
+ const folderName = path11.basename(featurePath);
1741
1876
  const match = folderName.match(/^(F\d+)-(.+)$/);
1742
1877
  const id = match?.[1];
1743
1878
  const slug = match?.[2] || folderName;
1744
- const specPath = path4.join(featurePath, "spec.md");
1745
- const planPath = path4.join(featurePath, "plan.md");
1746
- const tasksPath = path4.join(featurePath, "tasks.md");
1879
+ const specPath = path11.join(featurePath, "spec.md");
1880
+ const planPath = path11.join(featurePath, "plan.md");
1881
+ const tasksPath = path11.join(featurePath, "tasks.md");
1747
1882
  let specStatus;
1748
1883
  let issueNumber;
1749
1884
  const specExists = await fs8.pathExists(specPath);
@@ -1765,6 +1900,8 @@ async function parseFeature(featurePath, type, context, options) {
1765
1900
  const tasksSummary = { total: 0, todo: 0, doing: 0, done: 0 };
1766
1901
  let activeTask;
1767
1902
  let nextTodoTask;
1903
+ let tasksDocStatus;
1904
+ let tasksDocStatusFieldExists = false;
1768
1905
  let completionChecklist;
1769
1906
  let prLink;
1770
1907
  let prStatus;
@@ -1784,11 +1921,14 @@ async function parseFeature(featurePath, type, context, options) {
1784
1921
  const issueValue = extractFirstSpecValue(content, ["\uC774\uC288 \uBC88\uD638", "Issue Number", "Issue"]);
1785
1922
  issueNumber = parseIssueNumber(issueValue);
1786
1923
  }
1924
+ const tasksDocStatusValue = extractFirstSpecValue(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
1925
+ tasksDocStatusFieldExists = hasAnySpecKey(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
1926
+ tasksDocStatus = parseDocStatus(tasksDocStatusValue);
1787
1927
  const prValue = extractFirstSpecValue(content, ["PR", "Pull Request"]);
1788
- prFieldExists = prValue !== void 0;
1928
+ prFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
1789
1929
  prLink = parsePrLink(prValue);
1790
1930
  const prStatusValue = extractFirstSpecValue(content, ["PR \uC0C1\uD0DC", "PR Status"]);
1791
- prStatusFieldExists = prStatusValue !== void 0;
1931
+ prStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
1792
1932
  prStatus = parseDocStatus(prStatusValue);
1793
1933
  }
1794
1934
  const warnings = [];
@@ -1801,7 +1941,7 @@ async function parseFeature(featurePath, type, context, options) {
1801
1941
  slug,
1802
1942
  folderName
1803
1943
  );
1804
- const relativeFeaturePathFromDocs = path4.relative(context.docsDir, featurePath);
1944
+ const relativeFeaturePathFromDocs = path11.relative(context.docsDir, featurePath);
1805
1945
  const docsStatus = getGitStatusPorcelain(context.docsGitCwd, [relativeFeaturePathFromDocs]);
1806
1946
  const docsHasUncommittedChanges = docsStatus === void 0 ? true : docsStatus.trim().length > 0;
1807
1947
  const docsLastCommit = getLastCommitForPath(
@@ -1815,10 +1955,14 @@ async function parseFeature(featurePath, type, context, options) {
1815
1955
  if (tasksExists && (!prFieldExists || !prStatusFieldExists)) {
1816
1956
  warnings.push(tr(lang, "warnings", "legacyTasksPrFields"));
1817
1957
  }
1958
+ if (tasksExists && !tasksDocStatusFieldExists) {
1959
+ warnings.push(tr(lang, "warnings", "legacyTasksDocStatusField"));
1960
+ }
1818
1961
  if (docsEverCommitted && docsHasUncommittedChanges) {
1819
1962
  warnings.push(tr(lang, "warnings", "docsUncommittedChanges"));
1820
1963
  }
1821
- const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist });
1964
+ const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
1965
+ const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
1822
1966
  const workflowDone = implementationDone && specStatus === "Approved" && planStatus === "Approved" && isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink && prStatus === "Approved";
1823
1967
  if (implementationDone && !workflowDone) {
1824
1968
  if (specStatus !== "Approved") {
@@ -1848,6 +1992,7 @@ async function parseFeature(featurePath, type, context, options) {
1848
1992
  issueNumber,
1849
1993
  specStatus,
1850
1994
  planStatus,
1995
+ tasksDocStatus,
1851
1996
  tasks: tasksSummary,
1852
1997
  activeTask,
1853
1998
  nextTodoTask,
@@ -1868,6 +2013,7 @@ async function parseFeature(featurePath, type, context, options) {
1868
2013
  specExists,
1869
2014
  planExists,
1870
2015
  tasksExists,
2016
+ tasksDocStatusFieldExists,
1871
2017
  prFieldExists,
1872
2018
  prStatusFieldExists
1873
2019
  }
@@ -1875,7 +2021,8 @@ async function parseFeature(featurePath, type, context, options) {
1875
2021
  const { currentStep, actions, nextAction } = resolveFeatureProgress(
1876
2022
  featureState,
1877
2023
  options.stepDefinitions,
1878
- lang
2024
+ lang,
2025
+ options.approval
1879
2026
  );
1880
2027
  return { ...featureState, currentStep, actions, nextAction, warnings };
1881
2028
  }
@@ -1924,7 +2071,7 @@ async function scanFeatures(config) {
1924
2071
  docsDir: config.docsDir,
1925
2072
  projectBranchAvailable: Boolean(singleProject?.cwd)
1926
2073
  },
1927
- { lang: config.lang, stepDefinitions }
2074
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1928
2075
  )
1929
2076
  );
1930
2077
  }
@@ -1946,7 +2093,7 @@ async function scanFeatures(config) {
1946
2093
  docsDir: config.docsDir,
1947
2094
  projectBranchAvailable: Boolean(feProject?.cwd)
1948
2095
  },
1949
- { lang: config.lang, stepDefinitions }
2096
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1950
2097
  )
1951
2098
  );
1952
2099
  }
@@ -1965,7 +2112,7 @@ async function scanFeatures(config) {
1965
2112
  docsDir: config.docsDir,
1966
2113
  projectBranchAvailable: Boolean(beProject?.cwd)
1967
2114
  },
1968
- { lang: config.lang, stepDefinitions }
2115
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1969
2116
  )
1970
2117
  );
1971
2118
  }
@@ -2005,7 +2152,7 @@ async function runStatus(options) {
2005
2152
  process.exit(1);
2006
2153
  }
2007
2154
  const { docsDir, projectType, projectName, lang } = config;
2008
- const featuresDir = path4.join(docsDir, "features");
2155
+ const featuresDir = path11.join(docsDir, "features");
2009
2156
  const scan = await scanFeatures(config);
2010
2157
  const features = [];
2011
2158
  const idMap = /* @__PURE__ */ new Map();
@@ -2018,7 +2165,7 @@ async function runStatus(options) {
2018
2165
  ""
2019
2166
  ) : projectName ?? "{{projectName}}";
2020
2167
  const issue = f.issueNumber ? `#${f.issueNumber}` : "-";
2021
- const relPath = path4.relative(docsDir, f.path);
2168
+ const relPath = path11.relative(docsDir, f.path);
2022
2169
  if (!idMap.has(id)) idMap.set(id, []);
2023
2170
  idMap.get(id).push(relPath);
2024
2171
  const total = f.tasks.total;
@@ -2083,7 +2230,7 @@ async function runStatus(options) {
2083
2230
  }
2084
2231
  console.log();
2085
2232
  if (options.write) {
2086
- const outputPath = path4.join(featuresDir, "status.md");
2233
+ const outputPath = path11.join(featuresDir, "status.md");
2087
2234
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2088
2235
  const content = [
2089
2236
  "# Feature Status",
@@ -2111,7 +2258,7 @@ function escapeRegExp2(value) {
2111
2258
  }
2112
2259
  async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
2113
2260
  try {
2114
- const specPath = path4.join(featureDir, "spec.md");
2261
+ const specPath = path11.join(featureDir, "spec.md");
2115
2262
  if (!await fs8.pathExists(specPath)) return fallbackSlug;
2116
2263
  const content = await fs8.readFile(specPath, "utf-8");
2117
2264
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
@@ -2169,7 +2316,7 @@ async function runUpdate(options) {
2169
2316
  }
2170
2317
  const { docsDir, projectType, lang } = config;
2171
2318
  const templatesDir = getTemplatesDir();
2172
- const sourceDir = path4.join(templatesDir, lang, projectType);
2319
+ const sourceDir = path11.join(templatesDir, lang, projectType);
2173
2320
  const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang);
2174
2321
  const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
2175
2322
  const updateAgents = options.agents || options.skills || !hasExplicitSelection;
@@ -2188,12 +2335,12 @@ async function runUpdate(options) {
2188
2335
  agentsMode === "skills" ? tr(lang, "cli", "update.updatingSkills") : tr(lang, "cli", "update.updatingAgents")
2189
2336
  )
2190
2337
  );
2191
- const commonAgentsBase = path4.join(templatesDir, lang, "common", "agents");
2192
- const typeAgentsBase = path4.join(templatesDir, lang, projectType, "agents");
2193
- const targetAgentsBase = path4.join(docsDir, "agents");
2194
- const commonAgents = agentsMode === "skills" ? path4.join(commonAgentsBase, "skills") : commonAgentsBase;
2195
- const typeAgents = agentsMode === "skills" ? path4.join(typeAgentsBase, "skills") : typeAgentsBase;
2196
- const targetAgents = agentsMode === "skills" ? path4.join(targetAgentsBase, "skills") : targetAgentsBase;
2338
+ const commonAgentsBase = path11.join(templatesDir, lang, "common", "agents");
2339
+ const typeAgentsBase = path11.join(templatesDir, lang, projectType, "agents");
2340
+ const targetAgentsBase = path11.join(docsDir, "agents");
2341
+ const commonAgents = agentsMode === "skills" ? path11.join(commonAgentsBase, "skills") : commonAgentsBase;
2342
+ const typeAgents = agentsMode === "skills" ? path11.join(typeAgentsBase, "skills") : typeAgentsBase;
2343
+ const targetAgents = agentsMode === "skills" ? path11.join(targetAgentsBase, "skills") : targetAgentsBase;
2197
2344
  const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
2198
2345
  const projectName = config.projectName ?? "{{projectName}}";
2199
2346
  const commonReplacements = {
@@ -2231,8 +2378,8 @@ async function runUpdate(options) {
2231
2378
  }
2232
2379
  if (updateTemplates) {
2233
2380
  console.log(chalk6.blue(tr(lang, "cli", "update.updatingFeatureBase")));
2234
- const sourceFeatureBase = path4.join(sourceDir, "features", "feature-base");
2235
- const targetFeatureBase = path4.join(docsDir, "features", "feature-base");
2381
+ const sourceFeatureBase = path11.join(sourceDir, "features", "feature-base");
2382
+ const targetFeatureBase = path11.join(docsDir, "features", "feature-base");
2236
2383
  if (await fs8.pathExists(sourceFeatureBase)) {
2237
2384
  const replacements = {
2238
2385
  "{{projectName}}": config.projectName ?? "{{projectName}}"
@@ -2265,8 +2412,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
2265
2412
  const files = await fs8.readdir(sourceDir);
2266
2413
  let updatedCount = 0;
2267
2414
  for (const file of files) {
2268
- const sourcePath = path4.join(sourceDir, file);
2269
- const targetPath = path4.join(targetDir, file);
2415
+ const sourcePath = path11.join(sourceDir, file);
2416
+ const targetPath = path11.join(targetDir, file);
2270
2417
  const stat = await fs8.stat(sourcePath);
2271
2418
  if (stat.isFile()) {
2272
2419
  if (protectedFiles.has(file)) {
@@ -2326,7 +2473,7 @@ function getGitTopLevel2(cwd) {
2326
2473
  function getDocsPorcelainStatus(docsDir) {
2327
2474
  const top = getGitTopLevel2(docsDir);
2328
2475
  if (!top) return null;
2329
- const rel = path4.relative(top, docsDir) || ".";
2476
+ const rel = path11.relative(top, docsDir) || ".";
2330
2477
  try {
2331
2478
  return execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
2332
2479
  cwd: top,
@@ -2375,7 +2522,7 @@ async function runConfig(options) {
2375
2522
  );
2376
2523
  process.exit(1);
2377
2524
  }
2378
- const configPath = path4.join(config.docsDir, ".lee-spec-kit.json");
2525
+ const configPath = path11.join(config.docsDir, ".lee-spec-kit.json");
2379
2526
  if (!options.projectRoot) {
2380
2527
  console.log();
2381
2528
  console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -2608,6 +2755,12 @@ async function runContext(featureName, options) {
2608
2755
  inProgressCandidates: selectionMode === "open" ? inProgressFeatures : [],
2609
2756
  readyToCloseCandidates: selectionMode === "open" ? readyToCloseFeatures : [],
2610
2757
  actions: targetFeatures.length === 1 ? targetFeatures[0].actions : [],
2758
+ checkPolicy: {
2759
+ docPath: "/docs/agents/agents.md",
2760
+ hint: tr(lang, "cli", "context.checkPolicyHint"),
2761
+ token: "OK",
2762
+ config: config.approval ?? { mode: "builtin" }
2763
+ },
2611
2764
  recommendation: ""
2612
2765
  };
2613
2766
  if (result.status === "multiple_active") {
@@ -2623,7 +2776,7 @@ async function runContext(featureName, options) {
2623
2776
  return;
2624
2777
  }
2625
2778
  console.log();
2626
- console.log(chalk6.bold("\u{1F4CD} Current Context Check"));
2779
+ console.log(chalk6.bold(tr(lang, "cli", "context.header")));
2627
2780
  if (config.projectType === "single") {
2628
2781
  if (branches.project.single) {
2629
2782
  console.log(
@@ -2738,7 +2891,8 @@ async function runContext(featureName, options) {
2738
2891
  }
2739
2892
  const f = targetFeatures[0];
2740
2893
  const stepName = stepsMap[f.currentStep] || "Unknown";
2741
- const okTag = (requiresUserOk) => requiresUserOk ? chalk6.yellow(tr(lang, "cli", "context.okRequired")) : "";
2894
+ const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk6.yellow(tr(lang, "cli", "context.checkRequired")) : "";
2895
+ const hasCheckAction = (f.actions || []).some((a) => !!a.requiresUserCheck);
2742
2896
  console.log(
2743
2897
  `\u{1F539} Feature: ${chalk6.bold(f.folderName)} ${config.projectType === "fullstack" ? chalk6.cyan(`(${f.type})`) : ""}`
2744
2898
  );
@@ -2748,7 +2902,7 @@ async function runContext(featureName, options) {
2748
2902
  if (f.issueNumber) {
2749
2903
  console.log(` \u2022 Issue: #${f.issueNumber}`);
2750
2904
  }
2751
- console.log(` \u2022 Path: ${path4.relative(cwd, f.path)}`);
2905
+ console.log(` \u2022 Path: ${path11.relative(cwd, f.path)}`);
2752
2906
  if (f.git.projectBranch) {
2753
2907
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
2754
2908
  }
@@ -2773,6 +2927,9 @@ async function runContext(featureName, options) {
2773
2927
  }
2774
2928
  console.log();
2775
2929
  console.log(chalk6.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
2930
+ if (hasCheckAction) {
2931
+ console.log(chalk6.gray(tr(lang, "cli", "context.checkPolicyHint")));
2932
+ }
2776
2933
  if (!f.actions || f.actions.length === 0) {
2777
2934
  console.log(`\u{1F449} Next Action: ${chalk6.green(chalk6.bold(f.nextAction))}`);
2778
2935
  console.log();
@@ -2782,24 +2939,32 @@ async function runContext(featureName, options) {
2782
2939
  const action = f.actions[0];
2783
2940
  if (action.type === "command") {
2784
2941
  console.log(
2785
- `\u{1F449} Next Action (${chalk6.cyan(action.scope)}): ${okTag(action.requiresUserOk)}${chalk6.green(chalk6.bold(action.cmd))}`
2942
+ `\u{1F449} Next Action (${chalk6.cyan(action.scope)}): ${checkTag(action.requiresUserCheck)}${chalk6.green(chalk6.bold(action.cmd))}`
2786
2943
  );
2944
+ if (action.scope === "docs") {
2945
+ console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
2946
+ }
2787
2947
  } else {
2788
2948
  console.log(
2789
- `\u{1F449} Next Action: ${okTag(action.requiresUserOk)}${chalk6.green(chalk6.bold(action.message))}`
2949
+ `\u{1F449} Next Action: ${checkTag(action.requiresUserCheck)}${chalk6.green(chalk6.bold(action.message))}`
2790
2950
  );
2791
2951
  }
2792
2952
  console.log();
2793
2953
  return;
2794
2954
  }
2795
2955
  console.log(chalk6.green(chalk6.bold("\u{1F449} Next Actions:")));
2956
+ let hasDocsCommand = false;
2796
2957
  f.actions.forEach((action) => {
2797
2958
  if (action.type === "command") {
2798
- console.log(` \u2022 (${action.scope}) ${okTag(action.requiresUserOk)}${action.cmd}`);
2959
+ console.log(` \u2022 (${action.scope}) ${checkTag(action.requiresUserCheck)}${action.cmd}`);
2960
+ if (action.scope === "docs") hasDocsCommand = true;
2799
2961
  } else {
2800
- console.log(` \u2022 ${okTag(action.requiresUserOk)}${action.message}`);
2962
+ console.log(` \u2022 ${checkTag(action.requiresUserCheck)}${action.message}`);
2801
2963
  }
2802
2964
  });
2965
+ if (hasDocsCommand) {
2966
+ console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
2967
+ }
2803
2968
  console.log();
2804
2969
  }
2805
2970
  function printChecklist(f, stepDefinitions) {
@@ -2814,7 +2979,7 @@ function printChecklist(f, stepDefinitions) {
2814
2979
  }
2815
2980
  function formatPath(cwd, p) {
2816
2981
  if (!p) return "";
2817
- return path4.isAbsolute(p) ? path4.relative(cwd, p) : p;
2982
+ return path11.isAbsolute(p) ? path11.relative(cwd, p) : p;
2818
2983
  }
2819
2984
  function detectPlaceholders(content) {
2820
2985
  const patterns = [
@@ -2841,7 +3006,7 @@ async function checkDocsStructure(config, cwd) {
2841
3006
  const issues = [];
2842
3007
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
2843
3008
  for (const dir of requiredDirs) {
2844
- const p = path4.join(config.docsDir, dir);
3009
+ const p = path11.join(config.docsDir, dir);
2845
3010
  if (!await fs8.pathExists(p)) {
2846
3011
  issues.push({
2847
3012
  level: "error",
@@ -2851,7 +3016,7 @@ async function checkDocsStructure(config, cwd) {
2851
3016
  });
2852
3017
  }
2853
3018
  }
2854
- const configPath = path4.join(config.docsDir, ".lee-spec-kit.json");
3019
+ const configPath = path11.join(config.docsDir, ".lee-spec-kit.json");
2855
3020
  if (!await fs8.pathExists(configPath)) {
2856
3021
  issues.push({
2857
3022
  level: "warn",
@@ -2874,13 +3039,13 @@ async function checkFeatures(config, cwd, features) {
2874
3039
  }
2875
3040
  const idMap = /* @__PURE__ */ new Map();
2876
3041
  for (const f of features) {
2877
- const rel = f.docs.featurePathFromDocs || path4.relative(config.docsDir, f.path);
3042
+ const rel = f.docs.featurePathFromDocs || path11.relative(config.docsDir, f.path);
2878
3043
  const id = f.id || "UNKNOWN";
2879
3044
  if (!idMap.has(id)) idMap.set(id, []);
2880
3045
  idMap.get(id).push(rel);
2881
3046
  const featureDocs = ["spec.md", "plan.md", "tasks.md", "decisions.md"];
2882
3047
  for (const file of featureDocs) {
2883
- const p = path4.join(f.path, file);
3048
+ const p = path11.join(f.path, file);
2884
3049
  if (!await fs8.pathExists(p)) continue;
2885
3050
  const content = await fs8.readFile(p, "utf-8");
2886
3051
  const placeholders = detectPlaceholders(content);
@@ -2906,7 +3071,7 @@ async function checkFeatures(config, cwd, features) {
2906
3071
  level: "warn",
2907
3072
  code: "spec_status_unset",
2908
3073
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
2909
- path: formatPath(cwd, path4.join(f.path, "spec.md"))
3074
+ path: formatPath(cwd, path11.join(f.path, "spec.md"))
2910
3075
  });
2911
3076
  }
2912
3077
  if (f.docs.planExists && !f.planStatus) {
@@ -2914,7 +3079,7 @@ async function checkFeatures(config, cwd, features) {
2914
3079
  level: "warn",
2915
3080
  code: "plan_status_unset",
2916
3081
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
2917
- path: formatPath(cwd, path4.join(f.path, "plan.md"))
3082
+ path: formatPath(cwd, path11.join(f.path, "plan.md"))
2918
3083
  });
2919
3084
  }
2920
3085
  if (f.docs.tasksExists && f.tasks.total === 0) {
@@ -2922,7 +3087,23 @@ async function checkFeatures(config, cwd, features) {
2922
3087
  level: "warn",
2923
3088
  code: "tasks_empty",
2924
3089
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
2925
- path: formatPath(cwd, path4.join(f.path, "tasks.md"))
3090
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
3091
+ });
3092
+ }
3093
+ if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists) {
3094
+ issues.push({
3095
+ level: "warn",
3096
+ code: "tasks_doc_status_missing",
3097
+ message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
3098
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
3099
+ });
3100
+ }
3101
+ if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus) {
3102
+ issues.push({
3103
+ level: "warn",
3104
+ code: "tasks_doc_status_unset",
3105
+ message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
3106
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
2926
3107
  });
2927
3108
  }
2928
3109
  }
@@ -2946,7 +3127,7 @@ async function checkFeatures(config, cwd, features) {
2946
3127
  level: "warn",
2947
3128
  code: "missing_feature_id",
2948
3129
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
2949
- path: formatPath(cwd, path4.join(config.docsDir, p))
3130
+ path: formatPath(cwd, path11.join(config.docsDir, p))
2950
3131
  });
2951
3132
  }
2952
3133
  return issues;
@@ -2996,7 +3177,7 @@ function doctorCommand(program2) {
2996
3177
  }
2997
3178
  console.log();
2998
3179
  console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
2999
- console.log(chalk6.gray(`- Docs: ${path4.relative(cwd, docsDir)}`));
3180
+ console.log(chalk6.gray(`- Docs: ${path11.relative(cwd, docsDir)}`));
3000
3181
  console.log(chalk6.gray(`- Type: ${projectType}`));
3001
3182
  console.log(chalk6.gray(`- Lang: ${lang}`));
3002
3183
  console.log();
@@ -3090,11 +3271,11 @@ ${version}
3090
3271
  }
3091
3272
  return `${ascii}${footer}`;
3092
3273
  }
3093
- var CACHE_FILE = path4.join(os.homedir(), ".lee-spec-kit-version-cache.json");
3274
+ var CACHE_FILE = path11.join(os.homedir(), ".lee-spec-kit-version-cache.json");
3094
3275
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
3095
3276
  function getCurrentVersion() {
3096
3277
  try {
3097
- const packageJsonPath = path4.join(__dirname$1, "..", "package.json");
3278
+ const packageJsonPath = path11.join(__dirname$1, "..", "package.json");
3098
3279
  if (fs8.existsSync(packageJsonPath)) {
3099
3280
  const pkg = fs8.readJsonSync(packageJsonPath);
3100
3281
  return pkg.version;
@@ -3184,7 +3365,7 @@ function shouldCheckForUpdates() {
3184
3365
  if (shouldCheckForUpdates()) checkForUpdates();
3185
3366
  function getCliVersion() {
3186
3367
  try {
3187
- const packageJsonPath = path4.join(__dirname$1, "..", "package.json");
3368
+ const packageJsonPath = path11.join(__dirname$1, "..", "package.json");
3188
3369
  if (fs8.existsSync(packageJsonPath)) {
3189
3370
  const pkg = fs8.readJsonSync(packageJsonPath);
3190
3371
  if (pkg?.version) return String(pkg.version);