lee-spec-kit 0.6.17 → 0.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import path19 from 'path';
2
+ import path20 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { program } from 'commander';
5
5
  import fs15 from 'fs-extra';
@@ -11,7 +11,7 @@ import os from 'os';
11
11
  import { createHash, randomUUID } from 'crypto';
12
12
 
13
13
  var getFilename = () => fileURLToPath(import.meta.url);
14
- var getDirname = () => path19.dirname(getFilename());
14
+ var getDirname = () => path20.dirname(getFilename());
15
15
  var __dirname$1 = /* @__PURE__ */ getDirname();
16
16
  async function copyTemplates(src, dest) {
17
17
  await fs15.copy(src, dest, {
@@ -42,10 +42,10 @@ async function replaceInFiles(dir, replacements) {
42
42
  }
43
43
  }
44
44
  var __filename2 = fileURLToPath(import.meta.url);
45
- var __dirname2 = path19.dirname(__filename2);
45
+ var __dirname2 = path20.dirname(__filename2);
46
46
  function getTemplatesDir() {
47
- const rootDir = path19.resolve(__dirname2, "..");
48
- return path19.join(rootDir, "templates");
47
+ const rootDir = path20.resolve(__dirname2, "..");
48
+ return path20.join(rootDir, "templates");
49
49
  }
50
50
 
51
51
  // src/utils/i18n.ts
@@ -276,7 +276,7 @@ var I18N = {
276
276
  "github.operationPrMerge": "GitHub PR merge",
277
277
  "github.createIssueFailed": "GitHub issue \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
278
278
  "github.createPrFailed": "GitHub PR \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
279
- "github.mergeRequiresPr": "`--merge`\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 `--create` \uB610\uB294 `--pr <url|number>`\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
279
+ "github.mergeRequiresPr": "`--merge`\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 `--create`, `--pr <url|number>`, \uB610\uB294 tasks.md\uC758 PR \uB9C1\uD06C\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
280
280
  "github.checkoutBaseAfterMergeFailed": "merge \uD6C4 {base} \uBE0C\uB79C\uCE58 checkout\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
281
281
  "github.pullBaseAfterMergeFailed": "merge \uD6C4 {base} \uBE0C\uB79C\uCE58 \uCD5C\uC2E0\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
282
282
  "github.issueDefaultTitle": "{slug} ({summary})",
@@ -433,7 +433,7 @@ var I18N = {
433
433
  reviewFixCommitIssueGuidance: 'PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D\uC744 \uC694\uC57D\uD574\uC57C \uD558\uBA70, \uD0DC\uC2A4\uD06C \uC81C\uBAA9\uC744 \uC7AC\uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694. \uC608: `cd "{projectGitCwd}" && (git diff --cached --quiet && echo "\uC2A4\uD14C\uC774\uC9D5\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9AC\uBDF0 \uC218\uC815 \uBC18\uC601 \uD30C\uC77C\uB9CC git add [files] \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694." && exit 1 || git commit -m "fix(#{issueNumber}): <review-fix-summary>")` (`<review-fix-summary>`\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9\uC73C\uB85C \uC9C1\uC811 \uC791\uC131)',
434
434
  reviewFixCommitGuidance: 'PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D\uC744 \uC694\uC57D\uD574\uC57C \uD558\uBA70, \uD0DC\uC2A4\uD06C \uC81C\uBAA9\uC744 \uC7AC\uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694. \uC608: `cd "{projectGitCwd}" && (git diff --cached --quiet && echo "\uC2A4\uD14C\uC774\uC9D5\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9AC\uBDF0 \uC218\uC815 \uBC18\uC601 \uD30C\uC77C\uB9CC git add [files] \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694." && exit 1 || git commit -m "fix(review): <review-fix-summary>")` (`<review-fix-summary>`\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9\uC73C\uB85C \uC9C1\uC811 \uC791\uC131)',
435
435
  standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
436
- createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
436
+ createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
437
437
  tasksAllDoneButNoChecklist: '\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uB97C \uC791\uC131\uD558\uC138\uC694. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC5D0 \uAC80\uC99D \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0, \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uC138\uC694. \uCD5C\uC885 \uC2B9\uC778(OK)\uB3C4 \uBC18\uC601\uD558\uC138\uC694.',
438
438
  tasksAllDoneButChecklist: "\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uC758 \uB0A8\uC740 \uD56D\uBAA9\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uD604\uC7AC \uC9C4\uD589: ({checked}/{total}) \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uACE0 \uCD5C\uC885 \uC2B9\uC778(OK)\uC744 \uBC18\uC601\uD558\uC138\uC694.",
439
439
  finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD569\uB2C8\uB2E4: "{title}" ({done}/{total}) \uACB0\uACFC/\uAC80\uC99D \uACF5\uC720 + \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DONE \uCC98\uB9AC',
@@ -443,7 +443,7 @@ var I18N = {
443
443
  taskCommitGateWarnProceed: "\u26A0\uFE0F \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704 \uC810\uAC80 \uACBD\uACE0: {reason}. \uD604\uC7AC\uB294 \uC9C4\uD589 \uAC00\uB2A5\uD558\uC9C0\uB9CC `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B`\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
444
444
  taskCommitGateReasonNoTasksCommit: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
445
445
  taskCommitGateReasonTasksFileUnavailable: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uC774\uB825\uC744 \uD310\uB3C5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
446
- taskCommitGateReasonDoneCount: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uC810\uAC80 \uACB0\uACFC\uAC00 \uC608\uC0C1\uACFC \uB2E4\uB985\uB2C8\uB2E4 ({count})",
446
+ taskCommitGateReasonDoneCount: "\uCD5C\uC2E0 tasks.md \uCEE4\uBC0B\uC5D0\uC11C DONE \uC804\uD658\uC774 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
447
447
  taskCommitGateReasonMismatchLastDone: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B\uC774 \uC9C1\uC804 \uC644\uB8CC \uD0DC\uC2A4\uD06C\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
448
448
  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)",
449
449
  prePrReviewFieldMissing: "tasks.md\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uC804 \uB9AC\uBDF0**: Pending | Done` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
@@ -467,16 +467,21 @@ var I18N = {
467
467
  prCreateExecute: "\uD655\uC815\uB41C PR \uBCF8\uBB38\uC73C\uB85C PR\uC744 \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C PR \uB9C1\uD06C\uB97C tasks.md\uC758 PR \uD544\uB4DC\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.",
468
468
  prCreateRequiredSequence: "PR \uC0DD\uC131\uC740 \uD544\uC218 2\uB2E8\uACC4\uC785\uB2C8\uB2E4: (1) PR \uBCF8\uBB38 \uD15C\uD50C\uB9BF \uC0DD\uC131/\uBCF4\uC644 + \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK), (2) PR \uC0DD\uC131 + tasks.md PR \uB9C1\uD06C \uAE30\uB85D. \uC704 \uC21C\uC11C\uB97C \uBAA8\uB450 \uC644\uB8CC\uD558\uC138\uC694.",
469
469
  prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694. (PR \uC0DD\uC131/\uB9AC\uBDF0 \uB2E8\uACC4\uC5D0\uC11C\uB294 Review\uB97C \uC720\uC9C0\uD569\uB2C8\uB2E4.)",
470
+ prReviewMergedSyncStatus: "\uC6D0\uACA9 PR\uC774 \uC774\uBBF8 \uBA38\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. (PR/\uB9AC\uBDF0 \uADFC\uAC70 \uD544\uB4DC\uB3C4 \uCD5C\uC2E0 \uC0C1\uD0DC\uB85C \uD655\uC778)",
470
471
  prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uB294 \uB3D9\uC548 PR \uC0C1\uD0DC\uB294 Review\uB85C \uC720\uC9C0\uD558\uC138\uC694. \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD0DC\uC2A4\uD06C\uBA85\uC774 \uC544\uB2C8\uB77C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC694\uC57D\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694. \uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uC131\uACF5 \uC2DC PR \uC0C1\uD0DC\uAC00 Approved\uB85C \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.)",
471
472
  prReviewResolve: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uBA3C\uC800 \uD655\uC778/\uBD84\uC11D\uD55C \uB4A4 \uD544\uC694\uD55C \uC218\uC815 \uC791\uC5C5\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uC9C4\uD589 \uC911\uC5D0\uB294 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC720\uC9C0\uD558\uC138\uC694. \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD0DC\uC2A4\uD06C\uBA85\uC774 \uC544\uB2C8\uB77C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC694\uC57D\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694. `PR \uB9AC\uBDF0 Findings/Evidence`\uB97C \uCD5C\uC2E0\uC73C\uB85C \uAE30\uB85D\uD558\uACE0, \uCEE4\uBC0B \uD6C4 \uC6D0\uACA9 \uBC18\uC601(push)\uB3C4 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK)\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589\uD558\uC138\uC694.",
472
- prReviewPush: '\uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC6D0\uACA9 PR \uBE0C\uB79C\uCE58\uC5D0 \uBC18\uC601\uD558\uB824\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `cd "{projectGitCwd}" && git push`\uB97C \uC2E4\uD589\uD558\uC138\uC694.',
473
+ prReviewPush: 'cd "{projectGitCwd}" && git push',
473
474
  prReviewRemoteBlocked: "\uC6D0\uACA9 PR \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uACB0\uACFC \uC544\uC9C1 \uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: {reasons}. \uB9AC\uBDF0 \uCF54\uBA58\uD2B8/\uCCB4\uD06C \uC0C1\uD0DC\uB97C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
474
475
  prReviewRemoteReasonChangesRequested: "\uB9AC\uBDF0 \uC2B9\uC778 \uC0C1\uD0DC\uAC00 \uBCC0\uACBD \uC694\uCCAD \uB610\uB294 \uCD94\uAC00 \uB9AC\uBDF0 \uD544\uC694 \uC0C1\uD0DC\uC785\uB2C8\uB2E4",
476
+ prReviewRemoteReasonClosed: "PR\uC774 \uBA38\uC9C0\uB418\uC9C0 \uC54A\uC740 \uCC44 \uB2EB\uD600 \uC788\uC2B5\uB2C8\uB2E4 (reopen \uB610\uB294 \uC0C8 PR \uD544\uC694)",
475
477
  prReviewRemoteReasonChecksFailing: "\uC2E4\uD328\uD55C \uCCB4\uD06C\uAC00 {count}\uAC74 \uC788\uC2B5\uB2C8\uB2E4",
476
478
  prReviewRemoteReasonChecksPending: "\uB300\uAE30 \uC911\uC778 \uCCB4\uD06C\uAC00 {count}\uAC74 \uC788\uC2B5\uB2C8\uB2E4",
477
479
  prReviewRemoteReasonMergeBlocked: "\uBA38\uC9C0 \uC0C1\uD0DC\uAC00 `{status}`\uB85C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4",
480
+ prReviewRemoteReasonUnavailable: "\uC6D0\uACA9 PR \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (gh \uC778\uC99D/\uB124\uD2B8\uC6CC\uD06C/\uAD8C\uD55C \uD655\uC778 \uD544\uC694)",
478
481
  prReviewMerge: "\uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uC131\uACF5 \uC2DC PR \uC0C1\uD0DC\uAC00 Approved\uB85C \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.)",
482
+ prReviewMergeCommand: "npx lee-spec-kit github pr {featureRef} --merge --confirm OK",
479
483
  prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815/\uC720\uC9C0\uD558\uC138\uC694.",
484
+ userRequestReplan: "\uD604\uC7AC \uB2E8\uACC4\uC640 \uBCC4\uAC1C\uB85C \uC0AC\uC6A9\uC790\uAC00 \uC81C\uC548\uD55C \uC0C8 \uC694\uAD6C\uB97C \uBA3C\uC800 \uBC18\uC601\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC694\uAD6C\uC0AC\uD56D\uC744 \uC694\uC57D\uD574 tasks.md\uC5D0 \uCD94\uAC00\uD558\uAC70\uB098 \uBCC4\uB3C4 Feature\uB85C \uBD84\uB9AC\uD55C \uB4A4, \uBB38\uC11C \uC0C1\uD0DC\uB97C \uB9DE\uCD94\uACE0 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
480
485
  featureDone: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC694\uAD6C\uC0AC\uD56D\uACFC \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.",
481
486
  fallbackRerunContext: "\uC0C1\uD0DC\uB97C \uD310\uBCC4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694."
482
487
  },
@@ -728,7 +733,7 @@ var I18N = {
728
733
  "github.operationPrMerge": "GitHub PR merge",
729
734
  "github.createIssueFailed": "Failed to create GitHub issue",
730
735
  "github.createPrFailed": "Failed to create GitHub PR",
731
- "github.mergeRequiresPr": "`--merge` requires `--create` or `--pr <url|number>`.",
736
+ "github.mergeRequiresPr": "`--merge` requires `--create`, `--pr <url|number>`, or a PR link in tasks.md.",
732
737
  "github.checkoutBaseAfterMergeFailed": "Failed to checkout {base} after merge",
733
738
  "github.pullBaseAfterMergeFailed": "Failed to update {base} after merge",
734
739
  "github.issueDefaultTitle": "{slug} ({summary})",
@@ -885,7 +890,7 @@ var I18N = {
885
890
  reviewFixCommitIssueGuidance: 'Commit PR review fixes. The commit message must summarize review comments resolved in this commit, not reuse a task title. Example: `cd "{projectGitCwd}" && (git diff --cached --quiet && echo "No staged files. Stage only files that implement review fixes with git add [files], then run again." && exit 1 || git commit -m "fix(#{issueNumber}): <review-fix-summary>")` (replace `<review-fix-summary>` with what this commit actually resolves).',
886
891
  reviewFixCommitGuidance: 'Commit PR review fixes. The commit message must summarize review comments resolved in this commit, not reuse a task title. Example: `cd "{projectGitCwd}" && (git diff --cached --quiet && echo "No staged files. Stage only files that implement review fixes with git add [files], then run again." && exit 1 || git commit -m "fix(review): <review-fix-summary>")` (replace `<review-fix-summary>` with what this commit actually resolves).',
887
892
  standaloneNeedsProjectRoot: "Standalone mode requires projectRoot. (npx lee-spec-kit config --project-root ...)",
888
- createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
893
+ createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
889
894
  tasksAllDoneButNoChecklist: 'Create the completion checklist. Add verification items to the tasks.md "Completion Criteria" section, then mark satisfied items as [x] after user confirmation. Record final approval (OK) as well.',
890
895
  tasksAllDoneButChecklist: "Proceed with remaining completion checklist items. Current progress: ({checked}/{total}) Mark items as [x] only after user confirmation and real verification. Record final approval (OK) as well.",
891
896
  finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) Share outcome/verification + get OK before marking DONE',
@@ -895,7 +900,7 @@ var I18N = {
895
900
  taskCommitGateWarnProceed: "\u26A0\uFE0F Task commit boundary warning: {reason}. You may continue, but `1 task = 1 commit` is recommended.",
896
901
  taskCommitGateReasonNoTasksCommit: "No recent project code commit was found",
897
902
  taskCommitGateReasonTasksFileUnavailable: "Cannot read recent project code commit history",
898
- taskCommitGateReasonDoneCount: "Project commit gate check result is unexpected ({count})",
903
+ taskCommitGateReasonDoneCount: "DONE transitions detected in the latest tasks.md commit ({count})",
899
904
  taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
900
905
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
901
906
  prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
@@ -919,16 +924,21 @@ var I18N = {
919
924
  prCreateExecute: "Create the PR with the finalized body, then record the created PR link in tasks.md.",
920
925
  prCreateRequiredSequence: "PR creation is a required 2-step sequence: (1) generate/refine PR body template + explicit user OK, (2) create PR + record PR link in tasks.md. Complete both in order.",
921
926
  prFillStatus: "Set PR Status in tasks.md to Review. (Keep Review during PR creation/review stages.)",
927
+ prReviewMergedSyncStatus: "The remote PR is already merged. Update PR Status in tasks.md to Approved. (Also verify PR/review evidence fields are up to date.)",
922
928
  prResolveReview: "Keep PR Status as Review while addressing comments. For review-fix commits, use commit messages that summarize resolved review feedback (not task titles). Once ready to merge, get explicit user OK and run `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`. (On success, PR Status is synced to Approved.)",
923
929
  prReviewResolve: "First review/analyze the PR comments, then apply the required fixes. Keep PR Status as Review while addressing comments. For review-fix commits, use commit messages that summarize resolved review feedback (not task titles). Keep `PR Review Findings/Evidence` updated, then run push only after explicit user OK.",
924
- prReviewPush: 'To reflect review-fix commits on the PR head branch, get explicit user OK and run `cd "{projectGitCwd}" && git push`.',
930
+ prReviewPush: 'cd "{projectGitCwd}" && git push',
925
931
  prReviewRemoteBlocked: "Remote PR checks indicate this PR is not ready to merge yet: {reasons}. Resolve review comments/check statuses, then re-check.",
926
932
  prReviewRemoteReasonChangesRequested: "review decision is changes requested or additional review required",
933
+ prReviewRemoteReasonClosed: "PR is closed without merge (reopen or create a new PR)",
927
934
  prReviewRemoteReasonChecksFailing: "{count} failing check(s) detected",
928
935
  prReviewRemoteReasonChecksPending: "{count} pending check(s) detected",
929
936
  prReviewRemoteReasonMergeBlocked: "merge state is blocked (`{status}`)",
937
+ prReviewRemoteReasonUnavailable: "remote PR status could not be verified (check gh auth/network/permissions)",
930
938
  prReviewMerge: "When ready to merge, get explicit user OK and run `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`. (On success, PR Status is synced to Approved.)",
939
+ prReviewMergeCommand: "npx lee-spec-kit github pr {featureRef} --merge --confirm OK",
931
940
  prRequestReview: "Request review and set/keep PR Status as Review.",
941
+ userRequestReplan: "You can pause this step and handle a newly requested user requirement first. Summarize it, add it to tasks.md or split it into a separate Feature, then align document statuses and rerun context.",
932
942
  featureDone: "Workflow requirements and all tasks/completion criteria are satisfied. This feature is done.",
933
943
  fallbackRerunContext: "Cannot determine status. Check the docs and run context again."
934
944
  },
@@ -1349,10 +1359,10 @@ function sleep(ms) {
1349
1359
  return new Promise((resolve) => globalThis.setTimeout(resolve, ms));
1350
1360
  }
1351
1361
  function toScopeKey(value) {
1352
- return createHash("sha1").update(path19.resolve(value)).digest("hex").slice(0, 16);
1362
+ return createHash("sha1").update(path20.resolve(value)).digest("hex").slice(0, 16);
1353
1363
  }
1354
1364
  function getTempRuntimeDir(scopePath) {
1355
- return path19.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1365
+ return path20.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1356
1366
  }
1357
1367
  function resolveGitRuntimeDir(cwd) {
1358
1368
  try {
@@ -1366,38 +1376,38 @@ function resolveGitRuntimeDir(cwd) {
1366
1376
  }
1367
1377
  ).trim();
1368
1378
  if (!out) return null;
1369
- return path19.isAbsolute(out) ? out : path19.resolve(cwd, out);
1379
+ return path20.isAbsolute(out) ? out : path20.resolve(cwd, out);
1370
1380
  } catch {
1371
1381
  return null;
1372
1382
  }
1373
1383
  }
1374
1384
  function getRuntimeStateDir(cwd) {
1375
- const resolved = path19.resolve(cwd);
1385
+ const resolved = path20.resolve(cwd);
1376
1386
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1377
1387
  }
1378
1388
  function getDocsLockPath(docsDir) {
1379
- return path19.join(
1389
+ return path20.join(
1380
1390
  getRuntimeStateDir(docsDir),
1381
1391
  "locks",
1382
1392
  `docs-${toScopeKey(docsDir)}.lock`
1383
1393
  );
1384
1394
  }
1385
1395
  function getInitLockPath(targetDir) {
1386
- return path19.join(
1387
- getRuntimeStateDir(path19.dirname(path19.resolve(targetDir))),
1396
+ return path20.join(
1397
+ getRuntimeStateDir(path20.dirname(path20.resolve(targetDir))),
1388
1398
  "locks",
1389
1399
  `init-${toScopeKey(targetDir)}.lock`
1390
1400
  );
1391
1401
  }
1392
1402
  function getApprovalTicketStorePath(docsDir) {
1393
- return path19.join(
1403
+ return path20.join(
1394
1404
  getRuntimeStateDir(docsDir),
1395
1405
  "tickets",
1396
1406
  `approval-${toScopeKey(docsDir)}.json`
1397
1407
  );
1398
1408
  }
1399
1409
  function getProjectExecutionLockPath(cwd) {
1400
- return path19.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1410
+ return path20.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1401
1411
  }
1402
1412
  async function isStaleLock(lockPath, staleMs) {
1403
1413
  try {
@@ -1438,7 +1448,7 @@ function isProcessAlive(pid) {
1438
1448
  }
1439
1449
  }
1440
1450
  async function tryAcquire(lockPath, owner) {
1441
- await fs15.ensureDir(path19.dirname(lockPath));
1451
+ await fs15.ensureDir(path20.dirname(lockPath));
1442
1452
  try {
1443
1453
  const fd = await fs15.open(lockPath, "wx");
1444
1454
  const payload = JSON.stringify(
@@ -1515,30 +1525,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
1515
1525
  "pr-template.md"
1516
1526
  ];
1517
1527
  var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
1518
- var ENGINE_MANAGED_FEATURE_PATH = path19.join(
1528
+ var ENGINE_MANAGED_FEATURE_PATH = path20.join(
1519
1529
  "features",
1520
1530
  "feature-base"
1521
1531
  );
1522
1532
  async function pruneEngineManagedDocs(docsDir) {
1523
1533
  const removed = [];
1524
1534
  for (const file of ENGINE_MANAGED_AGENT_FILES) {
1525
- const target = path19.join(docsDir, "agents", file);
1535
+ const target = path20.join(docsDir, "agents", file);
1526
1536
  if (await fs15.pathExists(target)) {
1527
1537
  await fs15.remove(target);
1528
- removed.push(path19.relative(docsDir, target));
1538
+ removed.push(path20.relative(docsDir, target));
1529
1539
  }
1530
1540
  }
1531
1541
  for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
1532
- const target = path19.join(docsDir, "agents", dir);
1542
+ const target = path20.join(docsDir, "agents", dir);
1533
1543
  if (await fs15.pathExists(target)) {
1534
1544
  await fs15.remove(target);
1535
- removed.push(path19.relative(docsDir, target));
1545
+ removed.push(path20.relative(docsDir, target));
1536
1546
  }
1537
1547
  }
1538
- const featureBasePath = path19.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1548
+ const featureBasePath = path20.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1539
1549
  if (await fs15.pathExists(featureBasePath)) {
1540
1550
  await fs15.remove(featureBasePath);
1541
- removed.push(path19.relative(docsDir, featureBasePath));
1551
+ removed.push(path20.relative(docsDir, featureBasePath));
1542
1552
  }
1543
1553
  return removed;
1544
1554
  }
@@ -1679,7 +1689,7 @@ ${tr(lang2, "cli", "common.canceled")}`)
1679
1689
  }
1680
1690
  async function runInit(options) {
1681
1691
  const cwd = process.cwd();
1682
- const defaultName = path19.basename(cwd);
1692
+ const defaultName = path20.basename(cwd);
1683
1693
  let projectName = options.name || defaultName;
1684
1694
  let projectType = options.type;
1685
1695
  let components = parseComponentsOption(options.components);
@@ -1692,7 +1702,7 @@ async function runInit(options) {
1692
1702
  const componentProjectRoots = parseComponentProjectRootsOption(
1693
1703
  options.componentProjectRoots
1694
1704
  );
1695
- const targetDir = path19.resolve(cwd, options.dir || "./docs");
1705
+ const targetDir = path20.resolve(cwd, options.dir || "./docs");
1696
1706
  const skipPrompts = !!options.yes || !!options.nonInteractive;
1697
1707
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
1698
1708
  throw createCliError(
@@ -2081,7 +2091,7 @@ async function runInit(options) {
2081
2091
  );
2082
2092
  console.log();
2083
2093
  const templatesDir = getTemplatesDir();
2084
- const commonPath = path19.join(templatesDir, lang, "common");
2094
+ const commonPath = path20.join(templatesDir, lang, "common");
2085
2095
  if (!await fs15.pathExists(commonPath)) {
2086
2096
  throw new Error(
2087
2097
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
@@ -2089,11 +2099,11 @@ async function runInit(options) {
2089
2099
  }
2090
2100
  await copyTemplates(commonPath, targetDir);
2091
2101
  if (projectType === "multi") {
2092
- const featuresRoot = path19.join(targetDir, "features");
2102
+ const featuresRoot = path20.join(targetDir, "features");
2093
2103
  for (const component of components) {
2094
- const componentDir = path19.join(featuresRoot, component);
2104
+ const componentDir = path20.join(featuresRoot, component);
2095
2105
  await fs15.ensureDir(componentDir);
2096
- const readmePath = path19.join(componentDir, "README.md");
2106
+ const readmePath = path20.join(componentDir, "README.md");
2097
2107
  if (!await fs15.pathExists(readmePath)) {
2098
2108
  await fs15.writeFile(
2099
2109
  readmePath,
@@ -2122,7 +2132,7 @@ async function runInit(options) {
2122
2132
  workflow: {
2123
2133
  mode: workflowMode,
2124
2134
  codeDirtyScope: "auto",
2125
- taskCommitGate: "strict",
2135
+ taskCommitGate: "warn",
2126
2136
  prePrReview: {
2127
2137
  skills: ["code-review-excellence"],
2128
2138
  minorPolicy: "warn"
@@ -2146,7 +2156,7 @@ async function runInit(options) {
2146
2156
  config.projectRoot = projectRoot;
2147
2157
  }
2148
2158
  }
2149
- const configPath = path19.join(targetDir, ".lee-spec-kit.json");
2159
+ const configPath = path20.join(targetDir, ".lee-spec-kit.json");
2150
2160
  await fs15.writeJson(configPath, config, { spaces: 2 });
2151
2161
  console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
2152
2162
  console.log();
@@ -2217,7 +2227,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2217
2227
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
2218
2228
  runGit2(["init"], gitWorkdir);
2219
2229
  }
2220
- const relativePath = docsRepo === "standalone" ? "." : path19.relative(cwd, targetDir);
2230
+ const relativePath = docsRepo === "standalone" ? "." : path20.relative(cwd, targetDir);
2221
2231
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2222
2232
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2223
2233
  console.log(
@@ -2274,17 +2284,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2274
2284
  }
2275
2285
  function getAncestorDirs(startDir) {
2276
2286
  const dirs = [];
2277
- let current = path19.resolve(startDir);
2287
+ let current = path20.resolve(startDir);
2278
2288
  while (true) {
2279
2289
  dirs.push(current);
2280
- const parent = path19.dirname(current);
2290
+ const parent = path20.dirname(current);
2281
2291
  if (parent === current) break;
2282
2292
  current = parent;
2283
2293
  }
2284
2294
  return dirs;
2285
2295
  }
2286
2296
  function hasWorkspaceBoundary(dir) {
2287
- return fs15.existsSync(path19.join(dir, "package.json")) || fs15.existsSync(path19.join(dir, ".git"));
2297
+ return fs15.existsSync(path20.join(dir, "package.json")) || fs15.existsSync(path20.join(dir, ".git"));
2288
2298
  }
2289
2299
  function getSearchBaseDirs(cwd) {
2290
2300
  const ancestors = getAncestorDirs(cwd);
@@ -2300,7 +2310,7 @@ function normalizeComponentKeys(value) {
2300
2310
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2301
2311
  }
2302
2312
  async function inferComponentsFromFeaturesDir(docsDir) {
2303
- const featuresPath = path19.join(docsDir, "features");
2313
+ const featuresPath = path20.join(docsDir, "features");
2304
2314
  if (!await fs15.pathExists(featuresPath)) return [];
2305
2315
  const entries = await fs15.readdir(featuresPath, { withFileTypes: true });
2306
2316
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
@@ -2311,21 +2321,21 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2311
2321
  async function getConfig(cwd) {
2312
2322
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2313
2323
  const baseDirs = [
2314
- ...explicitDocsDir ? [path19.resolve(explicitDocsDir)] : [],
2324
+ ...explicitDocsDir ? [path20.resolve(explicitDocsDir)] : [],
2315
2325
  ...getSearchBaseDirs(cwd)
2316
2326
  ];
2317
2327
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2318
2328
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2319
2329
  for (const baseDir of baseDirs) {
2320
- const resolvedBaseDir = path19.resolve(baseDir);
2330
+ const resolvedBaseDir = path20.resolve(baseDir);
2321
2331
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2322
2332
  visitedBaseDirs.add(resolvedBaseDir);
2323
- const possibleDocsDirs = [path19.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2333
+ const possibleDocsDirs = [path20.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2324
2334
  for (const docsDir of possibleDocsDirs) {
2325
- const resolvedDocsDir = path19.resolve(docsDir);
2335
+ const resolvedDocsDir = path20.resolve(docsDir);
2326
2336
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2327
2337
  visitedDocsDirs.add(resolvedDocsDir);
2328
- const configPath = path19.join(resolvedDocsDir, ".lee-spec-kit.json");
2338
+ const configPath = path20.join(resolvedDocsDir, ".lee-spec-kit.json");
2329
2339
  if (await fs15.pathExists(configPath)) {
2330
2340
  try {
2331
2341
  const configFile = await fs15.readJson(configPath);
@@ -2355,16 +2365,16 @@ async function getConfig(cwd) {
2355
2365
  } catch {
2356
2366
  }
2357
2367
  }
2358
- const agentsPath = path19.join(resolvedDocsDir, "agents");
2359
- const featuresPath = path19.join(resolvedDocsDir, "features");
2368
+ const agentsPath = path20.join(resolvedDocsDir, "agents");
2369
+ const featuresPath = path20.join(resolvedDocsDir, "features");
2360
2370
  if (await fs15.pathExists(agentsPath) && await fs15.pathExists(featuresPath)) {
2361
2371
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2362
2372
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2363
2373
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2364
2374
  const langProbeCandidates = [
2365
- path19.join(agentsPath, "custom.md"),
2366
- path19.join(agentsPath, "constitution.md"),
2367
- path19.join(agentsPath, "agents.md")
2375
+ path20.join(agentsPath, "custom.md"),
2376
+ path20.join(agentsPath, "constitution.md"),
2377
+ path20.join(agentsPath, "agents.md")
2368
2378
  ];
2369
2379
  let lang = "en";
2370
2380
  for (const candidate of langProbeCandidates) {
@@ -2430,13 +2440,13 @@ async function patchMarkdownIfExists(filePath, transform) {
2430
2440
  await fs15.writeFile(filePath, transform(content), "utf-8");
2431
2441
  }
2432
2442
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
2433
- await patchMarkdownIfExists(path19.join(featureDir, "spec.md"), sanitizeSpecForLocal);
2443
+ await patchMarkdownIfExists(path20.join(featureDir, "spec.md"), sanitizeSpecForLocal);
2434
2444
  await patchMarkdownIfExists(
2435
- path19.join(featureDir, "tasks.md"),
2445
+ path20.join(featureDir, "tasks.md"),
2436
2446
  (content) => sanitizeTasksForLocal(content, lang)
2437
2447
  );
2438
- await fs15.remove(path19.join(featureDir, "issue.md"));
2439
- await fs15.remove(path19.join(featureDir, "pr.md"));
2448
+ await fs15.remove(path20.join(featureDir, "issue.md"));
2449
+ await fs15.remove(path20.join(featureDir, "pr.md"));
2440
2450
  }
2441
2451
 
2442
2452
  // src/commands/feature.ts
@@ -2581,19 +2591,19 @@ async function runFeature(name, options) {
2581
2591
  }
2582
2592
  let featuresDir;
2583
2593
  if (projectType === "multi") {
2584
- featuresDir = path19.join(docsDir, "features", component);
2594
+ featuresDir = path20.join(docsDir, "features", component);
2585
2595
  } else {
2586
- featuresDir = path19.join(docsDir, "features");
2596
+ featuresDir = path20.join(docsDir, "features");
2587
2597
  }
2588
2598
  const featureFolderName = `${featureId}-${name}`;
2589
- const featureDir = path19.join(featuresDir, featureFolderName);
2599
+ const featureDir = path20.join(featuresDir, featureFolderName);
2590
2600
  if (await fs15.pathExists(featureDir)) {
2591
2601
  throw createCliError(
2592
2602
  "INVALID_ARGUMENT",
2593
2603
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
2594
2604
  );
2595
2605
  }
2596
- const featureBasePath = path19.join(
2606
+ const featureBasePath = path20.join(
2597
2607
  getTemplatesDir(),
2598
2608
  lang,
2599
2609
  "common",
@@ -2652,7 +2662,7 @@ async function runFeature(name, options) {
2652
2662
  featureName: name,
2653
2663
  component: projectType === "multi" ? component : void 0,
2654
2664
  featurePath: featureDir,
2655
- featurePathFromDocs: path19.relative(docsDir, featureDir)
2665
+ featurePathFromDocs: path20.relative(docsDir, featureDir)
2656
2666
  };
2657
2667
  },
2658
2668
  { owner: "feature" }
@@ -2664,9 +2674,9 @@ function sleep2(ms) {
2664
2674
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
2665
2675
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2666
2676
  const candidates = [
2667
- ...explicitDocsDir ? [path19.resolve(explicitDocsDir)] : [],
2668
- path19.resolve(cwd, "docs"),
2669
- path19.resolve(cwd)
2677
+ ...explicitDocsDir ? [path20.resolve(explicitDocsDir)] : [],
2678
+ path20.resolve(cwd, "docs"),
2679
+ path20.resolve(cwd)
2670
2680
  ];
2671
2681
  const endAt = Date.now() + timeoutMs;
2672
2682
  while (Date.now() < endAt) {
@@ -2693,11 +2703,11 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
2693
2703
  return getConfig(cwd);
2694
2704
  }
2695
2705
  async function getNextFeatureId(docsDir, projectType, components) {
2696
- const featuresDir = path19.join(docsDir, "features");
2706
+ const featuresDir = path20.join(docsDir, "features");
2697
2707
  let max = 0;
2698
2708
  const scanDirs = [];
2699
2709
  if (projectType === "multi") {
2700
- scanDirs.push(...components.map((component) => path19.join(featuresDir, component)));
2710
+ scanDirs.push(...components.map((component) => path20.join(featuresDir, component)));
2701
2711
  } else {
2702
2712
  scanDirs.push(featuresDir);
2703
2713
  }
@@ -2846,6 +2856,9 @@ function getPrReviewRemoteBlockReasons(feature, lang) {
2846
2856
  const remote = feature.pr.remote;
2847
2857
  if (!remote || !remote.available) return [];
2848
2858
  const reasons = [];
2859
+ if (remote.state === "CLOSED" && !remote.isMerged) {
2860
+ reasons.push(tr(lang, "messages", "prReviewRemoteReasonClosed"));
2861
+ }
2849
2862
  if (remote.hasBlockingReview) {
2850
2863
  reasons.push(tr(lang, "messages", "prReviewRemoteReasonChangesRequested"));
2851
2864
  }
@@ -2911,14 +2924,90 @@ function normalizeTaskTopic(value) {
2911
2924
  function normalizeCommitSubjectForGate(value) {
2912
2925
  return normalizeCommitTopicText(value).replace(/^[a-z]+(?:\([^)]*\))?!?:\s*/i, "").toLowerCase();
2913
2926
  }
2927
+ function toTaskKey(rawTitle) {
2928
+ const trimmed = normalizeCommitTopicText(rawTitle);
2929
+ if (!trimmed) return "";
2930
+ const idMatch = trimmed.match(/^(T-[A-Za-z0-9-]+)/i);
2931
+ if (idMatch) return idMatch[1].toUpperCase();
2932
+ return normalizeTaskTopic(trimmed).toLowerCase();
2933
+ }
2934
+ function countDoneTransitionsInLatestTasksCommit(feature) {
2935
+ const docsGitCwd = feature.git.docsGitCwd;
2936
+ const tasksRelativePath = normalizeGitRelativePath(
2937
+ path20.join(feature.docs.featurePathFromDocs, "tasks.md")
2938
+ );
2939
+ const diff = readGitText(docsGitCwd, [
2940
+ "diff",
2941
+ "--unified=0",
2942
+ "--no-color",
2943
+ "HEAD~1",
2944
+ "HEAD",
2945
+ "--",
2946
+ tasksRelativePath
2947
+ ]);
2948
+ if (diff === void 0) return 0;
2949
+ if (!diff.trim()) return 0;
2950
+ const removedByTask = /* @__PURE__ */ new Map();
2951
+ const addedByTask = /* @__PURE__ */ new Map();
2952
+ const parseTaskLine = (line) => {
2953
+ const match = line.match(/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\s+(.+?)\s*$/i);
2954
+ if (!match) return null;
2955
+ const key = toTaskKey(match[2]);
2956
+ if (!key) return null;
2957
+ return {
2958
+ key,
2959
+ status: match[1].toUpperCase()
2960
+ };
2961
+ };
2962
+ for (const line of diff.split("\n")) {
2963
+ if (line.startsWith("---") || line.startsWith("+++")) continue;
2964
+ if (line.startsWith("-")) {
2965
+ const parsed = parseTaskLine(line.slice(1));
2966
+ if (!parsed) continue;
2967
+ const existing = removedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
2968
+ existing.add(parsed.status);
2969
+ removedByTask.set(parsed.key, existing);
2970
+ continue;
2971
+ }
2972
+ if (line.startsWith("+")) {
2973
+ const parsed = parseTaskLine(line.slice(1));
2974
+ if (!parsed) continue;
2975
+ const existing = addedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
2976
+ existing.add(parsed.status);
2977
+ addedByTask.set(parsed.key, existing);
2978
+ }
2979
+ }
2980
+ let doneTransitions = 0;
2981
+ for (const [taskKey, addedStatuses] of addedByTask.entries()) {
2982
+ if (!addedStatuses.has("DONE")) continue;
2983
+ const removedStatuses = removedByTask.get(taskKey);
2984
+ if (!removedStatuses) continue;
2985
+ const transitionedFromOpen = removedStatuses.has("TODO") || removedStatuses.has("DOING") || removedStatuses.has("REVIEW");
2986
+ if (transitionedFromOpen) {
2987
+ doneTransitions += 1;
2988
+ }
2989
+ }
2990
+ return doneTransitions;
2991
+ }
2914
2992
  function checkTaskCommitGate(feature) {
2993
+ const doneTransitions = countDoneTransitionsInLatestTasksCommit(feature);
2994
+ if (doneTransitions === 0) {
2995
+ return { pass: true, doneTransitions };
2996
+ }
2997
+ if (typeof doneTransitions === "number" && doneTransitions > 1) {
2998
+ return {
2999
+ pass: false,
3000
+ reason: "DONE_TRANSITIONS_COUNT",
3001
+ doneTransitions
3002
+ };
3003
+ }
2915
3004
  const projectGitCwd = feature.git.projectGitCwd;
2916
3005
  const lastDoneTopic = normalizeTaskTopic(feature.lastDoneTask?.title || "");
2917
3006
  if (!projectGitCwd || !lastDoneTopic) {
2918
3007
  return { pass: true };
2919
3008
  }
2920
3009
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
2921
- const relativeDocsDir = path19.relative(projectGitCwd, feature.git.docsGitCwd);
3010
+ const relativeDocsDir = path20.relative(projectGitCwd, feature.git.docsGitCwd);
2922
3011
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
2923
3012
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
2924
3013
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -2939,6 +3028,10 @@ function checkTaskCommitGate(feature) {
2939
3028
  }
2940
3029
  function getTaskCommitGateReasonText(lang, check) {
2941
3030
  switch (check.reason) {
3031
+ case "DONE_TRANSITIONS_COUNT":
3032
+ return tr(lang, "messages", "taskCommitGateReasonDoneCount", {
3033
+ count: check.doneTransitions || 0
3034
+ });
2942
3035
  case "NO_PROJECT_COMMIT":
2943
3036
  return tr(lang, "messages", "taskCommitGateReasonNoTasksCommit");
2944
3037
  case "PROJECT_LOG_UNAVAILABLE":
@@ -3752,6 +3845,16 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3752
3845
  ];
3753
3846
  }
3754
3847
  if (f.pr.status === "Review") {
3848
+ if (f.pr.remote?.available && f.pr.remote.isMerged) {
3849
+ return [
3850
+ {
3851
+ type: "instruction",
3852
+ category: "pr_status_update",
3853
+ requiresUserCheck: true,
3854
+ message: tr(lang, "messages", "prReviewMergedSyncStatus")
3855
+ }
3856
+ ];
3857
+ }
3755
3858
  if (!f.docs.prReviewFindingsFieldExists) {
3756
3859
  return [
3757
3860
  {
@@ -3793,6 +3896,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3793
3896
  ];
3794
3897
  }
3795
3898
  const remoteBlockReasons = getPrReviewRemoteBlockReasons(f, lang);
3899
+ const remoteUnavailable = workflowPolicy.mode === "github" && !!f.pr.link && (!f.pr.remote || !f.pr.remote.available);
3796
3900
  const actions = [
3797
3901
  {
3798
3902
  type: "instruction",
@@ -3820,16 +3924,41 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3820
3924
  })
3821
3925
  });
3822
3926
  }
3823
- actions.push({
3824
- type: "instruction",
3825
- category: "code_review",
3826
- requiresUserCheck: true,
3827
- message: remoteBlockReasons.length > 0 ? tr(lang, "messages", "prReviewRemoteBlocked", {
3828
- reasons: remoteBlockReasons.join("; ")
3829
- }) : tr(lang, "messages", "prReviewMerge", {
3830
- featureRef: f.id || f.folderName
3831
- })
3832
- });
3927
+ if (remoteBlockReasons.length > 0 || remoteUnavailable) {
3928
+ const reasons = [...remoteBlockReasons];
3929
+ if (remoteUnavailable) {
3930
+ reasons.push(tr(lang, "messages", "prReviewRemoteReasonUnavailable"));
3931
+ }
3932
+ actions.push({
3933
+ type: "instruction",
3934
+ category: "code_review",
3935
+ requiresUserCheck: true,
3936
+ message: tr(lang, "messages", "prReviewRemoteBlocked", {
3937
+ reasons: reasons.join("; ")
3938
+ })
3939
+ });
3940
+ } else if (f.git.docsGitCwd) {
3941
+ actions.push({
3942
+ type: "command",
3943
+ category: "code_review",
3944
+ requiresUserCheck: true,
3945
+ operationType: "remote",
3946
+ scope: "docs",
3947
+ cwd: f.git.docsGitCwd,
3948
+ cmd: tr(lang, "messages", "prReviewMergeCommand", {
3949
+ featureRef: f.id || f.folderName
3950
+ })
3951
+ });
3952
+ } else {
3953
+ actions.push({
3954
+ type: "instruction",
3955
+ category: "code_review",
3956
+ requiresUserCheck: true,
3957
+ message: tr(lang, "messages", "prReviewMerge", {
3958
+ featureRef: f.id || f.folderName
3959
+ })
3960
+ });
3961
+ }
3833
3962
  return actions;
3834
3963
  }
3835
3964
  return [
@@ -3907,14 +4036,32 @@ function applyApprovalPolicy(step, actions, approval) {
3907
4036
  return { ...a, requiresUserCheck };
3908
4037
  });
3909
4038
  }
4039
+ function withUserRequestReplanOption(actions, lang) {
4040
+ if (actions.some((action) => action.category === "user_request_replan")) {
4041
+ return actions;
4042
+ }
4043
+ return [
4044
+ ...actions,
4045
+ {
4046
+ type: "instruction",
4047
+ category: "user_request_replan",
4048
+ requiresUserCheck: true,
4049
+ message: tr(lang, "messages", "userRequestReplan")
4050
+ }
4051
+ ];
4052
+ }
3910
4053
  function resolveFeatureProgress(feature, stepDefinitions, lang, approval) {
3911
4054
  const ordered = [...stepDefinitions].sort((a, b) => a.step - b.step);
3912
4055
  for (const definition of ordered) {
3913
4056
  if (!definition.current) continue;
3914
4057
  if (definition.current.when(feature)) {
4058
+ const currentActions = withUserRequestReplanOption(
4059
+ definition.current.actions(feature),
4060
+ lang
4061
+ );
3915
4062
  const actions = applyApprovalPolicy(
3916
4063
  definition.step,
3917
- definition.current.actions(feature),
4064
+ currentActions,
3918
4065
  approval
3919
4066
  );
3920
4067
  return {
@@ -4040,6 +4187,7 @@ function isGitPathIgnored(cwd, relativePath) {
4040
4187
  return void 0;
4041
4188
  }
4042
4189
  }
4190
+ var GIT_WORKTREE_CACHE = /* @__PURE__ */ new Map();
4043
4191
  function getGitTopLevel(cwd) {
4044
4192
  try {
4045
4193
  return execSync("git rev-parse --show-toplevel", {
@@ -4051,6 +4199,52 @@ function getGitTopLevel(cwd) {
4051
4199
  return null;
4052
4200
  }
4053
4201
  }
4202
+ function listGitWorktrees(cwd) {
4203
+ const topLevel = getGitTopLevel(cwd) || cwd;
4204
+ const cacheKey = path20.resolve(topLevel);
4205
+ const cached = GIT_WORKTREE_CACHE.get(cacheKey);
4206
+ if (cached) return cached;
4207
+ try {
4208
+ const out = execFileSync("git", ["worktree", "list", "--porcelain"], {
4209
+ cwd: topLevel,
4210
+ encoding: "utf-8",
4211
+ stdio: ["ignore", "pipe", "pipe"]
4212
+ });
4213
+ const entries = [];
4214
+ let current = null;
4215
+ for (const rawLine of out.split("\n")) {
4216
+ const line = rawLine.trim();
4217
+ if (!line) {
4218
+ if (current?.path) entries.push(current);
4219
+ current = null;
4220
+ continue;
4221
+ }
4222
+ if (line.startsWith("worktree ")) {
4223
+ if (current?.path) entries.push(current);
4224
+ current = { path: line.slice("worktree ".length).trim() };
4225
+ continue;
4226
+ }
4227
+ if (line.startsWith("branch ")) {
4228
+ if (!current) continue;
4229
+ const fullRef = line.slice("branch ".length).trim();
4230
+ current.branch = fullRef.replace(/^refs\/heads\//, "");
4231
+ }
4232
+ }
4233
+ if (current?.path) entries.push(current);
4234
+ GIT_WORKTREE_CACHE.set(cacheKey, entries);
4235
+ return entries;
4236
+ } catch {
4237
+ return void 0;
4238
+ }
4239
+ }
4240
+ function findWorktreePathForBranch(cwd, branchName) {
4241
+ const target = branchName.trim();
4242
+ if (!target) return void 0;
4243
+ const entries = listGitWorktrees(cwd);
4244
+ if (!entries) return void 0;
4245
+ const match = entries.find((entry) => entry.branch === target);
4246
+ return match?.path;
4247
+ }
4054
4248
  function resolveProjectGitCwd(config, repo, lang = config.lang ?? DEFAULT_LANG) {
4055
4249
  const docsRepo = config.docsRepo;
4056
4250
  if (docsRepo !== "standalone") {
@@ -4204,13 +4398,13 @@ function parsePrLink(value) {
4204
4398
  return trimmed;
4205
4399
  }
4206
4400
  function normalizeGitPath(value) {
4207
- return value.split(path19.sep).join("/");
4401
+ return value.split(path20.sep).join("/");
4208
4402
  }
4209
4403
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
4210
- const relativeDocsDir = path19.relative(projectGitCwd, docsDir);
4404
+ const relativeDocsDir = path20.relative(projectGitCwd, docsDir);
4211
4405
  if (!relativeDocsDir) return [];
4212
- if (path19.isAbsolute(relativeDocsDir)) return [];
4213
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path19.sep}`)) {
4406
+ if (path20.isAbsolute(relativeDocsDir)) return [];
4407
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path20.sep}`)) {
4214
4408
  return [];
4215
4409
  }
4216
4410
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(/\/+$/, "");
@@ -4232,6 +4426,27 @@ function uniqueNormalizedPaths(values) {
4232
4426
  var PROJECT_DIRTY_STATUS_CACHE = /* @__PURE__ */ new Map();
4233
4427
  var COMPONENT_STATUS_PATH_CACHE = /* @__PURE__ */ new Map();
4234
4428
  var PR_REMOTE_STATUS_CACHE = /* @__PURE__ */ new Map();
4429
+ var FEATURE_WORKTREE_CACHE = /* @__PURE__ */ new Map();
4430
+ function resolveFeatureWorktreePath(projectGitCwd, issueNumber, slug, folderName) {
4431
+ const expectedBranches = [
4432
+ `feat/${issueNumber}-${slug}`,
4433
+ `feat/${issueNumber}-${folderName}`
4434
+ ];
4435
+ for (const branchName of expectedBranches) {
4436
+ const cacheKey = `${projectGitCwd}::${branchName}`;
4437
+ let foundPath = FEATURE_WORKTREE_CACHE.get(cacheKey);
4438
+ if (typeof foundPath === "undefined") {
4439
+ foundPath = findWorktreePathForBranch(projectGitCwd, branchName) || null;
4440
+ FEATURE_WORKTREE_CACHE.set(cacheKey, foundPath);
4441
+ }
4442
+ if (!foundPath) continue;
4443
+ return {
4444
+ cwd: foundPath,
4445
+ branch: getCurrentBranch(foundPath) || branchName
4446
+ };
4447
+ }
4448
+ return void 0;
4449
+ }
4235
4450
  function toUpperToken(value) {
4236
4451
  if (typeof value !== "string") return void 0;
4237
4452
  const normalized = value.trim().toUpperCase();
@@ -4274,7 +4489,7 @@ function resolvePrRemoteStatus(prRef, projectGitCwd) {
4274
4489
  "view",
4275
4490
  prRef,
4276
4491
  "--json",
4277
- "reviewDecision,mergeStateStatus,isDraft,statusCheckRollup"
4492
+ "state,mergedAt,reviewDecision,mergeStateStatus,isDraft,statusCheckRollup"
4278
4493
  ],
4279
4494
  {
4280
4495
  cwd: projectGitCwd,
@@ -4289,6 +4504,9 @@ function resolvePrRemoteStatus(prRef, projectGitCwd) {
4289
4504
  return null;
4290
4505
  }
4291
4506
  const parsed = JSON.parse(raw);
4507
+ const state = toUpperToken(parsed.state);
4508
+ const mergedAt = typeof parsed.mergedAt === "string" && parsed.mergedAt.trim().length > 0 ? parsed.mergedAt.trim() : void 0;
4509
+ const isMerged = state === "MERGED" || !!mergedAt;
4292
4510
  const reviewDecision = toUpperToken(parsed.reviewDecision);
4293
4511
  const mergeStateStatus = toUpperToken(parsed.mergeStateStatus);
4294
4512
  const isDraft = parsed.isDraft === true;
@@ -4303,11 +4521,14 @@ function resolvePrRemoteStatus(prRef, projectGitCwd) {
4303
4521
  const remote = {
4304
4522
  source: "gh",
4305
4523
  available: true,
4524
+ state,
4525
+ mergedAt,
4526
+ isMerged,
4306
4527
  reviewDecision,
4307
4528
  mergeStateStatus,
4308
4529
  isDraft,
4309
4530
  hasBlockingReview: reviewDecision === "CHANGES_REQUESTED" || reviewDecision === "REVIEW_REQUIRED",
4310
- mergeBlocked: isDraft || isMergeBlockedState(mergeStateStatus),
4531
+ mergeBlocked: !isMerged && (isDraft || isMergeBlockedState(mergeStateStatus)),
4311
4532
  failingChecks,
4312
4533
  pendingChecks
4313
4534
  };
@@ -4331,10 +4552,10 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
4331
4552
  const normalizedCandidates = uniqueNormalizedPaths(
4332
4553
  candidates.map((candidate) => {
4333
4554
  if (!candidate) return "";
4334
- if (!path19.isAbsolute(candidate)) return candidate;
4335
- const relative = path19.relative(projectGitCwd, candidate);
4555
+ if (!path20.isAbsolute(candidate)) return candidate;
4556
+ const relative = path20.relative(projectGitCwd, candidate);
4336
4557
  if (!relative) return "";
4337
- if (relative === ".." || relative.startsWith(`..${path19.sep}`)) return "";
4558
+ if (relative === ".." || relative.startsWith(`..${path20.sep}`)) return "";
4338
4559
  return relative;
4339
4560
  }).filter(Boolean)
4340
4561
  );
@@ -4347,7 +4568,7 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
4347
4568
  if (cached) return [...cached];
4348
4569
  const existing = [];
4349
4570
  for (const candidate of normalizedCandidates) {
4350
- if (await fs15.pathExists(path19.join(projectGitCwd, candidate))) {
4571
+ if (await fs15.pathExists(path20.join(projectGitCwd, candidate))) {
4351
4572
  existing.push(candidate);
4352
4573
  }
4353
4574
  }
@@ -4436,15 +4657,15 @@ async function parseFeature(featurePath, type, context, options) {
4436
4657
  const lang = options.lang;
4437
4658
  const workflowPolicy = resolveWorkflowPolicy(options.workflow);
4438
4659
  const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
4439
- const folderName = path19.basename(featurePath);
4660
+ const folderName = path20.basename(featurePath);
4440
4661
  const match = folderName.match(/^(F\d+)-(.+)$/);
4441
4662
  const id = match?.[1];
4442
4663
  const slug = match?.[2] || folderName;
4443
- const specPath = path19.join(featurePath, "spec.md");
4444
- const planPath = path19.join(featurePath, "plan.md");
4445
- const tasksPath = path19.join(featurePath, "tasks.md");
4446
- const issueDocPath = path19.join(featurePath, "issue.md");
4447
- const prDocPath = path19.join(featurePath, "pr.md");
4664
+ const specPath = path20.join(featurePath, "spec.md");
4665
+ const planPath = path20.join(featurePath, "plan.md");
4666
+ const tasksPath = path20.join(featurePath, "tasks.md");
4667
+ const issueDocPath = path20.join(featurePath, "issue.md");
4668
+ const prDocPath = path20.join(featurePath, "pr.md");
4448
4669
  let specStatus;
4449
4670
  let issueNumber;
4450
4671
  const specExists = await fs15.pathExists(specPath);
@@ -4455,6 +4676,30 @@ async function parseFeature(featurePath, type, context, options) {
4455
4676
  const issueValue = extractFirstSpecValue(content, ["\uC774\uC288 \uBC88\uD638", "Issue Number", "Issue"]);
4456
4677
  issueNumber = parseIssueNumber(issueValue);
4457
4678
  }
4679
+ let effectiveProjectGitCwd = context.projectGitCwd;
4680
+ let effectiveProjectBranch = context.projectBranch;
4681
+ let effectiveProjectBranchAvailable = context.projectBranchAvailable;
4682
+ if (effectiveProjectGitCwd && issueNumber) {
4683
+ const alreadyExpected = isExpectedFeatureBranch(
4684
+ effectiveProjectBranch,
4685
+ issueNumber,
4686
+ slug,
4687
+ folderName
4688
+ );
4689
+ if (!alreadyExpected) {
4690
+ const worktree = resolveFeatureWorktreePath(
4691
+ effectiveProjectGitCwd,
4692
+ issueNumber,
4693
+ slug,
4694
+ folderName
4695
+ );
4696
+ if (worktree) {
4697
+ effectiveProjectGitCwd = worktree.cwd;
4698
+ effectiveProjectBranch = worktree.branch;
4699
+ effectiveProjectBranchAvailable = true;
4700
+ }
4701
+ }
4702
+ }
4458
4703
  let planStatus;
4459
4704
  const planExists = await fs15.pathExists(planPath);
4460
4705
  if (planExists) {
@@ -4594,20 +4839,20 @@ async function parseFeature(featurePath, type, context, options) {
4594
4839
  prDocPrFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
4595
4840
  prDocReviewStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
4596
4841
  }
4597
- if (workflowPolicy.requireReview && prStatus === "Review" && prLink && context.projectGitCwd) {
4598
- prRemote = resolvePrRemoteStatus(prLink, context.projectGitCwd) || void 0;
4842
+ if (workflowPolicy.requireReview && prStatus === "Review" && prLink && effectiveProjectGitCwd) {
4843
+ prRemote = resolvePrRemoteStatus(prLink, effectiveProjectGitCwd) || void 0;
4599
4844
  }
4600
4845
  const warnings = [];
4601
- if (context.projectBranchAvailable === false) {
4846
+ if (effectiveProjectBranchAvailable === false) {
4602
4847
  warnings.push(tr(lang, "warnings", "projectBranchUnavailable"));
4603
4848
  }
4604
4849
  const onExpectedBranch = isExpectedFeatureBranch(
4605
- context.projectBranch,
4850
+ effectiveProjectBranch,
4606
4851
  issueNumber,
4607
4852
  slug,
4608
4853
  folderName
4609
4854
  );
4610
- const relativeFeaturePathFromDocs = path19.relative(context.docsDir, featurePath);
4855
+ const relativeFeaturePathFromDocs = path20.relative(context.docsDir, featurePath);
4611
4856
  const normalizedFeaturePathFromDocs = normalizeGitPath(relativeFeaturePathFromDocs);
4612
4857
  const docsPathIgnored = typeof context.docsPathIgnored === "boolean" ? context.docsPathIgnored : isGitPathIgnored(context.docsGitCwd, normalizedFeaturePathFromDocs);
4613
4858
  let docsHasUncommittedChanges = typeof context.docsHasUncommittedChanges === "boolean" ? context.docsHasUncommittedChanges : false;
@@ -4631,10 +4876,10 @@ async function parseFeature(featurePath, type, context, options) {
4631
4876
  }
4632
4877
  let projectHasUncommittedChanges = typeof context.projectHasUncommittedChanges === "boolean" ? context.projectHasUncommittedChanges : false;
4633
4878
  let projectStatusUnavailable = !!context.projectStatusUnavailable;
4634
- if (typeof context.projectHasUncommittedChanges !== "boolean" && context.projectGitCwd) {
4879
+ if (typeof context.projectHasUncommittedChanges !== "boolean" && effectiveProjectGitCwd) {
4635
4880
  const dirtyScopePolicy = resolveCodeDirtyScopePolicy(options.workflow, options.projectType);
4636
4881
  const projectCacheKey = JSON.stringify({
4637
- projectGitCwd: context.projectGitCwd,
4882
+ projectGitCwd: effectiveProjectGitCwd,
4638
4883
  docsDir: context.docsDir,
4639
4884
  type,
4640
4885
  dirtyScopePolicy,
@@ -4648,19 +4893,19 @@ async function parseFeature(featurePath, type, context, options) {
4648
4893
  let projectStatusPaths = [];
4649
4894
  if (dirtyScopePolicy === "component" && type !== "single") {
4650
4895
  const componentStatusPaths = await resolveComponentStatusPaths(
4651
- context.projectGitCwd,
4896
+ effectiveProjectGitCwd,
4652
4897
  type,
4653
4898
  options.workflow
4654
4899
  );
4655
- projectStatusPaths = componentStatusPaths.length > 0 ? componentStatusPaths : resolveProjectStatusPaths(context.projectGitCwd, context.docsDir);
4900
+ projectStatusPaths = componentStatusPaths.length > 0 ? componentStatusPaths : resolveProjectStatusPaths(effectiveProjectGitCwd, context.docsDir);
4656
4901
  } else {
4657
4902
  projectStatusPaths = resolveProjectStatusPaths(
4658
- context.projectGitCwd,
4903
+ effectiveProjectGitCwd,
4659
4904
  context.docsDir
4660
4905
  );
4661
4906
  }
4662
4907
  const projectStatus = getGitStatusPorcelain(
4663
- context.projectGitCwd,
4908
+ effectiveProjectGitCwd,
4664
4909
  projectStatusPaths
4665
4910
  );
4666
4911
  projectStatusUnavailable = projectStatus === void 0;
@@ -4829,10 +5074,10 @@ async function parseFeature(featurePath, type, context, options) {
4829
5074
  pr: { link: prLink, status: prStatus, remote: prRemote },
4830
5075
  git: {
4831
5076
  docsBranch: context.docsBranch,
4832
- projectBranch: context.projectBranch,
4833
- projectBranchAvailable: context.projectBranchAvailable,
5077
+ projectBranch: effectiveProjectBranch,
5078
+ projectBranchAvailable: effectiveProjectBranchAvailable,
4834
5079
  docsGitCwd: context.docsGitCwd,
4835
- projectGitCwd: context.projectGitCwd,
5080
+ projectGitCwd: effectiveProjectGitCwd,
4836
5081
  onExpectedBranch,
4837
5082
  docsEverCommitted,
4838
5083
  docsHasUncommittedChanges,
@@ -5005,7 +5250,7 @@ async function scanFeatures(config) {
5005
5250
  }
5006
5251
  }
5007
5252
  const relativeFeaturePaths = allFeatureDirs.map(
5008
- (dir) => normalizeRelPath(path19.relative(config.docsDir, dir))
5253
+ (dir) => normalizeRelPath(path20.relative(config.docsDir, dir))
5009
5254
  );
5010
5255
  const docsGitMeta = buildDocsFeatureGitMeta(config.docsDir, relativeFeaturePaths);
5011
5256
  const parseTargets = config.projectType === "single" ? [{ type: "single", dirs: componentFeatureDirs.get("single") || [] }] : resolveProjectComponents(config.projectType, config.components).map((component) => ({
@@ -5016,7 +5261,7 @@ async function scanFeatures(config) {
5016
5261
  const parsed = await Promise.all(
5017
5262
  target.dirs.map(async (dir) => {
5018
5263
  const relativeFeaturePathFromDocs = normalizeRelPath(
5019
- path19.relative(config.docsDir, dir)
5264
+ path20.relative(config.docsDir, dir)
5020
5265
  );
5021
5266
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
5022
5267
  return parseFeature(
@@ -5085,13 +5330,13 @@ async function runStatus(options) {
5085
5330
  );
5086
5331
  }
5087
5332
  const { docsDir, projectType, projectName, lang } = config;
5088
- const featuresDir = path19.join(docsDir, "features");
5333
+ const featuresDir = path20.join(docsDir, "features");
5089
5334
  const scan = await scanFeatures(config);
5090
5335
  const features = [];
5091
5336
  const idMap = /* @__PURE__ */ new Map();
5092
5337
  for (const f of scan.features) {
5093
5338
  const id = f.id || "UNKNOWN";
5094
- const relPath = path19.relative(docsDir, f.path);
5339
+ const relPath = path20.relative(docsDir, f.path);
5095
5340
  if (!idMap.has(id)) idMap.set(id, []);
5096
5341
  idMap.get(id).push(relPath);
5097
5342
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -5172,7 +5417,7 @@ async function runStatus(options) {
5172
5417
  }
5173
5418
  console.log();
5174
5419
  if (options.write) {
5175
- const outputPath = path19.join(featuresDir, "status.md");
5420
+ const outputPath = path20.join(featuresDir, "status.md");
5176
5421
  const date = getLocalDateString();
5177
5422
  const content = [
5178
5423
  "# Feature Status",
@@ -5200,7 +5445,7 @@ function escapeRegExp2(value) {
5200
5445
  }
5201
5446
  async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
5202
5447
  try {
5203
- const specPath = path19.join(featureDir, "spec.md");
5448
+ const specPath = path20.join(featureDir, "spec.md");
5204
5449
  if (!await fs15.pathExists(specPath)) return fallbackSlug;
5205
5450
  const content = await fs15.readFile(specPath, "utf-8");
5206
5451
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
@@ -5282,10 +5527,10 @@ async function runUpdate(options) {
5282
5527
  console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
5283
5528
  }
5284
5529
  if (agentsMode === "all") {
5285
- const commonAgentsBase = path19.join(templatesDir, lang, "common", "agents");
5286
- const targetAgentsBase = path19.join(docsDir, "agents");
5287
- const commonAgents = agentsMode === "skills" ? path19.join(commonAgentsBase, "skills") : commonAgentsBase;
5288
- const targetAgents = agentsMode === "skills" ? path19.join(targetAgentsBase, "skills") : targetAgentsBase;
5530
+ const commonAgentsBase = path20.join(templatesDir, lang, "common", "agents");
5531
+ const targetAgentsBase = path20.join(docsDir, "agents");
5532
+ const commonAgents = commonAgentsBase;
5533
+ const targetAgents = targetAgentsBase;
5289
5534
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
5290
5535
  const projectName = config.projectName ?? "{{projectName}}";
5291
5536
  const commonReplacements = {
@@ -5312,7 +5557,7 @@ async function runUpdate(options) {
5312
5557
  }
5313
5558
  console.log(
5314
5559
  chalk6.green(
5315
- ` \u2705 ${agentsMode === "skills" ? tr(lang, "cli", "update.skillsUpdated") : tr(lang, "cli", "update.agentsUpdated")}`
5560
+ ` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`
5316
5561
  )
5317
5562
  );
5318
5563
  }
@@ -5365,7 +5610,7 @@ function normalizeSkillList2(raw) {
5365
5610
  return [...deduped];
5366
5611
  }
5367
5612
  async function backfillMissingConfigDefaults(docsDir) {
5368
- const configPath = path19.join(docsDir, ".lee-spec-kit.json");
5613
+ const configPath = path20.join(docsDir, ".lee-spec-kit.json");
5369
5614
  if (!await fs15.pathExists(configPath)) {
5370
5615
  return { changed: false, changedPaths: [] };
5371
5616
  }
@@ -5386,7 +5631,7 @@ async function backfillMissingConfigDefaults(docsDir) {
5386
5631
  const workflow = raw.workflow;
5387
5632
  setIfMissing(workflow, "mode", "github", "workflow.mode");
5388
5633
  setIfMissing(workflow, "codeDirtyScope", "auto", "workflow.codeDirtyScope");
5389
- setIfMissing(workflow, "taskCommitGate", "strict", "workflow.taskCommitGate");
5634
+ setIfMissing(workflow, "taskCommitGate", "warn", "workflow.taskCommitGate");
5390
5635
  if (!isPlainObject(workflow.prePrReview)) {
5391
5636
  workflow.prePrReview = {};
5392
5637
  changedPaths.push("workflow.prePrReview");
@@ -5438,8 +5683,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
5438
5683
  const files = await fs15.readdir(sourceDir);
5439
5684
  let updatedCount = 0;
5440
5685
  for (const file of files) {
5441
- const sourcePath = path19.join(sourceDir, file);
5442
- const targetPath = path19.join(targetDir, file);
5686
+ const sourcePath = path20.join(sourceDir, file);
5687
+ const targetPath = path20.join(targetDir, file);
5443
5688
  const stat = await fs15.stat(sourcePath);
5444
5689
  if (stat.isFile()) {
5445
5690
  if (protectedFiles.has(file)) {
@@ -5522,7 +5767,7 @@ function extractPorcelainPaths(line) {
5522
5767
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
5523
5768
  const top = getGitTopLevel2(docsDir);
5524
5769
  if (!top) return null;
5525
- const rel = path19.relative(top, docsDir) || ".";
5770
+ const rel = path20.relative(top, docsDir) || ".";
5526
5771
  try {
5527
5772
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
5528
5773
  cwd: top,
@@ -5534,7 +5779,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
5534
5779
  }
5535
5780
  const ignoredRelPaths = new Set(
5536
5781
  ignoredAbsPaths.map(
5537
- (absPath) => normalizeGitPath2(path19.relative(top, absPath) || ".")
5782
+ (absPath) => normalizeGitPath2(path20.relative(top, absPath) || ".")
5538
5783
  )
5539
5784
  );
5540
5785
  const filtered = output.split("\n").filter((line) => {
@@ -5591,7 +5836,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
5591
5836
  }
5592
5837
  async function runConfig(options) {
5593
5838
  const cwd = process.cwd();
5594
- const targetCwd = options.dir ? path19.resolve(cwd, options.dir) : cwd;
5839
+ const targetCwd = options.dir ? path20.resolve(cwd, options.dir) : cwd;
5595
5840
  const config = await getConfig(targetCwd);
5596
5841
  if (!config) {
5597
5842
  throw createCliError(
@@ -5599,7 +5844,7 @@ async function runConfig(options) {
5599
5844
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
5600
5845
  );
5601
5846
  }
5602
- const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
5847
+ const configPath = path20.join(config.docsDir, ".lee-spec-kit.json");
5603
5848
  if (!options.projectRoot) {
5604
5849
  console.log();
5605
5850
  console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -5615,6 +5860,7 @@ async function runConfig(options) {
5615
5860
  console.log();
5616
5861
  return;
5617
5862
  }
5863
+ const projectRoot = options.projectRoot;
5618
5864
  await withFileLock(
5619
5865
  getDocsLockPath(config.docsDir),
5620
5866
  async () => {
@@ -5668,13 +5914,13 @@ async function runConfig(options) {
5668
5914
  }
5669
5915
  assertAllowedComponent(targetComponent, components);
5670
5916
  const currentRoot = typeof configFile.projectRoot === "object" && configFile.projectRoot ? configFile.projectRoot : {};
5671
- currentRoot[targetComponent] = options.projectRoot;
5917
+ currentRoot[targetComponent] = projectRoot;
5672
5918
  configFile.projectRoot = currentRoot;
5673
5919
  console.log(
5674
5920
  chalk6.green(
5675
5921
  tr(config.lang, "cli", "config.projectRootSet", {
5676
5922
  repo: targetComponent.toUpperCase(),
5677
- path: options.projectRoot
5923
+ path: projectRoot
5678
5924
  })
5679
5925
  )
5680
5926
  );
@@ -5685,11 +5931,11 @@ async function runConfig(options) {
5685
5931
  "`--component` is only valid for multi projectRoot updates."
5686
5932
  );
5687
5933
  }
5688
- configFile.projectRoot = options.projectRoot;
5934
+ configFile.projectRoot = projectRoot;
5689
5935
  console.log(
5690
5936
  chalk6.green(
5691
5937
  tr(config.lang, "cli", "config.projectRootSetSingle", {
5692
- path: options.projectRoot
5938
+ path: projectRoot
5693
5939
  })
5694
5940
  )
5695
5941
  );
@@ -5758,6 +6004,7 @@ function getActionSummary(action) {
5758
6004
  if (action.category === "pre_pr_review") return "Run pre-PR self review";
5759
6005
  if (action.category === "pr_status_update") return "Update PR status";
5760
6006
  if (action.category === "code_review") return "Process code review feedback";
6007
+ if (action.category === "user_request_replan") return "Handle a new user request first";
5761
6008
  if (action.category === "task_execute") return "Proceed with task execution";
5762
6009
  if (action.category === "review_fix_commit") return "Commit review feedback fixes";
5763
6010
  if (action.category === "feature_done") return "Feature is complete";
@@ -5965,42 +6212,42 @@ var BUILTIN_DOC_DEFINITIONS = [
5965
6212
  {
5966
6213
  id: "agents",
5967
6214
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
5968
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "agents.md")
6215
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "agents.md")
5969
6216
  },
5970
6217
  {
5971
6218
  id: "git-workflow",
5972
6219
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
5973
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "git-workflow.md")
6220
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "git-workflow.md")
5974
6221
  },
5975
6222
  {
5976
6223
  id: "issue-doc",
5977
6224
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
5978
- relativePath: (_, lang) => path19.join(lang, "common", "features", "feature-base", "issue.md")
6225
+ relativePath: (_, lang) => path20.join(lang, "common", "features", "feature-base", "issue.md")
5979
6226
  },
5980
6227
  {
5981
6228
  id: "pr-doc",
5982
6229
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
5983
- relativePath: (_, lang) => path19.join(lang, "common", "features", "feature-base", "pr.md")
6230
+ relativePath: (_, lang) => path20.join(lang, "common", "features", "feature-base", "pr.md")
5984
6231
  },
5985
6232
  {
5986
6233
  id: "create-feature",
5987
6234
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
5988
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-feature.md")
6235
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "skills", "create-feature.md")
5989
6236
  },
5990
6237
  {
5991
6238
  id: "execute-task",
5992
6239
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
5993
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "execute-task.md")
6240
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "skills", "execute-task.md")
5994
6241
  },
5995
6242
  {
5996
6243
  id: "create-issue",
5997
6244
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
5998
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-issue.md")
6245
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "skills", "create-issue.md")
5999
6246
  },
6000
6247
  {
6001
6248
  id: "create-pr",
6002
6249
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
6003
- relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-pr.md")
6250
+ relativePath: (_, lang) => path20.join(lang, "common", "agents", "skills", "create-pr.md")
6004
6251
  }
6005
6252
  ];
6006
6253
  var DOC_FOLLOWUPS = {
@@ -6036,7 +6283,8 @@ var CATEGORY_DOC_MAP = {
6036
6283
  pre_pr_review: ["create-pr"],
6037
6284
  pr_create: ["create-pr", "pr-doc", "git-workflow"],
6038
6285
  pr_status_update: ["create-pr"],
6039
- code_review: ["create-pr"]
6286
+ code_review: ["create-pr"],
6287
+ user_request_replan: ["agents", "execute-task"]
6040
6288
  };
6041
6289
  function getBuiltinDocIds() {
6042
6290
  return BUILTIN_DOC_DEFINITIONS.map((doc) => doc.id);
@@ -6087,7 +6335,7 @@ function listBuiltinDocs(projectType, lang) {
6087
6335
  id: doc.id,
6088
6336
  title: doc.title[lang],
6089
6337
  relativePath,
6090
- absolutePath: path19.join(templatesDir, relativePath)
6338
+ absolutePath: path20.join(templatesDir, relativePath)
6091
6339
  };
6092
6340
  });
6093
6341
  }
@@ -6136,7 +6384,7 @@ function parseApprovalLabel(input, validLabels) {
6136
6384
  function getApprovalTicketPaths(config) {
6137
6385
  return {
6138
6386
  runtimePath: getApprovalTicketStorePath(config.docsDir),
6139
- legacyPath: path19.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
6387
+ legacyPath: path20.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
6140
6388
  };
6141
6389
  }
6142
6390
  function getApprovalSessionId() {
@@ -6164,7 +6412,7 @@ async function loadApprovalTicketStore(storePath) {
6164
6412
  }
6165
6413
  }
6166
6414
  async function saveApprovalTicketStore(storePath, payload) {
6167
- await fs15.ensureDir(path19.dirname(storePath));
6415
+ await fs15.ensureDir(path20.dirname(storePath));
6168
6416
  await fs15.writeJson(storePath, payload, { spaces: 2 });
6169
6417
  }
6170
6418
  async function resolveApprovalTicketStoreAndPath(config, nowMs) {
@@ -7130,7 +7378,7 @@ async function runContext(featureName, options) {
7130
7378
  if (f.issueNumber) {
7131
7379
  console.log(` \u2022 Issue: #${f.issueNumber}`);
7132
7380
  }
7133
- console.log(` \u2022 Path: ${path19.relative(cwd, f.path)}`);
7381
+ console.log(` \u2022 Path: ${path20.relative(cwd, f.path)}`);
7134
7382
  if (f.git.projectBranch) {
7135
7383
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
7136
7384
  }
@@ -7460,7 +7708,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
7460
7708
  ]);
7461
7709
  function formatPath(cwd, p) {
7462
7710
  if (!p) return "";
7463
- return path19.isAbsolute(p) ? path19.relative(cwd, p) : p;
7711
+ return path20.isAbsolute(p) ? path20.relative(cwd, p) : p;
7464
7712
  }
7465
7713
  function detectPlaceholders(content) {
7466
7714
  const patterns = [
@@ -7609,7 +7857,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
7609
7857
  const placeholderContext = {
7610
7858
  projectName: config.projectName,
7611
7859
  featureName: f.slug,
7612
- featurePath: f.docs.featurePathFromDocs || path19.relative(config.docsDir, f.path),
7860
+ featurePath: f.docs.featurePathFromDocs || path20.relative(config.docsDir, f.path),
7613
7861
  repoType: f.type,
7614
7862
  featureNumber
7615
7863
  };
@@ -7619,7 +7867,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
7619
7867
  "tasks.md"
7620
7868
  ];
7621
7869
  for (const file of files) {
7622
- const fullPath = path19.join(f.path, file);
7870
+ const fullPath = path20.join(f.path, file);
7623
7871
  if (!await fs15.pathExists(fullPath)) continue;
7624
7872
  const original = await fs15.readFile(fullPath, "utf-8");
7625
7873
  let next = original;
@@ -7664,7 +7912,7 @@ async function checkDocsStructure(config, cwd) {
7664
7912
  const issues = [];
7665
7913
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
7666
7914
  for (const dir of requiredDirs) {
7667
- const p = path19.join(config.docsDir, dir);
7915
+ const p = path20.join(config.docsDir, dir);
7668
7916
  if (!await fs15.pathExists(p)) {
7669
7917
  issues.push({
7670
7918
  level: "error",
@@ -7674,7 +7922,7 @@ async function checkDocsStructure(config, cwd) {
7674
7922
  });
7675
7923
  }
7676
7924
  }
7677
- const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
7925
+ const configPath = path20.join(config.docsDir, ".lee-spec-kit.json");
7678
7926
  if (!await fs15.pathExists(configPath)) {
7679
7927
  issues.push({
7680
7928
  level: "warn",
@@ -7697,7 +7945,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7697
7945
  }
7698
7946
  const idMap = /* @__PURE__ */ new Map();
7699
7947
  for (const f of features) {
7700
- const rel = f.docs.featurePathFromDocs || path19.relative(config.docsDir, f.path);
7948
+ const rel = f.docs.featurePathFromDocs || path20.relative(config.docsDir, f.path);
7701
7949
  const id = f.id || "UNKNOWN";
7702
7950
  if (!idMap.has(id)) idMap.set(id, []);
7703
7951
  idMap.get(id).push(rel);
@@ -7705,7 +7953,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7705
7953
  if (!isInitialTemplateState) {
7706
7954
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
7707
7955
  for (const file of featureDocs) {
7708
- const p = path19.join(f.path, file);
7956
+ const p = path20.join(f.path, file);
7709
7957
  if (!await fs15.pathExists(p)) continue;
7710
7958
  const content = await fs15.readFile(p, "utf-8");
7711
7959
  const placeholders = detectPlaceholders(content);
@@ -7720,7 +7968,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7720
7968
  });
7721
7969
  }
7722
7970
  if (decisionsPlaceholderMode !== "off") {
7723
- const decisionsPath = path19.join(f.path, "decisions.md");
7971
+ const decisionsPath = path20.join(f.path, "decisions.md");
7724
7972
  if (await fs15.pathExists(decisionsPath)) {
7725
7973
  const content = await fs15.readFile(decisionsPath, "utf-8");
7726
7974
  const placeholders = detectPlaceholders(content);
@@ -7749,7 +7997,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7749
7997
  level: "warn",
7750
7998
  code: "spec_status_unset",
7751
7999
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
7752
- path: formatPath(cwd, path19.join(f.path, "spec.md"))
8000
+ path: formatPath(cwd, path20.join(f.path, "spec.md"))
7753
8001
  });
7754
8002
  }
7755
8003
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -7757,7 +8005,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7757
8005
  level: "warn",
7758
8006
  code: "plan_status_unset",
7759
8007
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
7760
- path: formatPath(cwd, path19.join(f.path, "plan.md"))
8008
+ path: formatPath(cwd, path20.join(f.path, "plan.md"))
7761
8009
  });
7762
8010
  }
7763
8011
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -7765,7 +8013,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7765
8013
  level: "warn",
7766
8014
  code: "tasks_empty",
7767
8015
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
7768
- path: formatPath(cwd, path19.join(f.path, "tasks.md"))
8016
+ path: formatPath(cwd, path20.join(f.path, "tasks.md"))
7769
8017
  });
7770
8018
  }
7771
8019
  if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
@@ -7773,7 +8021,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7773
8021
  level: "warn",
7774
8022
  code: "tasks_doc_status_missing",
7775
8023
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
7776
- path: formatPath(cwd, path19.join(f.path, "tasks.md"))
8024
+ path: formatPath(cwd, path20.join(f.path, "tasks.md"))
7777
8025
  });
7778
8026
  }
7779
8027
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -7781,7 +8029,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7781
8029
  level: "warn",
7782
8030
  code: "tasks_doc_status_unset",
7783
8031
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
7784
- path: formatPath(cwd, path19.join(f.path, "tasks.md"))
8032
+ path: formatPath(cwd, path20.join(f.path, "tasks.md"))
7785
8033
  });
7786
8034
  }
7787
8035
  }
@@ -7805,7 +8053,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
7805
8053
  level: "warn",
7806
8054
  code: "missing_feature_id",
7807
8055
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
7808
- path: formatPath(cwd, path19.join(config.docsDir, p))
8056
+ path: formatPath(cwd, path20.join(config.docsDir, p))
7809
8057
  });
7810
8058
  }
7811
8059
  return issues;
@@ -7927,7 +8175,7 @@ function doctorCommand(program2) {
7927
8175
  }
7928
8176
  console.log();
7929
8177
  console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
7930
- console.log(chalk6.gray(`- Docs: ${path19.relative(cwd, docsDir)}`));
8178
+ console.log(chalk6.gray(`- Docs: ${path20.relative(cwd, docsDir)}`));
7931
8179
  console.log(chalk6.gray(`- Type: ${projectType}`));
7932
8180
  console.log(chalk6.gray(`- Lang: ${lang}`));
7933
8181
  console.log();
@@ -8100,7 +8348,7 @@ async function runView(featureName, options) {
8100
8348
  }
8101
8349
  console.log();
8102
8350
  console.log(chalk6.bold("\u{1F4CA} Workflow View"));
8103
- console.log(chalk6.gray(`- Docs: ${path19.relative(cwd, config.docsDir)}`));
8351
+ console.log(chalk6.gray(`- Docs: ${path20.relative(cwd, config.docsDir)}`));
8104
8352
  console.log(
8105
8353
  chalk6.gray(
8106
8354
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
@@ -8473,25 +8721,25 @@ function tg(lang, key, vars = {}) {
8473
8721
  }
8474
8722
  function detectGithubCliLangSync(cwd) {
8475
8723
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
8476
- const startDirs = [explicitDocsDir ? path19.resolve(explicitDocsDir) : "", path19.resolve(cwd)].filter(Boolean);
8724
+ const startDirs = [explicitDocsDir ? path20.resolve(explicitDocsDir) : "", path20.resolve(cwd)].filter(Boolean);
8477
8725
  const scanOrder = [];
8478
8726
  const seen = /* @__PURE__ */ new Set();
8479
8727
  for (const start of startDirs) {
8480
8728
  let current = start;
8481
8729
  while (true) {
8482
- const abs = path19.resolve(current);
8730
+ const abs = path20.resolve(current);
8483
8731
  if (!seen.has(abs)) {
8484
8732
  scanOrder.push(abs);
8485
8733
  seen.add(abs);
8486
8734
  }
8487
- const parent = path19.dirname(abs);
8735
+ const parent = path20.dirname(abs);
8488
8736
  if (parent === abs) break;
8489
8737
  current = parent;
8490
8738
  }
8491
8739
  }
8492
8740
  for (const base of scanOrder) {
8493
- for (const docsDir of [path19.join(base, "docs"), base]) {
8494
- const configPath = path19.join(docsDir, ".lee-spec-kit.json");
8741
+ for (const docsDir of [path20.join(base, "docs"), base]) {
8742
+ const configPath = path20.join(docsDir, ".lee-spec-kit.json");
8495
8743
  if (fs15.existsSync(configPath)) {
8496
8744
  try {
8497
8745
  const parsed = fs15.readJsonSync(configPath);
@@ -8499,11 +8747,11 @@ function detectGithubCliLangSync(cwd) {
8499
8747
  } catch {
8500
8748
  }
8501
8749
  }
8502
- const agentsPath = path19.join(docsDir, "agents");
8503
- const featuresPath = path19.join(docsDir, "features");
8750
+ const agentsPath = path20.join(docsDir, "agents");
8751
+ const featuresPath = path20.join(docsDir, "features");
8504
8752
  if (!fs15.existsSync(agentsPath) || !fs15.existsSync(featuresPath)) continue;
8505
8753
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
8506
- const file = path19.join(agentsPath, probe);
8754
+ const file = path20.join(agentsPath, probe);
8507
8755
  if (!fs15.existsSync(file)) continue;
8508
8756
  try {
8509
8757
  const content = fs15.readFileSync(file, "utf-8");
@@ -8601,7 +8849,7 @@ function ensureSections(body, sections, kind, lang) {
8601
8849
  }
8602
8850
  function ensureDocsExist(docsDir, relativePaths, lang) {
8603
8851
  const missing = relativePaths.filter(
8604
- (relativePath) => !fs15.existsSync(path19.join(docsDir, relativePath))
8852
+ (relativePath) => !fs15.existsSync(path20.join(docsDir, relativePath))
8605
8853
  );
8606
8854
  if (missing.length > 0) {
8607
8855
  throw createCliError(
@@ -8611,13 +8859,13 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
8611
8859
  }
8612
8860
  }
8613
8861
  function buildDefaultBodyFileName(kind, docsDir, component) {
8614
- const key = `${path19.resolve(docsDir)}::${component.trim().toLowerCase()}`;
8862
+ const key = `${path20.resolve(docsDir)}::${component.trim().toLowerCase()}`;
8615
8863
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
8616
8864
  return `lee-spec-kit.${digest}.${kind}.md`;
8617
8865
  }
8618
8866
  function toBodyFilePath(raw, kind, docsDir, component) {
8619
- const selected = raw?.trim() || path19.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
8620
- return path19.resolve(selected);
8867
+ const selected = raw?.trim() || path20.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
8868
+ return path20.resolve(selected);
8621
8869
  }
8622
8870
  function toProjectRootDocsPath(relativePathFromDocs) {
8623
8871
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -8710,6 +8958,10 @@ function resolveGithubDocsCwd(config, feature) {
8710
8958
  if (docsGitCwd) return docsGitCwd;
8711
8959
  return config.docsDir;
8712
8960
  }
8961
+ function shouldPushDocsSync(config) {
8962
+ if (config.docsRepo !== "standalone") return true;
8963
+ return config.pushDocs === true;
8964
+ }
8713
8965
  function getFeatureDocPaths(feature) {
8714
8966
  const featurePathFromDocs = feature.docs.featurePathFromDocs;
8715
8967
  return {
@@ -9432,8 +9684,8 @@ function ensureCleanWorktree(cwd, lang) {
9432
9684
  );
9433
9685
  }
9434
9686
  }
9435
- function commitAndPushPath(cwd, absPath, message, lang) {
9436
- const relativePath = path19.relative(cwd, absPath) || absPath;
9687
+ function commitAndPushPath(cwd, absPath, message, lang, options) {
9688
+ const relativePath = path20.relative(cwd, absPath) || absPath;
9437
9689
  const status = runProcessOrThrow(
9438
9690
  "git",
9439
9691
  ["status", "--porcelain=v1", "--", relativePath],
@@ -9443,6 +9695,7 @@ function commitAndPushPath(cwd, absPath, message, lang) {
9443
9695
  if (status.stdout.trim().length === 0) return;
9444
9696
  runProcessOrThrow("git", ["add", "--", relativePath], cwd, tg(lang, "stageFileFailed"));
9445
9697
  runProcessOrThrow("git", ["commit", "-m", message], cwd, tg(lang, "commitSyncFailed"));
9698
+ if (options?.pushToOrigin === false) return;
9446
9699
  const branch = gitCurrentBranch(cwd, lang);
9447
9700
  runProcessOrThrow(
9448
9701
  "git",
@@ -9565,9 +9818,9 @@ function githubCommand(program2) {
9565
9818
  [paths.specPath, paths.planPath, paths.tasksPath],
9566
9819
  config.lang
9567
9820
  );
9568
- const specContent = await fs15.readFile(path19.join(config.docsDir, paths.specPath), "utf-8");
9569
- const planContent = await fs15.readFile(path19.join(config.docsDir, paths.planPath), "utf-8");
9570
- const tasksContent = await fs15.readFile(path19.join(config.docsDir, paths.tasksPath), "utf-8");
9821
+ const specContent = await fs15.readFile(path20.join(config.docsDir, paths.specPath), "utf-8");
9822
+ const planContent = await fs15.readFile(path20.join(config.docsDir, paths.planPath), "utf-8");
9823
+ const tasksContent = await fs15.readFile(path20.join(config.docsDir, paths.tasksPath), "utf-8");
9571
9824
  const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
9572
9825
  const title = options.title?.trim() || tg(config.lang, "issueDefaultTitle", {
9573
9826
  slug: feature.slug,
@@ -9605,7 +9858,7 @@ function githubCommand(program2) {
9605
9858
  config.lang
9606
9859
  );
9607
9860
  } else {
9608
- await fs15.ensureDir(path19.dirname(bodyFile));
9861
+ await fs15.ensureDir(path20.dirname(bodyFile));
9609
9862
  await fs15.writeFile(bodyFile, generatedBody, "utf-8");
9610
9863
  }
9611
9864
  let issueUrl;
@@ -9704,10 +9957,10 @@ function githubCommand(program2) {
9704
9957
  const labels = parseLabels(options.labels, config.lang);
9705
9958
  const paths = getFeatureDocPaths(feature);
9706
9959
  ensureDocsExist(config.docsDir, [paths.specPath, paths.tasksPath], config.lang);
9707
- const specContent = await fs15.readFile(path19.join(config.docsDir, paths.specPath), "utf-8");
9708
- const planPath = path19.join(config.docsDir, paths.planPath);
9960
+ const specContent = await fs15.readFile(path20.join(config.docsDir, paths.specPath), "utf-8");
9961
+ const planPath = path20.join(config.docsDir, paths.planPath);
9709
9962
  const planContent = await fs15.pathExists(planPath) ? await fs15.readFile(planPath, "utf-8") : "";
9710
- const tasksContent = await fs15.readFile(path19.join(config.docsDir, paths.tasksPath), "utf-8");
9963
+ const tasksContent = await fs15.readFile(path20.join(config.docsDir, paths.tasksPath), "utf-8");
9711
9964
  const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
9712
9965
  const defaultTitle = feature.issueNumber ? tg(config.lang, "prDefaultTitleWithIssue", {
9713
9966
  issue: feature.issueNumber,
@@ -9750,13 +10003,14 @@ function githubCommand(program2) {
9750
10003
  config.lang
9751
10004
  );
9752
10005
  } else {
9753
- await fs15.ensureDir(path19.dirname(bodyFile));
10006
+ await fs15.ensureDir(path20.dirname(bodyFile));
9754
10007
  await fs15.writeFile(bodyFile, generatedBody, "utf-8");
9755
10008
  }
9756
10009
  const retryCount = toRetryCount(options.retry, config.lang);
9757
10010
  let prUrl = options.pr?.trim() || "";
9758
10011
  let mergedAttempts;
9759
10012
  let syncChanged = false;
10013
+ const pushDocsSync = shouldPushDocsSync(config);
9760
10014
  if (options.create) {
9761
10015
  const projectGitCwd = resolveGithubProjectCwd(config, feature);
9762
10016
  ensureNoTodoPlaceholders(body, tg(config.lang, "kindPr"), config.lang);
@@ -9789,6 +10043,9 @@ function githubCommand(program2) {
9789
10043
  );
9790
10044
  prUrl = created.stdout.trim();
9791
10045
  }
10046
+ if (!prUrl && options.merge) {
10047
+ prUrl = (feature.pr.link || "").trim();
10048
+ }
9792
10049
  if (!prUrl && options.merge) {
9793
10050
  throw createCliError(
9794
10051
  "INVALID_ARGUMENT",
@@ -9804,7 +10061,7 @@ function githubCommand(program2) {
9804
10061
  }
9805
10062
  if (prUrl && options.syncTasks !== false) {
9806
10063
  const synced = syncTasksPrMetadata(
9807
- path19.join(config.docsDir, paths.tasksPath),
10064
+ path20.join(config.docsDir, paths.tasksPath),
9808
10065
  prUrl,
9809
10066
  "Review",
9810
10067
  config.lang
@@ -9823,7 +10080,8 @@ function githubCommand(program2) {
9823
10080
  docsGitCwd,
9824
10081
  synced.path,
9825
10082
  message,
9826
- config.lang
10083
+ config.lang,
10084
+ { pushToOrigin: pushDocsSync }
9827
10085
  );
9828
10086
  }
9829
10087
  }
@@ -9851,7 +10109,7 @@ function githubCommand(program2) {
9851
10109
  );
9852
10110
  if (prUrl && options.syncTasks !== false) {
9853
10111
  const mergedSync = syncTasksPrMetadata(
9854
- path19.join(config.docsDir, paths.tasksPath),
10112
+ path20.join(config.docsDir, paths.tasksPath),
9855
10113
  prUrl,
9856
10114
  "Approved",
9857
10115
  config.lang
@@ -9869,7 +10127,8 @@ function githubCommand(program2) {
9869
10127
  docsGitCwd,
9870
10128
  mergedSync.path,
9871
10129
  message,
9872
- config.lang
10130
+ config.lang,
10131
+ { pushToOrigin: pushDocsSync }
9873
10132
  );
9874
10133
  }
9875
10134
  }
@@ -10056,7 +10315,7 @@ function docsCommand(program2) {
10056
10315
  );
10057
10316
  return;
10058
10317
  }
10059
- const relativeFromCwd = path19.relative(process.cwd(), loaded.entry.absolutePath);
10318
+ const relativeFromCwd = path20.relative(process.cwd(), loaded.entry.absolutePath);
10060
10319
  console.log();
10061
10320
  console.log(chalk6.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
10062
10321
  console.log(
@@ -10134,7 +10393,7 @@ function detectCommand(program2) {
10134
10393
  }
10135
10394
  async function runDetect(options) {
10136
10395
  const cwd = process.cwd();
10137
- const targetCwd = options.dir ? path19.resolve(cwd, options.dir) : cwd;
10396
+ const targetCwd = options.dir ? path20.resolve(cwd, options.dir) : cwd;
10138
10397
  const config = await getConfig(targetCwd);
10139
10398
  const detected = !!config;
10140
10399
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
@@ -10161,7 +10420,7 @@ async function runDetect(options) {
10161
10420
  );
10162
10421
  return;
10163
10422
  }
10164
- const configPath2 = path19.join(config.docsDir, ".lee-spec-kit.json");
10423
+ const configPath2 = path20.join(config.docsDir, ".lee-spec-kit.json");
10165
10424
  const configFilePresent2 = await fs15.pathExists(configPath2);
10166
10425
  const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
10167
10426
  console.log(
@@ -10195,7 +10454,7 @@ async function runDetect(options) {
10195
10454
  console.log();
10196
10455
  return;
10197
10456
  }
10198
- const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
10457
+ const configPath = path20.join(config.docsDir, ".lee-spec-kit.json");
10199
10458
  const configFilePresent = await fs15.pathExists(configPath);
10200
10459
  const detectionSource = configFilePresent ? "config" : "heuristic";
10201
10460
  console.log(chalk6.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
@@ -10290,7 +10549,7 @@ async function countFeatureDirs(docsDir, projectType) {
10290
10549
  ignore: ["**/feature-base/**"]
10291
10550
  });
10292
10551
  return dirs.map((value) => value.replace(/\\/g, "/").replace(/\/+$/, "")).filter((value) => {
10293
- const base = path19.posix.basename(value);
10552
+ const base = path20.posix.basename(value);
10294
10553
  return !!base && base !== "feature-base";
10295
10554
  }).length;
10296
10555
  }
@@ -10302,7 +10561,7 @@ async function hasUserPrdFile(prdDir) {
10302
10561
  absolute: false,
10303
10562
  ignore: ["**/node_modules/**"]
10304
10563
  });
10305
- return files.some((relativePath) => path19.basename(relativePath).toLowerCase() !== "readme.md");
10564
+ return files.some((relativePath) => path20.basename(relativePath).toLowerCase() !== "readme.md");
10306
10565
  }
10307
10566
  function finalizeChecks(checks) {
10308
10567
  const summary = checks.reduce(
@@ -10431,7 +10690,7 @@ async function runOnboardChecks(config) {
10431
10690
  });
10432
10691
  }
10433
10692
  }
10434
- const constitutionPath = path19.join(docsDir, "agents", "constitution.md");
10693
+ const constitutionPath = path20.join(docsDir, "agents", "constitution.md");
10435
10694
  if (!await fs15.pathExists(constitutionPath)) {
10436
10695
  checks.push({
10437
10696
  id: "constitution_exists",
@@ -10469,7 +10728,7 @@ async function runOnboardChecks(config) {
10469
10728
  });
10470
10729
  }
10471
10730
  }
10472
- const customPath = path19.join(docsDir, "agents", "custom.md");
10731
+ const customPath = path20.join(docsDir, "agents", "custom.md");
10473
10732
  if (await fs15.pathExists(customPath)) {
10474
10733
  const content = await fs15.readFile(customPath, "utf-8");
10475
10734
  if (hasTemplateMarkers(content)) {
@@ -10494,7 +10753,7 @@ async function runOnboardChecks(config) {
10494
10753
  });
10495
10754
  }
10496
10755
  }
10497
- const prdDir = path19.join(docsDir, "prd");
10756
+ const prdDir = path20.join(docsDir, "prd");
10498
10757
  const featureCount = await countFeatureDirs(docsDir, config.projectType);
10499
10758
  const prdReady = await hasUserPrdFile(prdDir);
10500
10759
  if (!prdReady) {
@@ -10512,7 +10771,7 @@ async function runOnboardChecks(config) {
10512
10771
  "PRD is empty. If features already exist, fill PRD as soon as possible."
10513
10772
  ),
10514
10773
  path: prdDir,
10515
- suggestedCommand: `touch ${quotePath(path19.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
10774
+ suggestedCommand: `touch ${quotePath(path20.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
10516
10775
  });
10517
10776
  } else {
10518
10777
  checks.push({
@@ -10725,11 +10984,11 @@ ${version}
10725
10984
  }
10726
10985
  return `${ascii}${footer}`;
10727
10986
  }
10728
- var CACHE_FILE = path19.join(os.homedir(), ".lee-spec-kit-version-cache.json");
10987
+ var CACHE_FILE = path20.join(os.homedir(), ".lee-spec-kit-version-cache.json");
10729
10988
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
10730
10989
  function getCurrentVersion() {
10731
10990
  try {
10732
- const packageJsonPath = path19.join(__dirname$1, "..", "package.json");
10991
+ const packageJsonPath = path20.join(__dirname$1, "..", "package.json");
10733
10992
  if (fs15.existsSync(packageJsonPath)) {
10734
10993
  const pkg = fs15.readJsonSync(packageJsonPath);
10735
10994
  return pkg.version;
@@ -10833,7 +11092,7 @@ function shouldCheckForUpdates() {
10833
11092
  if (shouldCheckForUpdates()) checkForUpdates();
10834
11093
  function getCliVersion() {
10835
11094
  try {
10836
- const packageJsonPath = path19.join(__dirname$1, "..", "package.json");
11095
+ const packageJsonPath = path20.join(__dirname$1, "..", "package.json");
10837
11096
  if (fs15.existsSync(packageJsonPath)) {
10838
11097
  const pkg = fs15.readJsonSync(packageJsonPath);
10839
11098
  if (pkg?.version) return String(pkg.version);