lee-spec-kit 0.4.12 → 0.4.14

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 +54 -1
  2. package/README.md +58 -1
  3. package/dist/index.js +314 -124
  4. package/package.json +1 -1
  5. package/templates/en/common/agents/pr-template.md +21 -3
  6. package/templates/en/common/agents/skills/create-feature.md +2 -16
  7. package/templates/en/common/agents/skills/create-pr.md +24 -4
  8. package/templates/en/common/agents/skills/execute-task.md +5 -3
  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 +21 -3
  20. package/templates/ko/common/agents/skills/create-feature.md +2 -16
  21. package/templates/ko/common/agents/skills/create-pr.md +24 -4
  22. package/templates/ko/common/agents/skills/execute-task.md +7 -13
  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,15 @@ 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
+ pr: {
840
+ screenshots: { upload: false }
841
+ },
842
+ // Approval policy for "requiresUserCheck" actions shown by `context`.
843
+ // - builtin (default): Use requiresUserCheck embedded in steps/actions.
844
+ // - category: Override by action category (recommended for automation).
845
+ // - steps: Override by step number (fragile; not recommended).
846
+ approval: { mode: "builtin" }
823
847
  };
824
848
  if (docsRepo === "standalone") {
825
849
  config.pushDocs = pushDocs;
@@ -830,7 +854,7 @@ async function runInit(options) {
830
854
  config.projectRoot = projectRoot;
831
855
  }
832
856
  }
833
- const configPath = path4.join(targetDir, ".lee-spec-kit.json");
857
+ const configPath = path11.join(targetDir, ".lee-spec-kit.json");
834
858
  await fs8.writeJson(configPath, config, { spaces: 2 });
835
859
  console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
836
860
  console.log();
@@ -867,7 +891,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
867
891
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
868
892
  runGit(["init"], cwd);
869
893
  }
870
- const relativePath = path4.relative(cwd, targetDir);
894
+ const relativePath = path11.relative(cwd, targetDir);
871
895
  const stagedBeforeAdd = getCachedStagedFiles(cwd);
872
896
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
873
897
  console.log(
@@ -905,10 +929,10 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
905
929
  }
906
930
  function getAncestorDirs(startDir) {
907
931
  const dirs = [];
908
- let current = path4.resolve(startDir);
932
+ let current = path11.resolve(startDir);
909
933
  while (true) {
910
934
  dirs.push(current);
911
- const parent = path4.dirname(current);
935
+ const parent = path11.dirname(current);
912
936
  if (parent === current) break;
913
937
  current = parent;
914
938
  }
@@ -917,21 +941,21 @@ function getAncestorDirs(startDir) {
917
941
  async function getConfig(cwd) {
918
942
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
919
943
  const baseDirs = [
920
- ...explicitDocsDir ? [path4.resolve(explicitDocsDir)] : [],
944
+ ...explicitDocsDir ? [path11.resolve(explicitDocsDir)] : [],
921
945
  ...getAncestorDirs(cwd)
922
946
  ];
923
947
  const visitedBaseDirs = /* @__PURE__ */ new Set();
924
948
  const visitedDocsDirs = /* @__PURE__ */ new Set();
925
949
  for (const baseDir of baseDirs) {
926
- const resolvedBaseDir = path4.resolve(baseDir);
950
+ const resolvedBaseDir = path11.resolve(baseDir);
927
951
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
928
952
  visitedBaseDirs.add(resolvedBaseDir);
929
- const possibleDocsDirs = [path4.join(resolvedBaseDir, "docs"), resolvedBaseDir];
953
+ const possibleDocsDirs = [path11.join(resolvedBaseDir, "docs"), resolvedBaseDir];
930
954
  for (const docsDir of possibleDocsDirs) {
931
- const resolvedDocsDir = path4.resolve(docsDir);
955
+ const resolvedDocsDir = path11.resolve(docsDir);
932
956
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
933
957
  visitedDocsDirs.add(resolvedDocsDir);
934
- const configPath = path4.join(resolvedDocsDir, ".lee-spec-kit.json");
958
+ const configPath = path11.join(resolvedDocsDir, ".lee-spec-kit.json");
935
959
  if (await fs8.pathExists(configPath)) {
936
960
  try {
937
961
  const configFile = await fs8.readJson(configPath);
@@ -943,18 +967,20 @@ async function getConfig(cwd) {
943
967
  docsRepo: configFile.docsRepo,
944
968
  pushDocs: configFile.pushDocs,
945
969
  docsRemote: configFile.docsRemote,
946
- projectRoot: configFile.projectRoot
970
+ projectRoot: configFile.projectRoot,
971
+ pr: configFile.pr,
972
+ approval: configFile.approval
947
973
  };
948
974
  } catch {
949
975
  }
950
976
  }
951
- const agentsPath = path4.join(resolvedDocsDir, "agents");
952
- const featuresPath = path4.join(resolvedDocsDir, "features");
977
+ const agentsPath = path11.join(resolvedDocsDir, "agents");
978
+ const featuresPath = path11.join(resolvedDocsDir, "features");
953
979
  if (await fs8.pathExists(agentsPath) && await fs8.pathExists(featuresPath)) {
954
- const bePath = path4.join(featuresPath, "be");
955
- const fePath = path4.join(featuresPath, "fe");
980
+ const bePath = path11.join(featuresPath, "be");
981
+ const fePath = path11.join(featuresPath, "fe");
956
982
  const projectType = await fs8.pathExists(bePath) || await fs8.pathExists(fePath) ? "fullstack" : "single";
957
- const agentsMdPath = path4.join(agentsPath, "agents.md");
983
+ const agentsMdPath = path11.join(agentsPath, "agents.md");
958
984
  let lang = "en";
959
985
  if (await fs8.pathExists(agentsMdPath)) {
960
986
  const content = await fs8.readFile(agentsMdPath, "utf-8");
@@ -1031,12 +1057,12 @@ async function runFeature(name, options) {
1031
1057
  }
1032
1058
  let featuresDir;
1033
1059
  if (projectType === "fullstack" && repo) {
1034
- featuresDir = path4.join(docsDir, "features", repo);
1060
+ featuresDir = path11.join(docsDir, "features", repo);
1035
1061
  } else {
1036
- featuresDir = path4.join(docsDir, "features");
1062
+ featuresDir = path11.join(docsDir, "features");
1037
1063
  }
1038
1064
  const featureFolderName = `${featureId}-${name}`;
1039
- const featureDir = path4.join(featuresDir, featureFolderName);
1065
+ const featureDir = path11.join(featuresDir, featureFolderName);
1040
1066
  if (await fs8.pathExists(featureDir)) {
1041
1067
  console.error(
1042
1068
  chalk6.red(
@@ -1045,7 +1071,7 @@ async function runFeature(name, options) {
1045
1071
  );
1046
1072
  process.exit(1);
1047
1073
  }
1048
- const featureBasePath = path4.join(docsDir, "features", "feature-base");
1074
+ const featureBasePath = path11.join(docsDir, "features", "feature-base");
1049
1075
  if (!await fs8.pathExists(featureBasePath)) {
1050
1076
  console.error(
1051
1077
  chalk6.red(
@@ -1103,12 +1129,12 @@ async function runFeature(name, options) {
1103
1129
  console.log();
1104
1130
  }
1105
1131
  async function getNextFeatureId(docsDir, projectType) {
1106
- const featuresDir = path4.join(docsDir, "features");
1132
+ const featuresDir = path11.join(docsDir, "features");
1107
1133
  let max = 0;
1108
1134
  const scanDirs = [];
1109
1135
  if (projectType === "fullstack") {
1110
- scanDirs.push(path4.join(featuresDir, "be"));
1111
- scanDirs.push(path4.join(featuresDir, "fe"));
1136
+ scanDirs.push(path11.join(featuresDir, "be"));
1137
+ scanDirs.push(path11.join(featuresDir, "fe"));
1112
1138
  } else {
1113
1139
  scanDirs.push(featuresDir);
1114
1140
  }
@@ -1133,14 +1159,17 @@ async function getNextFeatureId(docsDir, projectType) {
1133
1159
  function isCompletionChecklistDone(feature) {
1134
1160
  return !!feature.completionChecklist && feature.completionChecklist.total > 0 && feature.completionChecklist.checked === feature.completionChecklist.total;
1135
1161
  }
1162
+ function isTasksDocApproved(feature) {
1163
+ return !feature.docs.tasksDocStatusFieldExists || feature.tasksDocStatus === "Approved";
1164
+ }
1136
1165
  function isImplementationDone(feature) {
1137
- return feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature);
1166
+ return feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature);
1138
1167
  }
1139
1168
  function isPrMetadataConfigured(feature) {
1140
1169
  return feature.docs.prFieldExists && feature.docs.prStatusFieldExists;
1141
1170
  }
1142
1171
  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";
1172
+ 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
1173
  }
1145
1174
  function getStepDefinitions(lang) {
1146
1175
  return [
@@ -1160,6 +1189,7 @@ function getStepDefinitions(lang) {
1160
1189
  actions: (f) => [
1161
1190
  {
1162
1191
  type: "instruction",
1192
+ category: "spec_write",
1163
1193
  message: !f.docs.specExists ? tr(lang, "messages", "specCreate") : tr(lang, "messages", "specImprove")
1164
1194
  }
1165
1195
  ]
@@ -1174,7 +1204,8 @@ function getStepDefinitions(lang) {
1174
1204
  actions: () => [
1175
1205
  {
1176
1206
  type: "instruction",
1177
- requiresUserOk: true,
1207
+ category: "spec_approve",
1208
+ requiresUserCheck: true,
1178
1209
  message: tr(lang, "messages", "specApproval")
1179
1210
  }
1180
1211
  ]
@@ -1191,6 +1222,7 @@ function getStepDefinitions(lang) {
1191
1222
  actions: (f) => [
1192
1223
  {
1193
1224
  type: "instruction",
1225
+ category: "plan_write",
1194
1226
  message: !f.docs.planExists ? tr(lang, "messages", "planCreate") : tr(lang, "messages", "planImprove")
1195
1227
  }
1196
1228
  ]
@@ -1205,7 +1237,8 @@ function getStepDefinitions(lang) {
1205
1237
  actions: () => [
1206
1238
  {
1207
1239
  type: "instruction",
1208
- requiresUserOk: true,
1240
+ category: "plan_approve",
1241
+ requiresUserCheck: true,
1209
1242
  message: tr(lang, "messages", "planApproval")
1210
1243
  }
1211
1244
  ]
@@ -1215,24 +1248,54 @@ function getStepDefinitions(lang) {
1215
1248
  step: 6,
1216
1249
  name: tr(lang, "steps", "tasksWrite"),
1217
1250
  checklist: {
1218
- done: (f) => f.docs.tasksExists && f.tasks.total > 0,
1251
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && isTasksDocApproved(f),
1219
1252
  detail: (f) => f.tasks.total > 0 ? `(${f.tasks.total})` : ""
1220
1253
  },
1221
1254
  current: {
1222
- when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0),
1255
+ when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0 || f.docs.tasksDocStatusFieldExists && (!f.tasksDocStatus || f.tasksDocStatus === "Draft" || f.tasksDocStatus === "Review")),
1223
1256
  actions: (f) => {
1224
1257
  if (!f.docs.tasksExists) {
1225
1258
  return [
1226
1259
  {
1227
1260
  type: "instruction",
1261
+ category: "tasks_write",
1228
1262
  message: tr(lang, "messages", "tasksCreate")
1229
1263
  }
1230
1264
  ];
1231
1265
  }
1266
+ if (f.tasks.total === 0) {
1267
+ return [
1268
+ {
1269
+ type: "instruction",
1270
+ category: "tasks_write",
1271
+ message: tr(lang, "messages", "tasksNeedAtLeastOne")
1272
+ }
1273
+ ];
1274
+ }
1275
+ if (f.docs.tasksDocStatusFieldExists && (!f.tasksDocStatus || f.tasksDocStatus === "Draft")) {
1276
+ return [
1277
+ {
1278
+ type: "instruction",
1279
+ category: "tasks_write",
1280
+ message: tr(lang, "messages", "tasksImprove")
1281
+ }
1282
+ ];
1283
+ }
1284
+ if (f.docs.tasksDocStatusFieldExists && f.tasksDocStatus === "Review") {
1285
+ return [
1286
+ {
1287
+ type: "instruction",
1288
+ category: "tasks_approve",
1289
+ requiresUserCheck: true,
1290
+ message: tr(lang, "messages", "tasksApproval")
1291
+ }
1292
+ ];
1293
+ }
1232
1294
  return [
1233
1295
  {
1234
1296
  type: "instruction",
1235
- message: tr(lang, "messages", "tasksNeedAtLeastOne")
1297
+ category: "tasks_write",
1298
+ message: tr(lang, "messages", "tasksImprove")
1236
1299
  }
1237
1300
  ];
1238
1301
  }
@@ -1242,16 +1305,17 @@ function getStepDefinitions(lang) {
1242
1305
  step: 7,
1243
1306
  name: tr(lang, "steps", "docsInitialCommit"),
1244
1307
  checklist: {
1245
- done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && f.git.docsEverCommitted
1308
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && f.git.docsEverCommitted
1246
1309
  },
1247
1310
  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,
1311
+ 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
1312
  actions: (f) => {
1250
1313
  if (f.issueNumber) {
1251
1314
  return [
1252
1315
  {
1253
1316
  type: "command",
1254
- requiresUserOk: true,
1317
+ category: "docs_commit",
1318
+ requiresUserCheck: true,
1255
1319
  scope: "docs",
1256
1320
  cwd: f.git.docsGitCwd,
1257
1321
  cmd: tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1266,7 +1330,8 @@ function getStepDefinitions(lang) {
1266
1330
  return [
1267
1331
  {
1268
1332
  type: "command",
1269
- requiresUserOk: true,
1333
+ category: "docs_commit",
1334
+ requiresUserCheck: true,
1270
1335
  scope: "docs",
1271
1336
  cwd: f.git.docsGitCwd,
1272
1337
  cmd: tr(lang, "messages", "docsCommitPlanning", {
@@ -1286,12 +1351,13 @@ function getStepDefinitions(lang) {
1286
1351
  done: (f) => !!f.issueNumber
1287
1352
  },
1288
1353
  current: {
1289
- when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && !f.issueNumber,
1354
+ when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && !f.issueNumber,
1290
1355
  actions: (f) => {
1291
1356
  return [
1292
1357
  {
1293
1358
  type: "instruction",
1294
- requiresUserOk: true,
1359
+ category: "issue_create",
1360
+ requiresUserCheck: true,
1295
1361
  message: tr(lang, "messages", "issueCreateAndWrite")
1296
1362
  }
1297
1363
  ];
@@ -1309,6 +1375,7 @@ function getStepDefinitions(lang) {
1309
1375
  return [
1310
1376
  {
1311
1377
  type: "instruction",
1378
+ category: "branch_create",
1312
1379
  message: tr(lang, "messages", "standaloneNeedsProjectRoot")
1313
1380
  }
1314
1381
  ];
@@ -1316,6 +1383,7 @@ function getStepDefinitions(lang) {
1316
1383
  return [
1317
1384
  {
1318
1385
  type: "command",
1386
+ category: "branch_create",
1319
1387
  scope: "project",
1320
1388
  cwd: f.git.projectGitCwd,
1321
1389
  cmd: tr(lang, "messages", "createBranch", {
@@ -1332,17 +1400,18 @@ function getStepDefinitions(lang) {
1332
1400
  step: 10,
1333
1401
  name: tr(lang, "steps", "tasksExecute"),
1334
1402
  checklist: {
1335
- done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f),
1403
+ done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && isTasksDocApproved(f),
1336
1404
  detail: (f) => f.tasks.total > 0 ? `(${f.tasks.done}/${f.tasks.total})` : ""
1337
1405
  },
1338
1406
  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),
1407
+ 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
1408
  actions: (f) => {
1341
1409
  if (f.tasks.total === f.tasks.done && !isCompletionChecklistDone(f)) {
1342
1410
  const actions = [
1343
1411
  {
1344
1412
  type: "instruction",
1345
- requiresUserOk: true,
1413
+ category: "task_execute",
1414
+ requiresUserCheck: true,
1346
1415
  message: !f.completionChecklist ? tr(lang, "messages", "tasksAllDoneButNoChecklist") : tr(lang, "messages", "tasksAllDoneButChecklist", {
1347
1416
  checked: f.completionChecklist.checked,
1348
1417
  total: f.completionChecklist.total
@@ -1352,7 +1421,8 @@ function getStepDefinitions(lang) {
1352
1421
  if (!isPrMetadataConfigured(f)) {
1353
1422
  actions.push({
1354
1423
  type: "instruction",
1355
- requiresUserOk: true,
1424
+ category: "pr_metadata_migrate",
1425
+ requiresUserCheck: true,
1356
1426
  message: tr(lang, "messages", "prLegacyAsk")
1357
1427
  });
1358
1428
  }
@@ -1362,7 +1432,8 @@ function getStepDefinitions(lang) {
1362
1432
  return [
1363
1433
  {
1364
1434
  type: "instruction",
1365
- requiresUserOk: true,
1435
+ category: "task_execute",
1436
+ requiresUserCheck: true,
1366
1437
  message: tr(lang, "messages", "finishDoingTask", {
1367
1438
  title: f.activeTask.title,
1368
1439
  done: f.tasks.done,
@@ -1376,7 +1447,8 @@ function getStepDefinitions(lang) {
1376
1447
  return [
1377
1448
  {
1378
1449
  type: "command",
1379
- requiresUserOk: true,
1450
+ category: "docs_commit",
1451
+ requiresUserCheck: true,
1380
1452
  scope: "docs",
1381
1453
  cwd: f.git.docsGitCwd,
1382
1454
  cmd: f.issueNumber ? tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1395,7 +1467,8 @@ function getStepDefinitions(lang) {
1395
1467
  return [
1396
1468
  {
1397
1469
  type: "instruction",
1398
- requiresUserOk: true,
1470
+ category: "task_execute",
1471
+ requiresUserCheck: true,
1399
1472
  message: tr(lang, "messages", "startNextTodoTask", {
1400
1473
  title: f.nextTodoTask.title,
1401
1474
  done: f.tasks.done,
@@ -1407,7 +1480,8 @@ function getStepDefinitions(lang) {
1407
1480
  return [
1408
1481
  {
1409
1482
  type: "instruction",
1410
- requiresUserOk: true,
1483
+ category: "task_execute",
1484
+ requiresUserCheck: true,
1411
1485
  message: tr(lang, "messages", "checkTaskStatuses", {
1412
1486
  done: f.tasks.done,
1413
1487
  total: f.tasks.total
@@ -1428,7 +1502,8 @@ function getStepDefinitions(lang) {
1428
1502
  actions: (f) => [
1429
1503
  {
1430
1504
  type: "command",
1431
- requiresUserOk: true,
1505
+ category: "docs_commit",
1506
+ requiresUserCheck: true,
1432
1507
  scope: "docs",
1433
1508
  cwd: f.git.docsGitCwd,
1434
1509
  cmd: f.issueNumber ? tr(lang, "messages", "docsCommitIssueUpdate", {
@@ -1456,7 +1531,8 @@ function getStepDefinitions(lang) {
1456
1531
  return [
1457
1532
  {
1458
1533
  type: "instruction",
1459
- requiresUserOk: true,
1534
+ category: "pr_metadata_migrate",
1535
+ requiresUserCheck: true,
1460
1536
  message: tr(lang, "messages", "prLegacyAsk")
1461
1537
  }
1462
1538
  ];
@@ -1464,7 +1540,8 @@ function getStepDefinitions(lang) {
1464
1540
  return [
1465
1541
  {
1466
1542
  type: "instruction",
1467
- requiresUserOk: true,
1543
+ category: "pr_create",
1544
+ requiresUserCheck: true,
1468
1545
  message: tr(lang, "messages", "prCreate")
1469
1546
  }
1470
1547
  ];
@@ -1484,7 +1561,8 @@ function getStepDefinitions(lang) {
1484
1561
  return [
1485
1562
  {
1486
1563
  type: "instruction",
1487
- requiresUserOk: true,
1564
+ category: "pr_status_update",
1565
+ requiresUserCheck: true,
1488
1566
  message: tr(lang, "messages", "prFillStatus")
1489
1567
  }
1490
1568
  ];
@@ -1493,6 +1571,7 @@ function getStepDefinitions(lang) {
1493
1571
  return [
1494
1572
  {
1495
1573
  type: "instruction",
1574
+ category: "code_review",
1496
1575
  message: tr(lang, "messages", "prResolveReview")
1497
1576
  }
1498
1577
  ];
@@ -1500,6 +1579,7 @@ function getStepDefinitions(lang) {
1500
1579
  return [
1501
1580
  {
1502
1581
  type: "instruction",
1582
+ category: "code_review",
1503
1583
  message: tr(lang, "messages", "prRequestReview")
1504
1584
  }
1505
1585
  ];
@@ -1515,6 +1595,7 @@ function getStepDefinitions(lang) {
1515
1595
  actions: () => [
1516
1596
  {
1517
1597
  type: "instruction",
1598
+ category: "feature_done",
1518
1599
  message: tr(lang, "messages", "featureDone")
1519
1600
  }
1520
1601
  ]
@@ -1529,12 +1610,53 @@ getStepDefinitions("ko");
1529
1610
  getStepsMap("ko");
1530
1611
 
1531
1612
  // src/utils/context/progress.ts
1532
- function resolveFeatureProgress(feature, stepDefinitions, lang) {
1613
+ function normalizeApprovalToken(value) {
1614
+ return (value ?? "").trim().toLowerCase();
1615
+ }
1616
+ function applyApprovalPolicy(step, actions, approval) {
1617
+ if (!approval) return actions;
1618
+ const mode = approval.mode ?? "builtin";
1619
+ if (mode === "builtin") return actions;
1620
+ if (mode === "steps") {
1621
+ const required = new Set(
1622
+ (approval.requireCheckSteps ?? approval.requireOkSteps ?? []).map((n) => typeof n === "number" ? n : Number(n)).filter((n) => Number.isFinite(n))
1623
+ );
1624
+ const requiresUserCheck = required.has(step);
1625
+ return actions.map((a) => ({ ...a, requiresUserCheck }));
1626
+ }
1627
+ const requiredCategories = new Set(
1628
+ (approval.requireCheckCategories ?? approval.requireOkCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
1629
+ );
1630
+ const skippedCategories = new Set(
1631
+ (approval.skipCheckCategories ?? approval.skipOkCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
1632
+ );
1633
+ const defaultPolicy = approval.default ?? "keep";
1634
+ return actions.map((a) => {
1635
+ const builtin = Boolean(a.requiresUserCheck);
1636
+ const category = normalizeApprovalToken(a.category ?? "uncategorized");
1637
+ let requiresUserCheck = builtin;
1638
+ if (requiredCategories.has("*") || requiredCategories.has(category)) {
1639
+ requiresUserCheck = true;
1640
+ } else if (skippedCategories.has("*") || skippedCategories.has(category)) {
1641
+ requiresUserCheck = false;
1642
+ } else if (defaultPolicy === "require") {
1643
+ requiresUserCheck = true;
1644
+ } else if (defaultPolicy === "skip") {
1645
+ requiresUserCheck = false;
1646
+ }
1647
+ return { ...a, requiresUserCheck };
1648
+ });
1649
+ }
1650
+ function resolveFeatureProgress(feature, stepDefinitions, lang, approval) {
1533
1651
  const ordered = [...stepDefinitions].sort((a, b) => a.step - b.step);
1534
1652
  for (const definition of ordered) {
1535
1653
  if (!definition.current) continue;
1536
1654
  if (definition.current.when(feature)) {
1537
- const actions = definition.current.actions(feature);
1655
+ const actions = applyApprovalPolicy(
1656
+ definition.step,
1657
+ definition.current.actions(feature),
1658
+ approval
1659
+ );
1538
1660
  return {
1539
1661
  currentStep: definition.step,
1540
1662
  actions,
@@ -1548,6 +1670,7 @@ function resolveFeatureProgress(feature, stepDefinitions, lang) {
1548
1670
  actions: [
1549
1671
  {
1550
1672
  type: "instruction",
1673
+ category: "fallback",
1551
1674
  message: tr(lang, "messages", "fallbackRerunContext")
1552
1675
  }
1553
1676
  ],
@@ -1654,6 +1777,16 @@ function extractSpecValue(content, key) {
1654
1777
  const match = content.match(regex);
1655
1778
  return match ? match[1].trim() : void 0;
1656
1779
  }
1780
+ function hasSpecKey(content, key) {
1781
+ const regex = new RegExp(
1782
+ `^\\s*-\\s*\\*\\*${escapeRegExp(key)}\\*\\*\\s*:`,
1783
+ "m"
1784
+ );
1785
+ return regex.test(content);
1786
+ }
1787
+ function hasAnySpecKey(content, keys) {
1788
+ return keys.some((key) => hasSpecKey(content, key));
1789
+ }
1657
1790
  function extractFirstSpecValue(content, keys) {
1658
1791
  for (const key of keys) {
1659
1792
  const value = extractSpecValue(content, key);
@@ -1693,7 +1826,13 @@ function parseTasks(content) {
1693
1826
  let activeTask;
1694
1827
  let nextTodoTask;
1695
1828
  const lines = content.split("\n");
1829
+ let inCodeBlock = false;
1696
1830
  for (const line of lines) {
1831
+ if (/^\s*(```|~~~)/.test(line)) {
1832
+ inCodeBlock = !inCodeBlock;
1833
+ continue;
1834
+ }
1835
+ if (inCodeBlock) continue;
1697
1836
  const match = line.match(/^\s*-\s*\[([A-Z]+)\]((?:\[[^\]]+\])*)\s*(.+?)\s*$/);
1698
1837
  if (!match) continue;
1699
1838
  const status = match[1].toUpperCase();
@@ -1737,13 +1876,13 @@ function isPrMetadataConfigured2(feature) {
1737
1876
  }
1738
1877
  async function parseFeature(featurePath, type, context, options) {
1739
1878
  const lang = options.lang;
1740
- const folderName = path4.basename(featurePath);
1879
+ const folderName = path11.basename(featurePath);
1741
1880
  const match = folderName.match(/^(F\d+)-(.+)$/);
1742
1881
  const id = match?.[1];
1743
1882
  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");
1883
+ const specPath = path11.join(featurePath, "spec.md");
1884
+ const planPath = path11.join(featurePath, "plan.md");
1885
+ const tasksPath = path11.join(featurePath, "tasks.md");
1747
1886
  let specStatus;
1748
1887
  let issueNumber;
1749
1888
  const specExists = await fs8.pathExists(specPath);
@@ -1765,6 +1904,8 @@ async function parseFeature(featurePath, type, context, options) {
1765
1904
  const tasksSummary = { total: 0, todo: 0, doing: 0, done: 0 };
1766
1905
  let activeTask;
1767
1906
  let nextTodoTask;
1907
+ let tasksDocStatus;
1908
+ let tasksDocStatusFieldExists = false;
1768
1909
  let completionChecklist;
1769
1910
  let prLink;
1770
1911
  let prStatus;
@@ -1784,11 +1925,14 @@ async function parseFeature(featurePath, type, context, options) {
1784
1925
  const issueValue = extractFirstSpecValue(content, ["\uC774\uC288 \uBC88\uD638", "Issue Number", "Issue"]);
1785
1926
  issueNumber = parseIssueNumber(issueValue);
1786
1927
  }
1928
+ const tasksDocStatusValue = extractFirstSpecValue(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
1929
+ tasksDocStatusFieldExists = hasAnySpecKey(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
1930
+ tasksDocStatus = parseDocStatus(tasksDocStatusValue);
1787
1931
  const prValue = extractFirstSpecValue(content, ["PR", "Pull Request"]);
1788
- prFieldExists = prValue !== void 0;
1932
+ prFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
1789
1933
  prLink = parsePrLink(prValue);
1790
1934
  const prStatusValue = extractFirstSpecValue(content, ["PR \uC0C1\uD0DC", "PR Status"]);
1791
- prStatusFieldExists = prStatusValue !== void 0;
1935
+ prStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
1792
1936
  prStatus = parseDocStatus(prStatusValue);
1793
1937
  }
1794
1938
  const warnings = [];
@@ -1801,7 +1945,7 @@ async function parseFeature(featurePath, type, context, options) {
1801
1945
  slug,
1802
1946
  folderName
1803
1947
  );
1804
- const relativeFeaturePathFromDocs = path4.relative(context.docsDir, featurePath);
1948
+ const relativeFeaturePathFromDocs = path11.relative(context.docsDir, featurePath);
1805
1949
  const docsStatus = getGitStatusPorcelain(context.docsGitCwd, [relativeFeaturePathFromDocs]);
1806
1950
  const docsHasUncommittedChanges = docsStatus === void 0 ? true : docsStatus.trim().length > 0;
1807
1951
  const docsLastCommit = getLastCommitForPath(
@@ -1815,10 +1959,14 @@ async function parseFeature(featurePath, type, context, options) {
1815
1959
  if (tasksExists && (!prFieldExists || !prStatusFieldExists)) {
1816
1960
  warnings.push(tr(lang, "warnings", "legacyTasksPrFields"));
1817
1961
  }
1962
+ if (tasksExists && !tasksDocStatusFieldExists) {
1963
+ warnings.push(tr(lang, "warnings", "legacyTasksDocStatusField"));
1964
+ }
1818
1965
  if (docsEverCommitted && docsHasUncommittedChanges) {
1819
1966
  warnings.push(tr(lang, "warnings", "docsUncommittedChanges"));
1820
1967
  }
1821
- const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist });
1968
+ const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
1969
+ const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
1822
1970
  const workflowDone = implementationDone && specStatus === "Approved" && planStatus === "Approved" && isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink && prStatus === "Approved";
1823
1971
  if (implementationDone && !workflowDone) {
1824
1972
  if (specStatus !== "Approved") {
@@ -1848,6 +1996,7 @@ async function parseFeature(featurePath, type, context, options) {
1848
1996
  issueNumber,
1849
1997
  specStatus,
1850
1998
  planStatus,
1999
+ tasksDocStatus,
1851
2000
  tasks: tasksSummary,
1852
2001
  activeTask,
1853
2002
  nextTodoTask,
@@ -1868,6 +2017,7 @@ async function parseFeature(featurePath, type, context, options) {
1868
2017
  specExists,
1869
2018
  planExists,
1870
2019
  tasksExists,
2020
+ tasksDocStatusFieldExists,
1871
2021
  prFieldExists,
1872
2022
  prStatusFieldExists
1873
2023
  }
@@ -1875,7 +2025,8 @@ async function parseFeature(featurePath, type, context, options) {
1875
2025
  const { currentStep, actions, nextAction } = resolveFeatureProgress(
1876
2026
  featureState,
1877
2027
  options.stepDefinitions,
1878
- lang
2028
+ lang,
2029
+ options.approval
1879
2030
  );
1880
2031
  return { ...featureState, currentStep, actions, nextAction, warnings };
1881
2032
  }
@@ -1924,7 +2075,7 @@ async function scanFeatures(config) {
1924
2075
  docsDir: config.docsDir,
1925
2076
  projectBranchAvailable: Boolean(singleProject?.cwd)
1926
2077
  },
1927
- { lang: config.lang, stepDefinitions }
2078
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1928
2079
  )
1929
2080
  );
1930
2081
  }
@@ -1946,7 +2097,7 @@ async function scanFeatures(config) {
1946
2097
  docsDir: config.docsDir,
1947
2098
  projectBranchAvailable: Boolean(feProject?.cwd)
1948
2099
  },
1949
- { lang: config.lang, stepDefinitions }
2100
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1950
2101
  )
1951
2102
  );
1952
2103
  }
@@ -1965,7 +2116,7 @@ async function scanFeatures(config) {
1965
2116
  docsDir: config.docsDir,
1966
2117
  projectBranchAvailable: Boolean(beProject?.cwd)
1967
2118
  },
1968
- { lang: config.lang, stepDefinitions }
2119
+ { lang: config.lang, stepDefinitions, approval: config.approval }
1969
2120
  )
1970
2121
  );
1971
2122
  }
@@ -2005,7 +2156,7 @@ async function runStatus(options) {
2005
2156
  process.exit(1);
2006
2157
  }
2007
2158
  const { docsDir, projectType, projectName, lang } = config;
2008
- const featuresDir = path4.join(docsDir, "features");
2159
+ const featuresDir = path11.join(docsDir, "features");
2009
2160
  const scan = await scanFeatures(config);
2010
2161
  const features = [];
2011
2162
  const idMap = /* @__PURE__ */ new Map();
@@ -2018,7 +2169,7 @@ async function runStatus(options) {
2018
2169
  ""
2019
2170
  ) : projectName ?? "{{projectName}}";
2020
2171
  const issue = f.issueNumber ? `#${f.issueNumber}` : "-";
2021
- const relPath = path4.relative(docsDir, f.path);
2172
+ const relPath = path11.relative(docsDir, f.path);
2022
2173
  if (!idMap.has(id)) idMap.set(id, []);
2023
2174
  idMap.get(id).push(relPath);
2024
2175
  const total = f.tasks.total;
@@ -2083,7 +2234,7 @@ async function runStatus(options) {
2083
2234
  }
2084
2235
  console.log();
2085
2236
  if (options.write) {
2086
- const outputPath = path4.join(featuresDir, "status.md");
2237
+ const outputPath = path11.join(featuresDir, "status.md");
2087
2238
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2088
2239
  const content = [
2089
2240
  "# Feature Status",
@@ -2111,7 +2262,7 @@ function escapeRegExp2(value) {
2111
2262
  }
2112
2263
  async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
2113
2264
  try {
2114
- const specPath = path4.join(featureDir, "spec.md");
2265
+ const specPath = path11.join(featureDir, "spec.md");
2115
2266
  if (!await fs8.pathExists(specPath)) return fallbackSlug;
2116
2267
  const content = await fs8.readFile(specPath, "utf-8");
2117
2268
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
@@ -2169,7 +2320,7 @@ async function runUpdate(options) {
2169
2320
  }
2170
2321
  const { docsDir, projectType, lang } = config;
2171
2322
  const templatesDir = getTemplatesDir();
2172
- const sourceDir = path4.join(templatesDir, lang, projectType);
2323
+ const sourceDir = path11.join(templatesDir, lang, projectType);
2173
2324
  const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang);
2174
2325
  const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
2175
2326
  const updateAgents = options.agents || options.skills || !hasExplicitSelection;
@@ -2188,12 +2339,12 @@ async function runUpdate(options) {
2188
2339
  agentsMode === "skills" ? tr(lang, "cli", "update.updatingSkills") : tr(lang, "cli", "update.updatingAgents")
2189
2340
  )
2190
2341
  );
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;
2342
+ const commonAgentsBase = path11.join(templatesDir, lang, "common", "agents");
2343
+ const typeAgentsBase = path11.join(templatesDir, lang, projectType, "agents");
2344
+ const targetAgentsBase = path11.join(docsDir, "agents");
2345
+ const commonAgents = agentsMode === "skills" ? path11.join(commonAgentsBase, "skills") : commonAgentsBase;
2346
+ const typeAgents = agentsMode === "skills" ? path11.join(typeAgentsBase, "skills") : typeAgentsBase;
2347
+ const targetAgents = agentsMode === "skills" ? path11.join(targetAgentsBase, "skills") : targetAgentsBase;
2197
2348
  const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
2198
2349
  const projectName = config.projectName ?? "{{projectName}}";
2199
2350
  const commonReplacements = {
@@ -2231,8 +2382,8 @@ async function runUpdate(options) {
2231
2382
  }
2232
2383
  if (updateTemplates) {
2233
2384
  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");
2385
+ const sourceFeatureBase = path11.join(sourceDir, "features", "feature-base");
2386
+ const targetFeatureBase = path11.join(docsDir, "features", "feature-base");
2236
2387
  if (await fs8.pathExists(sourceFeatureBase)) {
2237
2388
  const replacements = {
2238
2389
  "{{projectName}}": config.projectName ?? "{{projectName}}"
@@ -2265,8 +2416,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
2265
2416
  const files = await fs8.readdir(sourceDir);
2266
2417
  let updatedCount = 0;
2267
2418
  for (const file of files) {
2268
- const sourcePath = path4.join(sourceDir, file);
2269
- const targetPath = path4.join(targetDir, file);
2419
+ const sourcePath = path11.join(sourceDir, file);
2420
+ const targetPath = path11.join(targetDir, file);
2270
2421
  const stat = await fs8.stat(sourcePath);
2271
2422
  if (stat.isFile()) {
2272
2423
  if (protectedFiles.has(file)) {
@@ -2326,7 +2477,7 @@ function getGitTopLevel2(cwd) {
2326
2477
  function getDocsPorcelainStatus(docsDir) {
2327
2478
  const top = getGitTopLevel2(docsDir);
2328
2479
  if (!top) return null;
2329
- const rel = path4.relative(top, docsDir) || ".";
2480
+ const rel = path11.relative(top, docsDir) || ".";
2330
2481
  try {
2331
2482
  return execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
2332
2483
  cwd: top,
@@ -2375,7 +2526,7 @@ async function runConfig(options) {
2375
2526
  );
2376
2527
  process.exit(1);
2377
2528
  }
2378
- const configPath = path4.join(config.docsDir, ".lee-spec-kit.json");
2529
+ const configPath = path11.join(config.docsDir, ".lee-spec-kit.json");
2379
2530
  if (!options.projectRoot) {
2380
2531
  console.log();
2381
2532
  console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -2608,6 +2759,17 @@ async function runContext(featureName, options) {
2608
2759
  inProgressCandidates: selectionMode === "open" ? inProgressFeatures : [],
2609
2760
  readyToCloseCandidates: selectionMode === "open" ? readyToCloseFeatures : [],
2610
2761
  actions: targetFeatures.length === 1 ? targetFeatures[0].actions : [],
2762
+ checkPolicy: {
2763
+ docPath: "/docs/agents/agents.md",
2764
+ hint: tr(lang, "cli", "context.checkPolicyHint"),
2765
+ token: "OK",
2766
+ config: config.approval ?? { mode: "builtin" }
2767
+ },
2768
+ prPolicy: {
2769
+ screenshots: {
2770
+ upload: config.pr?.screenshots?.upload ?? false
2771
+ }
2772
+ },
2611
2773
  recommendation: ""
2612
2774
  };
2613
2775
  if (result.status === "multiple_active") {
@@ -2623,7 +2785,7 @@ async function runContext(featureName, options) {
2623
2785
  return;
2624
2786
  }
2625
2787
  console.log();
2626
- console.log(chalk6.bold("\u{1F4CD} Current Context Check"));
2788
+ console.log(chalk6.bold(tr(lang, "cli", "context.header")));
2627
2789
  if (config.projectType === "single") {
2628
2790
  if (branches.project.single) {
2629
2791
  console.log(
@@ -2738,7 +2900,8 @@ async function runContext(featureName, options) {
2738
2900
  }
2739
2901
  const f = targetFeatures[0];
2740
2902
  const stepName = stepsMap[f.currentStep] || "Unknown";
2741
- const okTag = (requiresUserOk) => requiresUserOk ? chalk6.yellow(tr(lang, "cli", "context.okRequired")) : "";
2903
+ const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk6.yellow(tr(lang, "cli", "context.checkRequired")) : "";
2904
+ const hasCheckAction = (f.actions || []).some((a) => !!a.requiresUserCheck);
2742
2905
  console.log(
2743
2906
  `\u{1F539} Feature: ${chalk6.bold(f.folderName)} ${config.projectType === "fullstack" ? chalk6.cyan(`(${f.type})`) : ""}`
2744
2907
  );
@@ -2748,7 +2911,7 @@ async function runContext(featureName, options) {
2748
2911
  if (f.issueNumber) {
2749
2912
  console.log(` \u2022 Issue: #${f.issueNumber}`);
2750
2913
  }
2751
- console.log(` \u2022 Path: ${path4.relative(cwd, f.path)}`);
2914
+ console.log(` \u2022 Path: ${path11.relative(cwd, f.path)}`);
2752
2915
  if (f.git.projectBranch) {
2753
2916
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
2754
2917
  }
@@ -2773,6 +2936,9 @@ async function runContext(featureName, options) {
2773
2936
  }
2774
2937
  console.log();
2775
2938
  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"));
2939
+ if (hasCheckAction) {
2940
+ console.log(chalk6.gray(tr(lang, "cli", "context.checkPolicyHint")));
2941
+ }
2776
2942
  if (!f.actions || f.actions.length === 0) {
2777
2943
  console.log(`\u{1F449} Next Action: ${chalk6.green(chalk6.bold(f.nextAction))}`);
2778
2944
  console.log();
@@ -2782,24 +2948,32 @@ async function runContext(featureName, options) {
2782
2948
  const action = f.actions[0];
2783
2949
  if (action.type === "command") {
2784
2950
  console.log(
2785
- `\u{1F449} Next Action (${chalk6.cyan(action.scope)}): ${okTag(action.requiresUserOk)}${chalk6.green(chalk6.bold(action.cmd))}`
2951
+ `\u{1F449} Next Action (${chalk6.cyan(action.scope)}): ${checkTag(action.requiresUserCheck)}${chalk6.green(chalk6.bold(action.cmd))}`
2786
2952
  );
2953
+ if (action.scope === "docs") {
2954
+ console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
2955
+ }
2787
2956
  } else {
2788
2957
  console.log(
2789
- `\u{1F449} Next Action: ${okTag(action.requiresUserOk)}${chalk6.green(chalk6.bold(action.message))}`
2958
+ `\u{1F449} Next Action: ${checkTag(action.requiresUserCheck)}${chalk6.green(chalk6.bold(action.message))}`
2790
2959
  );
2791
2960
  }
2792
2961
  console.log();
2793
2962
  return;
2794
2963
  }
2795
2964
  console.log(chalk6.green(chalk6.bold("\u{1F449} Next Actions:")));
2965
+ let hasDocsCommand = false;
2796
2966
  f.actions.forEach((action) => {
2797
2967
  if (action.type === "command") {
2798
- console.log(` \u2022 (${action.scope}) ${okTag(action.requiresUserOk)}${action.cmd}`);
2968
+ console.log(` \u2022 (${action.scope}) ${checkTag(action.requiresUserCheck)}${action.cmd}`);
2969
+ if (action.scope === "docs") hasDocsCommand = true;
2799
2970
  } else {
2800
- console.log(` \u2022 ${okTag(action.requiresUserOk)}${action.message}`);
2971
+ console.log(` \u2022 ${checkTag(action.requiresUserCheck)}${action.message}`);
2801
2972
  }
2802
2973
  });
2974
+ if (hasDocsCommand) {
2975
+ console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
2976
+ }
2803
2977
  console.log();
2804
2978
  }
2805
2979
  function printChecklist(f, stepDefinitions) {
@@ -2814,7 +2988,7 @@ function printChecklist(f, stepDefinitions) {
2814
2988
  }
2815
2989
  function formatPath(cwd, p) {
2816
2990
  if (!p) return "";
2817
- return path4.isAbsolute(p) ? path4.relative(cwd, p) : p;
2991
+ return path11.isAbsolute(p) ? path11.relative(cwd, p) : p;
2818
2992
  }
2819
2993
  function detectPlaceholders(content) {
2820
2994
  const patterns = [
@@ -2841,7 +3015,7 @@ async function checkDocsStructure(config, cwd) {
2841
3015
  const issues = [];
2842
3016
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
2843
3017
  for (const dir of requiredDirs) {
2844
- const p = path4.join(config.docsDir, dir);
3018
+ const p = path11.join(config.docsDir, dir);
2845
3019
  if (!await fs8.pathExists(p)) {
2846
3020
  issues.push({
2847
3021
  level: "error",
@@ -2851,7 +3025,7 @@ async function checkDocsStructure(config, cwd) {
2851
3025
  });
2852
3026
  }
2853
3027
  }
2854
- const configPath = path4.join(config.docsDir, ".lee-spec-kit.json");
3028
+ const configPath = path11.join(config.docsDir, ".lee-spec-kit.json");
2855
3029
  if (!await fs8.pathExists(configPath)) {
2856
3030
  issues.push({
2857
3031
  level: "warn",
@@ -2874,13 +3048,13 @@ async function checkFeatures(config, cwd, features) {
2874
3048
  }
2875
3049
  const idMap = /* @__PURE__ */ new Map();
2876
3050
  for (const f of features) {
2877
- const rel = f.docs.featurePathFromDocs || path4.relative(config.docsDir, f.path);
3051
+ const rel = f.docs.featurePathFromDocs || path11.relative(config.docsDir, f.path);
2878
3052
  const id = f.id || "UNKNOWN";
2879
3053
  if (!idMap.has(id)) idMap.set(id, []);
2880
3054
  idMap.get(id).push(rel);
2881
3055
  const featureDocs = ["spec.md", "plan.md", "tasks.md", "decisions.md"];
2882
3056
  for (const file of featureDocs) {
2883
- const p = path4.join(f.path, file);
3057
+ const p = path11.join(f.path, file);
2884
3058
  if (!await fs8.pathExists(p)) continue;
2885
3059
  const content = await fs8.readFile(p, "utf-8");
2886
3060
  const placeholders = detectPlaceholders(content);
@@ -2906,7 +3080,7 @@ async function checkFeatures(config, cwd, features) {
2906
3080
  level: "warn",
2907
3081
  code: "spec_status_unset",
2908
3082
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
2909
- path: formatPath(cwd, path4.join(f.path, "spec.md"))
3083
+ path: formatPath(cwd, path11.join(f.path, "spec.md"))
2910
3084
  });
2911
3085
  }
2912
3086
  if (f.docs.planExists && !f.planStatus) {
@@ -2914,7 +3088,7 @@ async function checkFeatures(config, cwd, features) {
2914
3088
  level: "warn",
2915
3089
  code: "plan_status_unset",
2916
3090
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
2917
- path: formatPath(cwd, path4.join(f.path, "plan.md"))
3091
+ path: formatPath(cwd, path11.join(f.path, "plan.md"))
2918
3092
  });
2919
3093
  }
2920
3094
  if (f.docs.tasksExists && f.tasks.total === 0) {
@@ -2922,7 +3096,23 @@ async function checkFeatures(config, cwd, features) {
2922
3096
  level: "warn",
2923
3097
  code: "tasks_empty",
2924
3098
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
2925
- path: formatPath(cwd, path4.join(f.path, "tasks.md"))
3099
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
3100
+ });
3101
+ }
3102
+ if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists) {
3103
+ issues.push({
3104
+ level: "warn",
3105
+ code: "tasks_doc_status_missing",
3106
+ message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
3107
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
3108
+ });
3109
+ }
3110
+ if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus) {
3111
+ issues.push({
3112
+ level: "warn",
3113
+ code: "tasks_doc_status_unset",
3114
+ message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
3115
+ path: formatPath(cwd, path11.join(f.path, "tasks.md"))
2926
3116
  });
2927
3117
  }
2928
3118
  }
@@ -2946,7 +3136,7 @@ async function checkFeatures(config, cwd, features) {
2946
3136
  level: "warn",
2947
3137
  code: "missing_feature_id",
2948
3138
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
2949
- path: formatPath(cwd, path4.join(config.docsDir, p))
3139
+ path: formatPath(cwd, path11.join(config.docsDir, p))
2950
3140
  });
2951
3141
  }
2952
3142
  return issues;
@@ -2996,7 +3186,7 @@ function doctorCommand(program2) {
2996
3186
  }
2997
3187
  console.log();
2998
3188
  console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
2999
- console.log(chalk6.gray(`- Docs: ${path4.relative(cwd, docsDir)}`));
3189
+ console.log(chalk6.gray(`- Docs: ${path11.relative(cwd, docsDir)}`));
3000
3190
  console.log(chalk6.gray(`- Type: ${projectType}`));
3001
3191
  console.log(chalk6.gray(`- Lang: ${lang}`));
3002
3192
  console.log();
@@ -3090,11 +3280,11 @@ ${version}
3090
3280
  }
3091
3281
  return `${ascii}${footer}`;
3092
3282
  }
3093
- var CACHE_FILE = path4.join(os.homedir(), ".lee-spec-kit-version-cache.json");
3283
+ var CACHE_FILE = path11.join(os.homedir(), ".lee-spec-kit-version-cache.json");
3094
3284
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
3095
3285
  function getCurrentVersion() {
3096
3286
  try {
3097
- const packageJsonPath = path4.join(__dirname$1, "..", "package.json");
3287
+ const packageJsonPath = path11.join(__dirname$1, "..", "package.json");
3098
3288
  if (fs8.existsSync(packageJsonPath)) {
3099
3289
  const pkg = fs8.readJsonSync(packageJsonPath);
3100
3290
  return pkg.version;
@@ -3184,7 +3374,7 @@ function shouldCheckForUpdates() {
3184
3374
  if (shouldCheckForUpdates()) checkForUpdates();
3185
3375
  function getCliVersion() {
3186
3376
  try {
3187
- const packageJsonPath = path4.join(__dirname$1, "..", "package.json");
3377
+ const packageJsonPath = path11.join(__dirname$1, "..", "package.json");
3188
3378
  if (fs8.existsSync(packageJsonPath)) {
3189
3379
  const pkg = fs8.readJsonSync(packageJsonPath);
3190
3380
  if (pkg?.version) return String(pkg.version);