lee-spec-kit 0.7.3 → 0.7.4

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 path14 from 'path';
2
+ import path15 from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { program } from 'commander';
5
5
  import fs from 'fs-extra';
@@ -8,11 +8,11 @@ import chalk9 from 'chalk';
8
8
  import { spawn, spawnSync, execFileSync, execSync } from 'child_process';
9
9
  import os from 'os';
10
10
  import { createHash, randomUUID } from 'crypto';
11
- import fs11 from 'fs';
11
+ import fs12 from 'fs';
12
12
  import { Buffer as Buffer$1 } from 'buffer';
13
13
 
14
14
  var getFilename = () => fileURLToPath(import.meta.url);
15
- var getDirname = () => path14.dirname(getFilename());
15
+ var getDirname = () => path15.dirname(getFilename());
16
16
  var __dirname$1 = /* @__PURE__ */ getDirname();
17
17
  async function walkFiles(fsAdapter, rootDir, options = {}) {
18
18
  const out = [];
@@ -25,7 +25,7 @@ async function walkFiles(fsAdapter, rootDir, options = {}) {
25
25
  async function visit(current) {
26
26
  const entries = await fsAdapter.readdir(current);
27
27
  for (const entryName of entries) {
28
- const absolute = path14.join(current, entryName);
28
+ const absolute = path15.join(current, entryName);
29
29
  const stat = await fsAdapter.stat(absolute);
30
30
  if (stat.isDirectory()) {
31
31
  if (ignored.has(entryName.trim().toLowerCase())) continue;
@@ -34,7 +34,7 @@ async function walkFiles(fsAdapter, rootDir, options = {}) {
34
34
  }
35
35
  if (!stat.isFile()) continue;
36
36
  if (normalizedExtensions.size > 0) {
37
- const ext = path14.extname(entryName).toLowerCase();
37
+ const ext = path15.extname(entryName).toLowerCase();
38
38
  if (!normalizedExtensions.has(ext)) continue;
39
39
  }
40
40
  out.push(absolute);
@@ -50,7 +50,7 @@ async function listSubdirectories(fsAdapter, rootDir) {
50
50
  const entries = await fsAdapter.readdir(rootDir);
51
51
  const dirs = [];
52
52
  for (const entryName of entries) {
53
- const absolute = path14.join(rootDir, entryName);
53
+ const absolute = path15.join(rootDir, entryName);
54
54
  const stat = await fsAdapter.stat(absolute);
55
55
  if (stat.isDirectory()) {
56
56
  dirs.push(absolute);
@@ -151,10 +151,10 @@ var DefaultFileSystemAdapter = class {
151
151
  }
152
152
  };
153
153
  var __filename2 = fileURLToPath(import.meta.url);
154
- var __dirname2 = path14.dirname(__filename2);
154
+ var __dirname2 = path15.dirname(__filename2);
155
155
  function getTemplatesDir() {
156
- const rootDir = path14.resolve(__dirname2, "..");
157
- return path14.join(rootDir, "templates");
156
+ const rootDir = path15.resolve(__dirname2, "..");
157
+ return path15.join(rootDir, "templates");
158
158
  }
159
159
 
160
160
  // src/utils/locales/ko/cli.ts
@@ -259,6 +259,7 @@ var koCli = {
259
259
  "init.log.nextSteps1": " 1. {docsDir}/prd/README.md \uC791\uC131",
260
260
  "init.log.nextSteps2": " 2. npx lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00",
261
261
  "init.log.nextSteps3": " 3. npx lee-spec-kit onboard --strict \uB85C \uCD08\uAE30 \uC124\uC815 \uC810\uAC80",
262
+ "init.log.nextSteps4": " 4. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 AGENTS.md \uC5C6\uC774 Codex\uB97C \uC4F4\uB2E4\uBA74 bootstrap \uC124\uCE58: npx lee-spec-kit setup codex-bootstrap",
262
263
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0, docs \uCEE4\uBC0B \uC911...",
263
264
  "init.log.gitInit": "\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911...",
264
265
  "init.warn.stagedChangesSkip": '\u26A0\uFE0F \uD604\uC7AC Git index\uC5D0 \uC774\uBBF8 stage\uB41C \uBCC0\uACBD\uC774 \uC788\uC2B5\uB2C8\uB2E4. (--dir "." \uC778 \uACBD\uC6B0 \uCEE4\uBC0B \uBC94\uC704\uB97C \uC548\uC804\uD558\uAC8C \uC81C\uD55C\uD560 \uC218 \uC5C6\uC5B4 \uC790\uB3D9 \uCEE4\uBC0B\uC744 \uAC74\uB108\uB701\uB2C8\uB2E4)',
@@ -277,6 +278,10 @@ var koCli = {
277
278
  "idea.nextSteps1": " 1. \uBC94\uC704, PRD Refs, \uC2B9\uACA9 \uBA54\uBAA8\uB97C \uC791\uC131",
278
279
  "idea.nextSteps2": " 2. Feature\uB85C \uC2B9\uACA9: npx lee-spec-kit feature <name> --idea {ideaId}",
279
280
  "idea.nextSteps3": " 3. Feature\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uC744 \uACBD\uC6B0 Dropped\uB85C \uD45C\uC2DC",
281
+ "setup.codexBootstrapInstalled": "\u2705 Codex bootstrap \uC124\uCE58 \uC644\uB8CC: {path}",
282
+ "setup.codexBootstrapAlreadyInstalled": "\u2705 Codex bootstrap \uC774 \uC774\uBBF8 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: {path}",
283
+ "setup.codexBootstrapRemoved": "\u2705 Codex bootstrap \uC81C\uAC70 \uC644\uB8CC: {path}",
284
+ "setup.codexBootstrapAlreadyAbsent": "\u2705 Codex bootstrap \uC774 \uC774\uBBF8 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
280
285
  "github.cmdGithubDescription": "GitHub \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uB3C4\uC6B0\uBBF8 (issue/pr \uBCF8\uBB38 \uD15C\uD50C\uB9BF \uC0DD\uC131, \uAC80\uC99D, merge \uC7AC\uC2DC\uB3C4)",
281
286
  "github.cmdIssueDescription": "feature \uBB38\uC11C \uAE30\uBC18 GitHub issue \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131",
282
287
  "github.cmdPrDescription": "GitHub PR \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131 + tasks \uB3D9\uAE30\uD654 + merge \uC7AC\uC2DC\uB3C4",
@@ -304,8 +309,11 @@ var koCli = {
304
309
  "github.labelsRequired": "\uCD5C\uC18C 1\uAC1C \uB77C\uBCA8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. `--labels enhancement`\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
305
310
  "github.approvalRequired": "{operation}\uC740(\uB294) \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uC2B9\uC778 \uD6C4\uC5D0\uB9CC \uC2E4\uD589\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uD68D \uACF5\uC720 \uD6C4 `--confirm OK`\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
306
311
  "github.ghCommandFailed": "GitHub CLI \uBA85\uB839 \uC2E4\uD589\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
312
+ "github.issueLookupFailed": "GitHub issue \uC874\uC7AC \uC5EC\uBD80 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
307
313
  "github.ghEmptyJson": "GitHub CLI JSON \uCD9C\uB825\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
308
314
  "github.ghInvalidJson": "GitHub CLI JSON \uD30C\uC2F1\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: {snippet}",
315
+ "github.invalidIssueReference": "Issue \uD544\uB4DC\uAC00 \uC62C\uBC14\uB978 GitHub issue reference \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4: {value}. `#123` \uAC19\uC740 \uC2E4\uC81C issue \uBC88\uD638\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
316
+ "github.issueNotFound": "GitHub issue {issue} \uB97C \uD604\uC7AC repository context\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
309
317
  "github.sectionsMissing": "{kind} \uBCF8\uBB38\uC5D0 \uD544\uC218 \uC139\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {sections}",
310
318
  "github.todoPlaceholdersRemain": "{kind} \uBCF8\uBB38\uC5D0 TODO \uD56D\uBAA9\uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uBAA9\uD45C/\uC644\uB8CC \uAE30\uC900 \uB4F1\uC744 \uCC44\uC6B4 \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
311
319
  "github.artifactModeInvalid": "`--{kind}` \uAC12\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {value}. \uD5C8\uC6A9\uAC12: auto,on,off",
@@ -538,12 +546,12 @@ var koContext = {
538
546
  "context.actionDetail.featureScopeSplitKeep": "\uBD84\uD560 \uAC00\uC774\uB4DC\uB97C \uD655\uC778\uD55C \uB4A4 \uD604\uC7AC \uC774\uC288 \uBC94\uC704\uB97C \uC720\uC9C0\uD558\uACE0 \uC9C4\uD589\uD558\uC138\uC694",
539
547
  "context.actionDetail.featureScopeSplitTwo": "\uACB0\uD569\uB3C4/\uD30C\uC77C\uACB9\uCE68/\uD14C\uC2A4\uD2B8/\uBC30\uD3EC \uAE30\uC900\uC73C\uB85C 2\uAC1C \uC774\uC288\uB85C \uBD84\uB9AC\uD558\uC138\uC694",
540
548
  "context.actionDetail.featureScopeSplitFour": "\uAE30\uC900 \uAE30\uBC18\uC73C\uB85C 4\uAC1C \uC774\uC288\uB85C \uBD84\uB9AC\uD558\uACE0 \uC758\uC874 \uC21C\uC11C\uB300\uB85C PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
541
- "context.actionDetail.worktreeCleanup": "\uC644\uB8CC\uB41C feature worktree\uB97C \uC815\uB9AC\uD558\uC138\uC694",
549
+ "context.actionDetail.worktreeCleanup": "feature\uB97C \uB05D\uB0B4\uB824\uBA74 worktree\uB97C \uC815\uB9AC\uD558\uC138\uC694",
542
550
  "context.actionDetail.prMetadataMigrate": "tasks.md\uC758 PR \uD56D\uBAA9 \uD615\uC2DD\uC744 \uCD5C\uC2E0 \uD15C\uD50C\uB9BF\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
543
551
  "context.actionDetail.prMetadataMigratePrFields": "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
544
552
  "context.actionDetail.prMetadataMigratePrePrReviewField": "tasks.md\uC5D0 PR \uC804 \uB9AC\uBDF0 \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
545
553
  "context.actionDetail.userRequestReplan": "\uC0C8 \uC0AC\uC6A9\uC790 \uC694\uAD6C\uB97C \uBA3C\uC800 \uBC18\uC601\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
546
- "context.actionDetail.featureDone": "\uC774 Feature\uC758 \uC644\uB8CC \uC870\uAC74\uC774 \uBAA8\uB450 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
554
+ "context.actionDetail.featureDone": "\uC774 Feature\uAC00 \uC644\uC804\uD788 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
547
555
  "context.actionDetail.fallback": "\uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
548
556
  "context.suggestion.createFeature": "\uC0C8 Feature\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4",
549
557
  "context.suggestion.runOnboard": "\uCD08\uAE30 \uC124\uC815 \uC810\uAC80(onboard)\uC744 \uC2E4\uD589\uD569\uB2C8\uB2E4",
@@ -557,6 +565,7 @@ var koContext = {
557
565
  "context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59\uC740 git-workflow \uAC00\uC774\uB4DC\uB97C \uAE30\uC900\uC73C\uB85C \uD655\uC778\uD558\uC138\uC694.",
558
566
  "context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
559
567
  "context.list.projectCommitNeeded": "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694",
568
+ "context.list.cleanupPending": "feature \uC885\uB8CC\uB97C \uC704\uD574 worktree \uC815\uB9AC",
560
569
  "context.list.issueNumberNeeded": "\uC774\uC288 \uBC88\uD638 \uAE30\uB85D \uD544\uC694",
561
570
  "context.list.addPrMetadata": "PR \uBA54\uD0C0\uB370\uC774\uD130(PR/PR \uC0C1\uD0DC) \uCD94\uAC00",
562
571
  "context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
@@ -594,7 +603,7 @@ var koSteps = {
594
603
  prePrReview: "Pre-PR \uB9AC\uBDF0",
595
604
  prCreate: "PR \uC0DD\uC131",
596
605
  codeReview: "\uCF54\uB4DC \uB9AC\uBDF0",
597
- featureDone: "Feature \uC644\uB8CC"
606
+ featureDone: "Feature \uB9C8\uBB34\uB9AC"
598
607
  };
599
608
 
600
609
  // src/utils/locales/ko/messages.ts
@@ -674,7 +683,7 @@ var koMessages = {
674
683
  featureScopeSplitTwo: "Feature \uBC94\uC704\uAC00 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}) \uAD8C\uC7A5 \uADDC\uCE59\uC0C1 40~79 \uD0DC\uC2A4\uD06C\uC774\uBA74\uC11C \uD558\uB4DC \uAE30\uC900 \uBBF8\uB9CC\uC774\uBA74 2\uAC1C \uC774\uC288 \uBD84\uD560\uC774 \uAE30\uBCF8\uC785\uB2C8\uB2E4. `{guideCommand}`\uB97C \uB530\uB77C \uACB0\uD569\uB3C4/\uD30C\uC77C\uACB9\uCE68/\uD14C\uC2A4\uD2B8/\uBC30\uD3EC \uAE30\uC900\uC73C\uB85C \uBD84\uD560\uD558\uC138\uC694. \uAC01 \uC774\uC288\uC5D0\uB294 \uB2E4\uC74C \uD15C\uD50C\uB9BF\uC744 \uAE30\uB85D\uD558\uC138\uC694: \uBAA9\uD45C, \uD3EC\uD568 \uBC94\uC704, \uC81C\uC678 \uBC94\uC704, \uC120\uD589 \uC758\uC874\uC131, PR \uC644\uB8CC \uAE30\uC900.",
675
684
  featureScopeSplitFour: "Feature \uBC94\uC704\uAC00 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}) tasks >= {recommendFourTaskThreshold} \uB610\uB294 decisions \uC904\uC218 >= {recommendFourDecisionsThreshold}\uC774\uBA74 4\uAC1C \uC774\uC288 \uBD84\uD560\uC744 \uAC15\uD558\uAC8C \uAD8C\uC7A5\uD569\uB2C8\uB2E4. `{guideCommand}`\uB97C \uB530\uB77C 4\uAC1C\uC758 \uC5F0\uAD00 \uC774\uC288\uB85C \uBD84\uB9AC\uD558\uACE0 \uC758\uC874 \uC21C\uC11C\uB97C \uBA85\uC2DC\uD55C \uB4A4 PR\uC744 \uC21C\uCC28 \uBA38\uC9C0\uD558\uC138\uC694. \uAC01 \uC774\uC288 \uD15C\uD50C\uB9BF: \uBAA9\uD45C, \uD3EC\uD568 \uBC94\uC704, \uC81C\uC678 \uBC94\uC704, \uC120\uD589 \uC758\uC874\uC131, PR \uC644\uB8CC \uAE30\uC900.",
676
685
  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.",
677
- 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.",
686
+ featureDone: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC694\uAD6C\uC0AC\uD56D\uACFC \uB85C\uCEEC \uC815\uB9AC\uAE4C\uC9C0 \uBAA8\uB450 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uC804\uD788 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
678
687
  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."
679
688
  };
680
689
 
@@ -828,6 +837,7 @@ var enCli = {
828
837
  "init.log.nextSteps1": " 1. Write {docsDir}/prd/README.md",
829
838
  "init.log.nextSteps2": " 2. Add a feature with: npx lee-spec-kit feature <name>",
830
839
  "init.log.nextSteps3": " 3. Run setup checks: npx lee-spec-kit onboard --strict",
840
+ "init.log.nextSteps4": " 4. If you use Codex without repo-root AGENTS.md, install bootstrap: npx lee-spec-kit setup codex-bootstrap",
831
841
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git repo detected, committing docs...",
832
842
  "init.log.gitInit": "\u{1F4E6} Initializing Git...",
833
843
  "init.warn.stagedChangesSkip": '\u26A0\uFE0F There are already staged changes in the Git index. (With --dir ".", commit scope cannot be safely restricted, so auto-commit is skipped.)',
@@ -846,6 +856,10 @@ var enCli = {
846
856
  "idea.nextSteps1": " 1. Fill scope, PRD refs, and promotion notes",
847
857
  "idea.nextSteps2": " 2. Promote it with: npx lee-spec-kit feature <name> --idea {ideaId}",
848
858
  "idea.nextSteps3": " 3. Mark it dropped if it should not become a feature",
859
+ "setup.codexBootstrapInstalled": "\u2705 Codex bootstrap installed: {path}",
860
+ "setup.codexBootstrapAlreadyInstalled": "\u2705 Codex bootstrap already installed: {path}",
861
+ "setup.codexBootstrapRemoved": "\u2705 Codex bootstrap removed: {path}",
862
+ "setup.codexBootstrapAlreadyAbsent": "\u2705 Codex bootstrap is already absent: {path}",
849
863
  "github.cmdGithubDescription": "GitHub workflow helpers (issue/pr templates, validation, merge retry)",
850
864
  "github.cmdIssueDescription": "Generate/create GitHub issue body from feature docs with validation",
851
865
  "github.cmdPrDescription": "Generate/create GitHub PR body with validation, tasks PR sync, and merge retry",
@@ -873,8 +887,11 @@ var enCli = {
873
887
  "github.labelsRequired": "At least one label is required. Use `--labels enhancement`.",
874
888
  "github.approvalRequired": "{operation} requires explicit user approval. Re-run with `--confirm OK` after sharing the plan with the user.",
875
889
  "github.ghCommandFailed": "GitHub CLI command failed",
890
+ "github.issueLookupFailed": "Failed to verify GitHub issue",
876
891
  "github.ghEmptyJson": "GitHub CLI returned empty JSON output.",
877
892
  "github.ghInvalidJson": "GitHub CLI returned invalid JSON: {snippet}",
893
+ "github.invalidIssueReference": "Issue field is not a valid GitHub issue reference: {value}. Use a real issue number such as `#123`.",
894
+ "github.issueNotFound": "GitHub issue {issue} was not found or is not accessible from the current repository context.",
878
895
  "github.sectionsMissing": "{kind} body is missing required sections: {sections}",
879
896
  "github.todoPlaceholdersRemain": "{kind} body still contains TODO placeholders. Fill goals/completion criteria before creating remotely.",
880
897
  "github.artifactModeInvalid": "Invalid value for `--{kind}`: {value}. Allowed: auto,on,off",
@@ -1107,12 +1124,12 @@ var enContext = {
1107
1124
  "context.actionDetail.featureScopeSplitKeep": "Keep current issue scope and continue (after split-guide check)",
1108
1125
  "context.actionDetail.featureScopeSplitTwo": "Split into 2 linked issues using coupling/file-overlap/test/deploy criteria",
1109
1126
  "context.actionDetail.featureScopeSplitFour": "Split into 4 linked issues (criteria-based) and merge PRs in dependency order",
1110
- "context.actionDetail.worktreeCleanup": "Clean up the completed feature worktree",
1127
+ "context.actionDetail.worktreeCleanup": "Clean up the feature worktree to finish this feature",
1111
1128
  "context.actionDetail.prMetadataMigrate": "Update tasks.md PR fields to the latest template format",
1112
1129
  "context.actionDetail.prMetadataMigratePrFields": "Update tasks.md with PR/PR Status fields",
1113
1130
  "context.actionDetail.prMetadataMigratePrePrReviewField": "Add Pre-PR Review field in tasks.md",
1114
1131
  "context.actionDetail.userRequestReplan": "Handle the new user request first and re-run context",
1115
- "context.actionDetail.featureDone": "All completion checks are satisfied for this feature",
1132
+ "context.actionDetail.featureDone": "This feature is fully complete",
1116
1133
  "context.actionDetail.fallback": "Verify current status and re-run context",
1117
1134
  "context.suggestion.createFeature": "Create a new feature",
1118
1135
  "context.suggestion.runOnboard": "Run onboarding checks",
@@ -1126,6 +1143,7 @@ var enContext = {
1126
1143
  "context.tipDocsCommitRules": "Check commit message rules against the git-workflow guide.",
1127
1144
  "context.list.docsCommitNeeded": "Commit docs changes",
1128
1145
  "context.list.projectCommitNeeded": "Commit project code changes",
1146
+ "context.list.cleanupPending": "Clean up the feature worktree to finish",
1129
1147
  "context.list.issueNumberNeeded": "Fill issue number in docs",
1130
1148
  "context.list.addPrMetadata": "Add PR metadata (PR/PR Status)",
1131
1149
  "context.list.recordPrLink": "Record PR link",
@@ -1163,7 +1181,7 @@ var enSteps = {
1163
1181
  prePrReview: "Pre-PR review",
1164
1182
  prCreate: "Create PR",
1165
1183
  codeReview: "Code review",
1166
- featureDone: "Feature done"
1184
+ featureDone: "Finalize feature"
1167
1185
  };
1168
1186
 
1169
1187
  // src/utils/locales/en/messages.ts
@@ -1243,7 +1261,7 @@ var enMessages = {
1243
1261
  featureScopeSplitTwo: "Feature scope is large (tasks: {taskCount}, decisions lines: {decisionsLineCount}). Recommendation rule: 40~79 tasks and below the hard threshold usually maps to 2 issues. Follow `{guideCommand}` and split by coupling/file-overlap/test/deploy criteria. For each new issue, document this template: Goal, Included Scope, Excluded Scope, Depends On, PR Done Criteria.",
1244
1262
  featureScopeSplitFour: "Feature scope is large (tasks: {taskCount}, decisions lines: {decisionsLineCount}). Hard recommendation is 4 issues when tasks >= {recommendFourTaskThreshold} or decisions lines >= {recommendFourDecisionsThreshold}. Follow `{guideCommand}` and split into 4 linked issues with explicit dependency order and sequential PR merges. Use the per-issue template: Goal, Included Scope, Excluded Scope, Depends On, PR Done Criteria.",
1245
1263
  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.",
1246
- featureDone: "Workflow requirements and all tasks/completion criteria are satisfied. This feature is done.",
1264
+ featureDone: "All workflow requirements and local cleanup are complete. This feature is fully finished.",
1247
1265
  fallbackRerunContext: "Cannot determine status. Check the docs and run context again."
1248
1266
  };
1249
1267
 
@@ -1741,10 +1759,10 @@ var DEFAULT_STALE_MS = 2 * 6e4;
1741
1759
  var RUNTIME_GIT_DIRNAME = "lee-spec-kit.runtime";
1742
1760
  var RUNTIME_TEMP_DIRNAME = "lee-spec-kit-runtime";
1743
1761
  function toScopeKey(value) {
1744
- return createHash("sha1").update(path14.resolve(value)).digest("hex").slice(0, 16);
1762
+ return createHash("sha1").update(path15.resolve(value)).digest("hex").slice(0, 16);
1745
1763
  }
1746
1764
  function getTempRuntimeDir(scopePath) {
1747
- return path14.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1765
+ return path15.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1748
1766
  }
1749
1767
  function resolveGitRuntimeDir(cwd) {
1750
1768
  try {
@@ -1758,38 +1776,38 @@ function resolveGitRuntimeDir(cwd) {
1758
1776
  }
1759
1777
  ).trim();
1760
1778
  if (!out) return null;
1761
- return path14.isAbsolute(out) ? out : path14.resolve(cwd, out);
1779
+ return path15.isAbsolute(out) ? out : path15.resolve(cwd, out);
1762
1780
  } catch {
1763
1781
  return null;
1764
1782
  }
1765
1783
  }
1766
1784
  function getRuntimeStateDir(cwd) {
1767
- const resolved = path14.resolve(cwd);
1785
+ const resolved = path15.resolve(cwd);
1768
1786
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1769
1787
  }
1770
1788
  function getDocsLockPath(docsDir) {
1771
- return path14.join(
1789
+ return path15.join(
1772
1790
  getRuntimeStateDir(docsDir),
1773
1791
  "locks",
1774
1792
  `docs-${toScopeKey(docsDir)}.lock`
1775
1793
  );
1776
1794
  }
1777
1795
  function getInitLockPath(targetDir) {
1778
- return path14.join(
1779
- getRuntimeStateDir(path14.dirname(path14.resolve(targetDir))),
1796
+ return path15.join(
1797
+ getRuntimeStateDir(path15.dirname(path15.resolve(targetDir))),
1780
1798
  "locks",
1781
1799
  `init-${toScopeKey(targetDir)}.lock`
1782
1800
  );
1783
1801
  }
1784
1802
  function getApprovalTicketStorePath(docsDir) {
1785
- return path14.join(
1803
+ return path15.join(
1786
1804
  getRuntimeStateDir(docsDir),
1787
1805
  "tickets",
1788
1806
  `approval-${toScopeKey(docsDir)}.json`
1789
1807
  );
1790
1808
  }
1791
1809
  function getProjectExecutionLockPath(cwd) {
1792
- return path14.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1810
+ return path15.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1793
1811
  }
1794
1812
  async function isStaleLock(lockPath, staleMs) {
1795
1813
  try {
@@ -1830,7 +1848,7 @@ function isProcessAlive(pid) {
1830
1848
  }
1831
1849
  }
1832
1850
  async function tryAcquire(lockPath, owner) {
1833
- await fs.ensureDir(path14.dirname(lockPath));
1851
+ await fs.ensureDir(path15.dirname(lockPath));
1834
1852
  try {
1835
1853
  const fd = await fs.open(lockPath, "wx");
1836
1854
  const payload = JSON.stringify(
@@ -1907,30 +1925,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
1907
1925
  "pr-template.md"
1908
1926
  ];
1909
1927
  var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
1910
- var ENGINE_MANAGED_FEATURE_PATH = path14.join(
1928
+ var ENGINE_MANAGED_FEATURE_PATH = path15.join(
1911
1929
  "features",
1912
1930
  "feature-base"
1913
1931
  );
1914
1932
  async function pruneEngineManagedDocs(docsDir) {
1915
1933
  const removed = [];
1916
1934
  for (const file of ENGINE_MANAGED_AGENT_FILES) {
1917
- const target = path14.join(docsDir, "agents", file);
1935
+ const target = path15.join(docsDir, "agents", file);
1918
1936
  if (await fs.pathExists(target)) {
1919
1937
  await fs.remove(target);
1920
- removed.push(path14.relative(docsDir, target));
1938
+ removed.push(path15.relative(docsDir, target));
1921
1939
  }
1922
1940
  }
1923
1941
  for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
1924
- const target = path14.join(docsDir, "agents", dir);
1942
+ const target = path15.join(docsDir, "agents", dir);
1925
1943
  if (await fs.pathExists(target)) {
1926
1944
  await fs.remove(target);
1927
- removed.push(path14.relative(docsDir, target));
1945
+ removed.push(path15.relative(docsDir, target));
1928
1946
  }
1929
1947
  }
1930
- const featureBasePath = path14.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1948
+ const featureBasePath = path15.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1931
1949
  if (await fs.pathExists(featureBasePath)) {
1932
1950
  await fs.remove(featureBasePath);
1933
- removed.push(path14.relative(docsDir, featureBasePath));
1951
+ removed.push(path15.relative(docsDir, featureBasePath));
1934
1952
  }
1935
1953
  return removed;
1936
1954
  }
@@ -2081,6 +2099,101 @@ async function upsertLeeSpecKitAgentsMd(filePath, options) {
2081
2099
  await fs.writeFile(filePath, next, "utf-8");
2082
2100
  return { changed: true, action: "appended" };
2083
2101
  }
2102
+ var LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN = "# lee-spec-kit:codex-bootstrap:begin";
2103
+ var LEE_SPEC_KIT_CODEX_BOOTSTRAP_END = "# lee-spec-kit:codex-bootstrap:end";
2104
+ var REQUIRED_FALLBACK = "docs/AGENTS.md";
2105
+ var REQUIRED_COMPACT_LINES = [
2106
+ "Preserve any instructions loaded from ./docs/AGENTS.md in the compacted summary.",
2107
+ "After context compression/reset, read ./docs/AGENTS.md again before resuming project-specific work."
2108
+ ];
2109
+ function renderManagedSegment2() {
2110
+ return [
2111
+ LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN,
2112
+ `project_doc_fallback_filenames = ["${REQUIRED_FALLBACK}"]`,
2113
+ 'compact_prompt = """',
2114
+ ...REQUIRED_COMPACT_LINES,
2115
+ '"""',
2116
+ LEE_SPEC_KIT_CODEX_BOOTSTRAP_END
2117
+ ].join("\n");
2118
+ }
2119
+ function renderManagedBlock2() {
2120
+ return `${renderManagedSegment2()}
2121
+
2122
+ `;
2123
+ }
2124
+ function getCodexHome() {
2125
+ const explicit = String(process.env.CODEX_HOME || "").trim();
2126
+ if (explicit) return explicit;
2127
+ return path15.join(os.homedir(), ".codex");
2128
+ }
2129
+ function getCodexConfigPath() {
2130
+ return path15.join(getCodexHome(), "config.toml");
2131
+ }
2132
+ function contentIncludesRequiredBootstrap(content) {
2133
+ return content.includes(REQUIRED_FALLBACK) && REQUIRED_COMPACT_LINES.every((line) => content.includes(line));
2134
+ }
2135
+ function hasConflictingTopLevelKey(content, key) {
2136
+ const keyPattern = new RegExp(`^\\s*${key}\\s*=`, "m");
2137
+ return keyPattern.test(content);
2138
+ }
2139
+ async function hasLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
2140
+ if (!await fs.pathExists(filePath)) return false;
2141
+ const content = await fs.readFile(filePath, "utf-8");
2142
+ return content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN) && content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END) || contentIncludesRequiredBootstrap(content);
2143
+ }
2144
+ async function upsertLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
2145
+ const block = renderManagedBlock2();
2146
+ const segment = renderManagedSegment2();
2147
+ await fs.ensureDir(path15.dirname(filePath));
2148
+ const exists = await fs.pathExists(filePath);
2149
+ if (!exists) {
2150
+ await fs.writeFile(filePath, block, "utf-8");
2151
+ return { changed: true, action: "created", filePath };
2152
+ }
2153
+ const current = await fs.readFile(filePath, "utf-8");
2154
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);
2155
+ const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);
2156
+ if (beginIndex !== -1 && endIndex !== -1 && beginIndex <= endIndex) {
2157
+ const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
2158
+ const next2 = `${current.slice(0, beginIndex)}${segment}${current.slice(replaceEnd)}`;
2159
+ if (next2 === current) {
2160
+ return { changed: false, action: "noop", filePath };
2161
+ }
2162
+ await fs.writeFile(filePath, next2, "utf-8");
2163
+ return { changed: true, action: "updated", filePath };
2164
+ }
2165
+ if (hasConflictingTopLevelKey(current, "project_doc_fallback_filenames") || hasConflictingTopLevelKey(current, "compact_prompt")) {
2166
+ if (contentIncludesRequiredBootstrap(current)) {
2167
+ return { changed: false, action: "noop", filePath };
2168
+ }
2169
+ throw new Error(
2170
+ `Codex config already defines project_doc_fallback_filenames or compact_prompt outside lee-spec-kit managed block: ${filePath}`
2171
+ );
2172
+ }
2173
+ let next = current;
2174
+ if (next.length > 0 && !next.endsWith("\n")) next += "\n";
2175
+ if (next.trim().length > 0 && !next.endsWith("\n\n")) next += "\n";
2176
+ next += block;
2177
+ await fs.writeFile(filePath, next, "utf-8");
2178
+ return { changed: true, action: "appended", filePath };
2179
+ }
2180
+ async function removeLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
2181
+ if (!await fs.pathExists(filePath)) {
2182
+ return { changed: false, filePath };
2183
+ }
2184
+ const current = await fs.readFile(filePath, "utf-8");
2185
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);
2186
+ const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);
2187
+ if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {
2188
+ return { changed: false, filePath };
2189
+ }
2190
+ const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
2191
+ let next = `${current.slice(0, beginIndex)}${current.slice(replaceEnd)}`;
2192
+ next = next.replace(/\n{3,}/g, "\n\n").trimEnd();
2193
+ if (next.length > 0) next += "\n";
2194
+ await fs.writeFile(filePath, next, "utf-8");
2195
+ return { changed: true, filePath };
2196
+ }
2084
2197
 
2085
2198
  // src/utils/init/options.ts
2086
2199
  function parseStandaloneMultiProjectRootJson(raw) {
@@ -2229,7 +2342,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
2229
2342
  }
2230
2343
  async function runInit(options) {
2231
2344
  const cwd = process.cwd();
2232
- const defaultName = path14.basename(cwd);
2345
+ const defaultName = path15.basename(cwd);
2233
2346
  let projectName = options.name || defaultName;
2234
2347
  let projectType = options.type;
2235
2348
  let components = parseComponentsOption(options.components);
@@ -2240,7 +2353,7 @@ async function runInit(options) {
2240
2353
  let docsRemote = options.docsRemote;
2241
2354
  let projectRoot;
2242
2355
  const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
2243
- const targetDir = path14.resolve(cwd, options.dir || "./docs");
2356
+ const targetDir = path15.resolve(cwd, options.dir || "./docs");
2244
2357
  const skipPrompts = !!options.yes || !!options.nonInteractive;
2245
2358
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
2246
2359
  throw createCliError(
@@ -2637,7 +2750,7 @@ async function runInit(options) {
2637
2750
  );
2638
2751
  console.log();
2639
2752
  const templatesDir = getTemplatesDir();
2640
- const commonPath = path14.join(templatesDir, lang, "common");
2753
+ const commonPath = path15.join(templatesDir, lang, "common");
2641
2754
  if (!await fs.pathExists(commonPath)) {
2642
2755
  throw new Error(
2643
2756
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
@@ -2646,11 +2759,11 @@ async function runInit(options) {
2646
2759
  const fsAdapter = new DefaultFileSystemAdapter();
2647
2760
  await copyTemplates(fsAdapter, commonPath, targetDir);
2648
2761
  if (projectType === "multi") {
2649
- const featuresRoot = path14.join(targetDir, "features");
2762
+ const featuresRoot = path15.join(targetDir, "features");
2650
2763
  for (const component of components) {
2651
- const componentDir = path14.join(featuresRoot, component);
2764
+ const componentDir = path15.join(featuresRoot, component);
2652
2765
  await fs.ensureDir(componentDir);
2653
- const readmePath = path14.join(componentDir, "README.md");
2766
+ const readmePath = path15.join(componentDir, "README.md");
2654
2767
  if (!await fs.pathExists(readmePath)) {
2655
2768
  await fs.writeFile(
2656
2769
  readmePath,
@@ -2711,20 +2824,20 @@ async function runInit(options) {
2711
2824
  config.projectRoot = projectRoot;
2712
2825
  }
2713
2826
  }
2714
- const configPath = path14.join(targetDir, ".lee-spec-kit.json");
2827
+ const configPath = path15.join(targetDir, ".lee-spec-kit.json");
2715
2828
  await fs.writeJson(configPath, config, { spaces: 2 });
2716
2829
  const extraCommitPathsAbs = [];
2717
2830
  try {
2718
2831
  if (docsRepo === "embedded") {
2719
2832
  const repoRoot = getGitTopLevelOrNull(cwd) || cwd;
2720
- const agentsMdPath = path14.join(repoRoot, "AGENTS.md");
2833
+ const agentsMdPath = path15.join(repoRoot, "AGENTS.md");
2721
2834
  const result = await upsertLeeSpecKitAgentsMd(agentsMdPath, {
2722
2835
  lang,
2723
2836
  docsRepo
2724
2837
  });
2725
2838
  if (result.changed) extraCommitPathsAbs.push(agentsMdPath);
2726
2839
  } else {
2727
- await upsertLeeSpecKitAgentsMd(path14.join(targetDir, "AGENTS.md"), {
2840
+ await upsertLeeSpecKitAgentsMd(path15.join(targetDir, "AGENTS.md"), {
2728
2841
  lang,
2729
2842
  docsRepo
2730
2843
  });
@@ -2734,16 +2847,16 @@ async function runInit(options) {
2734
2847
  } else if (projectRoot && typeof projectRoot === "object") {
2735
2848
  roots.push(...Object.values(projectRoot));
2736
2849
  }
2737
- const resolvedCwd = path14.resolve(cwd);
2850
+ const resolvedCwd = path15.resolve(cwd);
2738
2851
  for (const raw of roots) {
2739
2852
  const value = String(raw || "").trim();
2740
2853
  if (!value) continue;
2741
- const abs = path14.resolve(cwd, value);
2742
- if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path14.sep}`)) {
2854
+ const abs = path15.resolve(cwd, value);
2855
+ if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path15.sep}`)) {
2743
2856
  if (await fs.pathExists(abs)) {
2744
2857
  const stat = await fs.stat(abs);
2745
2858
  if (stat.isDirectory()) {
2746
- await upsertLeeSpecKitAgentsMd(path14.join(abs, "AGENTS.md"), {
2859
+ await upsertLeeSpecKitAgentsMd(path15.join(abs, "AGENTS.md"), {
2747
2860
  lang,
2748
2861
  docsRepo
2749
2862
  });
@@ -2773,6 +2886,9 @@ async function runInit(options) {
2773
2886
  );
2774
2887
  console.log(chalk9.gray(tr(lang, "cli", "init.log.nextSteps2")));
2775
2888
  console.log(chalk9.gray(tr(lang, "cli", "init.log.nextSteps3")));
2889
+ if (!await hasLeeSpecKitCodexBootstrap()) {
2890
+ console.log(chalk9.gray(tr(lang, "cli", "init.log.nextSteps4")));
2891
+ }
2776
2892
  console.log();
2777
2893
  },
2778
2894
  { owner: "init" }
@@ -2833,7 +2949,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2833
2949
  console.log(chalk9.blue(tr(lang, "cli", "init.log.gitInit")));
2834
2950
  runGitOrThrow(["init"], gitWorkdir);
2835
2951
  }
2836
- const relativePath = docsRepo === "standalone" ? "." : path14.relative(gitWorkdir, targetDir);
2952
+ const relativePath = docsRepo === "standalone" ? "." : path15.relative(gitWorkdir, targetDir);
2837
2953
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2838
2954
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2839
2955
  console.log(chalk9.yellow(tr(lang, "cli", "init.warn.stagedChangesSkip")));
@@ -2860,7 +2976,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2860
2976
  console.log();
2861
2977
  return;
2862
2978
  }
2863
- const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path14.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
2979
+ const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path15.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
2864
2980
  const pathsToStage = [relativePath, ...extraRelativePaths];
2865
2981
  for (const p of pathsToStage) {
2866
2982
  runGitOrThrow(["add", p], gitWorkdir);
@@ -2896,17 +3012,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
2896
3012
  }
2897
3013
  function getAncestorDirs(startDir) {
2898
3014
  const dirs = [];
2899
- let current = path14.resolve(startDir);
3015
+ let current = path15.resolve(startDir);
2900
3016
  while (true) {
2901
3017
  dirs.push(current);
2902
- const parent = path14.dirname(current);
3018
+ const parent = path15.dirname(current);
2903
3019
  if (parent === current) break;
2904
3020
  current = parent;
2905
3021
  }
2906
3022
  return dirs;
2907
3023
  }
2908
3024
  function hasWorkspaceBoundary(dir) {
2909
- return fs.existsSync(path14.join(dir, "package.json")) || fs.existsSync(path14.join(dir, ".git"));
3025
+ return fs.existsSync(path15.join(dir, "package.json")) || fs.existsSync(path15.join(dir, ".git"));
2910
3026
  }
2911
3027
  function getSearchBaseDirs(cwd) {
2912
3028
  const ancestors = getAncestorDirs(cwd);
@@ -2922,7 +3038,7 @@ function normalizeComponentKeys(value) {
2922
3038
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2923
3039
  }
2924
3040
  async function inferComponentsFromFeaturesDir(docsDir) {
2925
- const featuresPath = path14.join(docsDir, "features");
3041
+ const featuresPath = path15.join(docsDir, "features");
2926
3042
  if (!await fs.pathExists(featuresPath)) return [];
2927
3043
  const entries = await fs.readdir(featuresPath, { withFileTypes: true });
2928
3044
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
@@ -2933,21 +3049,21 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2933
3049
  async function getConfig(cwd) {
2934
3050
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2935
3051
  const baseDirs = [
2936
- ...explicitDocsDir ? [path14.resolve(explicitDocsDir)] : [],
3052
+ ...explicitDocsDir ? [path15.resolve(explicitDocsDir)] : [],
2937
3053
  ...getSearchBaseDirs(cwd)
2938
3054
  ];
2939
3055
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2940
3056
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2941
3057
  for (const baseDir of baseDirs) {
2942
- const resolvedBaseDir = path14.resolve(baseDir);
3058
+ const resolvedBaseDir = path15.resolve(baseDir);
2943
3059
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2944
3060
  visitedBaseDirs.add(resolvedBaseDir);
2945
- const possibleDocsDirs = [path14.join(resolvedBaseDir, "docs"), resolvedBaseDir];
3061
+ const possibleDocsDirs = [path15.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2946
3062
  for (const docsDir of possibleDocsDirs) {
2947
- const resolvedDocsDir = path14.resolve(docsDir);
3063
+ const resolvedDocsDir = path15.resolve(docsDir);
2948
3064
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2949
3065
  visitedDocsDirs.add(resolvedDocsDir);
2950
- const configPath = path14.join(resolvedDocsDir, ".lee-spec-kit.json");
3066
+ const configPath = path15.join(resolvedDocsDir, ".lee-spec-kit.json");
2951
3067
  if (await fs.pathExists(configPath)) {
2952
3068
  try {
2953
3069
  const configFile = await fs.readJson(configPath);
@@ -2977,16 +3093,16 @@ async function getConfig(cwd) {
2977
3093
  } catch {
2978
3094
  }
2979
3095
  }
2980
- const agentsPath = path14.join(resolvedDocsDir, "agents");
2981
- const featuresPath = path14.join(resolvedDocsDir, "features");
3096
+ const agentsPath = path15.join(resolvedDocsDir, "agents");
3097
+ const featuresPath = path15.join(resolvedDocsDir, "features");
2982
3098
  if (await fs.pathExists(agentsPath) && await fs.pathExists(featuresPath)) {
2983
3099
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2984
3100
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2985
3101
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2986
3102
  const langProbeCandidates = [
2987
- path14.join(agentsPath, "custom.md"),
2988
- path14.join(agentsPath, "constitution.md"),
2989
- path14.join(agentsPath, "agents.md")
3103
+ path15.join(agentsPath, "custom.md"),
3104
+ path15.join(agentsPath, "constitution.md"),
3105
+ path15.join(agentsPath, "agents.md")
2990
3106
  ];
2991
3107
  let lang = "en";
2992
3108
  for (const candidate of langProbeCandidates) {
@@ -3057,13 +3173,13 @@ async function patchMarkdownIfExists(filePath, transform) {
3057
3173
  await fs.writeFile(filePath, transform(content), "utf-8");
3058
3174
  }
3059
3175
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
3060
- await patchMarkdownIfExists(path14.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3176
+ await patchMarkdownIfExists(path15.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3061
3177
  await patchMarkdownIfExists(
3062
- path14.join(featureDir, "tasks.md"),
3178
+ path15.join(featureDir, "tasks.md"),
3063
3179
  (content) => sanitizeTasksForLocal(content, lang)
3064
3180
  );
3065
- await fs.remove(path14.join(featureDir, "issue.md"));
3066
- await fs.remove(path14.join(featureDir, "pr.md"));
3181
+ await fs.remove(path15.join(featureDir, "issue.md"));
3182
+ await fs.remove(path15.join(featureDir, "pr.md"));
3067
3183
  }
3068
3184
  var IDEA_REF_PATTERN = /\b(I\d{3,}(?:-[A-Za-z0-9._-]+)?)\b/;
3069
3185
  var IDEA_PATH_PATTERN = /\b(?:\.\/)?docs\/ideas\/[^\s]+\.md\b/;
@@ -3075,7 +3191,7 @@ function extractExplicitIdeaRef(requestText) {
3075
3191
  return null;
3076
3192
  }
3077
3193
  async function resolveIdeaReference(docsDir, ref, lang) {
3078
- const ideasDir = path14.join(docsDir, "ideas");
3194
+ const ideasDir = path15.join(docsDir, "ideas");
3079
3195
  const trimmedRef = ref.trim();
3080
3196
  if (!trimmedRef) {
3081
3197
  throw createCliError(
@@ -3084,7 +3200,7 @@ async function resolveIdeaReference(docsDir, ref, lang) {
3084
3200
  );
3085
3201
  }
3086
3202
  if (trimmedRef.includes("/") || trimmedRef.endsWith(".md")) {
3087
- const candidate = path14.resolve(process.cwd(), trimmedRef);
3203
+ const candidate = path15.resolve(process.cwd(), trimmedRef);
3088
3204
  if (await fs.pathExists(candidate)) {
3089
3205
  return { path: candidate };
3090
3206
  }
@@ -3103,11 +3219,11 @@ async function resolveIdeaReference(docsDir, ref, lang) {
3103
3219
  const files = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md")).map((entry) => entry.name);
3104
3220
  const exactName = `${trimmedRef}.md`;
3105
3221
  if (files.includes(exactName)) {
3106
- return { path: path14.join(ideasDir, exactName) };
3222
+ return { path: path15.join(ideasDir, exactName) };
3107
3223
  }
3108
3224
  const byId = /^I\d{3,}$/.test(trimmedRef) ? files.filter((name) => name.startsWith(`${trimmedRef}-`)) : [];
3109
3225
  if (byId.length === 1) {
3110
- return { path: path14.join(ideasDir, byId[0]) };
3226
+ return { path: path15.join(ideasDir, byId[0]) };
3111
3227
  }
3112
3228
  if (byId.length > 1) {
3113
3229
  throw createCliError(
@@ -3131,7 +3247,7 @@ async function readIdeaMetadataValue(ideaPath, label) {
3131
3247
  async function deriveFeatureNameFromIdea(ideaPath) {
3132
3248
  const ideaName = await readIdeaMetadataValue(ideaPath, "Idea Name");
3133
3249
  if (ideaName && ideaName !== "-") return ideaName;
3134
- const basename = path14.basename(ideaPath, ".md");
3250
+ const basename = path15.basename(ideaPath, ".md");
3135
3251
  return basename.replace(/^I\d{3,}-/, "");
3136
3252
  }
3137
3253
  function escapeRegExp(value) {
@@ -3287,19 +3403,19 @@ async function runFeature(name, options) {
3287
3403
  }
3288
3404
  let featuresDir;
3289
3405
  if (projectType === "multi") {
3290
- featuresDir = path14.join(docsDir, "features", component);
3406
+ featuresDir = path15.join(docsDir, "features", component);
3291
3407
  } else {
3292
- featuresDir = path14.join(docsDir, "features");
3408
+ featuresDir = path15.join(docsDir, "features");
3293
3409
  }
3294
3410
  const featureFolderName = `${featureId}-${name}`;
3295
- const featureDir = path14.join(featuresDir, featureFolderName);
3411
+ const featureDir = path15.join(featuresDir, featureFolderName);
3296
3412
  if (await fs.pathExists(featureDir)) {
3297
3413
  throw createCliError(
3298
3414
  "INVALID_ARGUMENT",
3299
3415
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
3300
3416
  );
3301
3417
  }
3302
- const featureBasePath = path14.join(
3418
+ const featureBasePath = path15.join(
3303
3419
  getTemplatesDir(),
3304
3420
  lang,
3305
3421
  "common",
@@ -3346,8 +3462,8 @@ async function runFeature(name, options) {
3346
3462
  await replaceInFiles(fsAdapter, featureDir, replacements);
3347
3463
  if (linkedIdea) {
3348
3464
  await stampIdeaReferenceInSpec(
3349
- path14.join(featureDir, "spec.md"),
3350
- path14.relative(featureDir, linkedIdea.path)
3465
+ path15.join(featureDir, "spec.md"),
3466
+ path15.relative(featureDir, linkedIdea.path)
3351
3467
  );
3352
3468
  await markIdeaAsFeatureized(linkedIdea.path, featureFolderName);
3353
3469
  }
@@ -3375,7 +3491,7 @@ async function runFeature(name, options) {
3375
3491
  featureName: name,
3376
3492
  component: projectType === "multi" ? component : void 0,
3377
3493
  featurePath: featureDir,
3378
- featurePathFromDocs: path14.relative(docsDir, featureDir)
3494
+ featurePathFromDocs: path15.relative(docsDir, featureDir)
3379
3495
  };
3380
3496
  },
3381
3497
  { owner: "feature" }
@@ -3435,9 +3551,9 @@ function escapeRegExp2(value) {
3435
3551
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3436
3552
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
3437
3553
  const candidates = [
3438
- ...explicitDocsDir ? [path14.resolve(explicitDocsDir)] : [],
3439
- path14.resolve(cwd, "docs"),
3440
- path14.resolve(cwd)
3554
+ ...explicitDocsDir ? [path15.resolve(explicitDocsDir)] : [],
3555
+ path15.resolve(cwd, "docs"),
3556
+ path15.resolve(cwd)
3441
3557
  ];
3442
3558
  const endAt = Date.now() + timeoutMs;
3443
3559
  while (Date.now() < endAt) {
@@ -3464,12 +3580,12 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3464
3580
  return getConfig(cwd);
3465
3581
  }
3466
3582
  async function getNextFeatureId(docsDir, projectType, components) {
3467
- const featuresDir = path14.join(docsDir, "features");
3583
+ const featuresDir = path15.join(docsDir, "features");
3468
3584
  let max = 0;
3469
3585
  const scanDirs = [];
3470
3586
  if (projectType === "multi") {
3471
3587
  scanDirs.push(
3472
- ...components.map((component) => path14.join(featuresDir, component))
3588
+ ...components.map((component) => path15.join(featuresDir, component))
3473
3589
  );
3474
3590
  } else {
3475
3591
  scanDirs.push(featuresDir);
@@ -3570,16 +3686,16 @@ async function runIdea(name, options) {
3570
3686
  getDocsLockPath(docsDir),
3571
3687
  async () => {
3572
3688
  const ideaId = options.id ? validateProvidedIdeaId(options.id, lang) : await getNextIdeaId(docsDir);
3573
- const ideasDir = path14.join(docsDir, "ideas");
3689
+ const ideasDir = path15.join(docsDir, "ideas");
3574
3690
  const ideaFileName = `${ideaId}-${name}.md`;
3575
- const ideaPath = path14.join(ideasDir, ideaFileName);
3691
+ const ideaPath = path15.join(ideasDir, ideaFileName);
3576
3692
  if (await fs.pathExists(ideaPath)) {
3577
3693
  throw createCliError(
3578
3694
  "INVALID_ARGUMENT",
3579
3695
  tr(lang, "cli", "idea.fileExists", { path: ideaPath })
3580
3696
  );
3581
3697
  }
3582
- const templatePath = path14.join(
3698
+ const templatePath = path15.join(
3583
3699
  getTemplatesDir(),
3584
3700
  lang,
3585
3701
  "common",
@@ -3617,7 +3733,7 @@ async function runIdea(name, options) {
3617
3733
  ideaName: name,
3618
3734
  component: component || void 0,
3619
3735
  ideaPath,
3620
- ideaPathFromDocs: path14.relative(docsDir, ideaPath)
3736
+ ideaPathFromDocs: path15.relative(docsDir, ideaPath)
3621
3737
  };
3622
3738
  },
3623
3739
  { owner: "idea" }
@@ -3635,7 +3751,7 @@ function applyIdeaTemplate(template, values) {
3635
3751
  return template.replaceAll("{idea-id}", values.ideaId).replaceAll("{idea-name}", values.name).replaceAll("{YYYY-MM-DD}", values.created).replaceAll("{{description}}", values.description).replaceAll("{component}", values.component);
3636
3752
  }
3637
3753
  async function getNextIdeaId(docsDir) {
3638
- const ideasDir = path14.join(docsDir, "ideas");
3754
+ const ideasDir = path15.join(docsDir, "ideas");
3639
3755
  let max = 0;
3640
3756
  if (await fs.pathExists(ideasDir)) {
3641
3757
  const entries = await fs.readdir(ideasDir, { withFileTypes: true });
@@ -4074,7 +4190,7 @@ function isNonNegativeIntegerValue(value) {
4074
4190
  }
4075
4191
  function isLikelyCurrentPrePrReviewEvidence(evidencePath, feature) {
4076
4192
  try {
4077
- const raw = fs11.readFileSync(evidencePath, "utf-8");
4193
+ const raw = fs12.readFileSync(evidencePath, "utf-8");
4078
4194
  const parsed = JSON.parse(raw);
4079
4195
  const evidenceFeature = (parsed.feature || "").toString().trim();
4080
4196
  if (evidenceFeature && evidenceFeature !== feature.folderName) {
@@ -4090,31 +4206,31 @@ function resolvePrePrReviewEvidencePath(feature) {
4090
4206
  const candidates = [];
4091
4207
  const explicit = (feature.prePrReview.evidence || "").trim();
4092
4208
  if (explicit && explicit !== "-") {
4093
- if (path14.isAbsolute(explicit)) {
4209
+ if (path15.isAbsolute(explicit)) {
4094
4210
  candidates.push(explicit);
4095
4211
  } else {
4096
- candidates.push(path14.resolve(feature.path, explicit));
4097
- candidates.push(path14.resolve(docsRoot, explicit));
4212
+ candidates.push(path15.resolve(feature.path, explicit));
4213
+ candidates.push(path15.resolve(docsRoot, explicit));
4098
4214
  const normalizedExplicit = explicit.replace(/\\/g, "/");
4099
4215
  if (normalizedExplicit.startsWith("docs/")) {
4100
4216
  const withoutDocsPrefix = normalizedExplicit.slice("docs/".length);
4101
4217
  if (withoutDocsPrefix) {
4102
- candidates.push(path14.resolve(docsRoot, withoutDocsPrefix));
4218
+ candidates.push(path15.resolve(docsRoot, withoutDocsPrefix));
4103
4219
  }
4104
4220
  }
4105
4221
  }
4106
4222
  }
4107
- candidates.push(path14.join(feature.path, "review-trace.json"));
4108
- candidates.push(path14.join(docsRoot, "review-trace.json"));
4223
+ candidates.push(path15.join(feature.path, "review-trace.json"));
4224
+ candidates.push(path15.join(docsRoot, "review-trace.json"));
4109
4225
  const seen = /* @__PURE__ */ new Set();
4110
4226
  for (const candidate of candidates) {
4111
- const abs = path14.resolve(candidate);
4227
+ const abs = path15.resolve(candidate);
4112
4228
  if (seen.has(abs)) continue;
4113
4229
  seen.add(abs);
4114
- if (!fs11.existsSync(abs)) continue;
4230
+ if (!fs12.existsSync(abs)) continue;
4115
4231
  if (!abs.toLowerCase().endsWith(".json")) continue;
4116
4232
  if (!isLikelyCurrentPrePrReviewEvidence(abs, feature)) continue;
4117
- const rel = path14.relative(docsRoot, abs).replace(/\\/g, "/");
4233
+ const rel = path15.relative(docsRoot, abs).replace(/\\/g, "/");
4118
4234
  if (rel && !rel.startsWith("../")) {
4119
4235
  return rel;
4120
4236
  }
@@ -4155,8 +4271,8 @@ function getReviewFixCommitGuidance(feature, lang, options) {
4155
4271
  }
4156
4272
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
4157
4273
  if (!projectGitCwd) return null;
4158
- const normalized = path14.resolve(projectGitCwd);
4159
- const marker = `${path14.sep}.worktrees${path14.sep}`;
4274
+ const normalized = path15.resolve(projectGitCwd);
4275
+ const marker = `${path15.sep}.worktrees${path15.sep}`;
4160
4276
  const markerIndex = normalized.lastIndexOf(marker);
4161
4277
  if (markerIndex <= 0) return null;
4162
4278
  const projectRoot = normalized.slice(0, markerIndex);
@@ -4166,6 +4282,10 @@ function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
4166
4282
  worktreePath: normalized
4167
4283
  };
4168
4284
  }
4285
+ function resolveFeatureWorktreeCleanupPaths(feature) {
4286
+ const cleanupCandidate = feature.git.projectInManagedWorktree ? feature.git.projectGitCwd : feature.git.expectedWorktreePath;
4287
+ return resolveManagedWorktreeCleanupPaths(cleanupCandidate);
4288
+ }
4169
4289
  function shouldBlockTaskCommitGate(policy, check) {
4170
4290
  if (policy !== "strict") return false;
4171
4291
  return !check.pass;
@@ -4199,7 +4319,7 @@ function toTaskKey(rawTitle) {
4199
4319
  function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
4200
4320
  const docsGitCwd = feature.git.docsGitCwd;
4201
4321
  const tasksRelativePath = normalizeGitRelativePath(
4202
- path14.join(feature.docs.featurePathFromDocs, "tasks.md")
4322
+ path15.join(feature.docs.featurePathFromDocs, "tasks.md")
4203
4323
  );
4204
4324
  const diff = readGitText(ctx, docsGitCwd, [
4205
4325
  "diff",
@@ -4274,7 +4394,7 @@ function checkTaskCommitGate(ctx, feature) {
4274
4394
  return { pass: true };
4275
4395
  }
4276
4396
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
4277
- const relativeDocsDir = path14.relative(projectGitCwd, feature.git.docsGitCwd);
4397
+ const relativeDocsDir = path15.relative(projectGitCwd, feature.git.docsGitCwd);
4278
4398
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
4279
4399
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
4280
4400
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -5484,35 +5604,36 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
5484
5604
  step: 15,
5485
5605
  name: tr(lang, "steps", "featureDone"),
5486
5606
  checklist: {
5487
- done: (f) => isFeatureDone(f, workflowPolicy, prePrReviewPolicy)
5607
+ done: (f) => f.completion.workflowDone
5488
5608
  },
5489
5609
  current: {
5490
5610
  when: (f) => isFeatureDone(f, workflowPolicy, prePrReviewPolicy),
5491
5611
  actions: (f) => {
5492
- const actions = [
5612
+ if (f.completion.cleanupPending) {
5613
+ const cleanupPaths = resolveFeatureWorktreeCleanupPaths(f);
5614
+ if (cleanupPaths) {
5615
+ return [
5616
+ {
5617
+ type: "command",
5618
+ category: "worktree_cleanup",
5619
+ requiresUserCheck: true,
5620
+ scope: "project",
5621
+ cwd: cleanupPaths.projectRoot,
5622
+ cmd: tr(lang, "messages", "worktreeCleanupCommand", {
5623
+ projectGitCwd: cleanupPaths.projectRoot,
5624
+ worktreePath: cleanupPaths.worktreePath
5625
+ })
5626
+ }
5627
+ ];
5628
+ }
5629
+ }
5630
+ return [
5493
5631
  {
5494
5632
  type: "instruction",
5495
5633
  category: "feature_done",
5496
5634
  message: tr(lang, "messages", "featureDone")
5497
5635
  }
5498
5636
  ];
5499
- const cleanupPaths = resolveManagedWorktreeCleanupPaths(
5500
- f.git.projectGitCwd
5501
- );
5502
- if (cleanupPaths) {
5503
- actions.push({
5504
- type: "command",
5505
- category: "worktree_cleanup",
5506
- requiresUserCheck: true,
5507
- scope: "project",
5508
- cwd: cleanupPaths.projectRoot,
5509
- cmd: tr(lang, "messages", "worktreeCleanupCommand", {
5510
- projectGitCwd: cleanupPaths.projectRoot,
5511
- worktreePath: cleanupPaths.worktreePath
5512
- })
5513
- });
5514
- }
5515
- return actions;
5516
5637
  }
5517
5638
  }
5518
5639
  }
@@ -5890,17 +6011,17 @@ function isGitPathIgnored(ctx, cwd, relativePath) {
5890
6011
  }
5891
6012
  }
5892
6013
  var GIT_WORKTREE_CACHE = /* @__PURE__ */ new Map();
5893
- var WORKTREE_MARKER = `${path14.sep}.worktrees${path14.sep}`;
6014
+ var WORKTREE_MARKER = `${path15.sep}.worktrees${path15.sep}`;
5894
6015
  function resetContextGitCaches() {
5895
6016
  GIT_WORKTREE_CACHE.clear();
5896
6017
  }
5897
6018
  function isManagedWorktreePath(cwd) {
5898
6019
  if (!cwd) return false;
5899
- const normalized = path14.resolve(cwd);
6020
+ const normalized = path15.resolve(cwd);
5900
6021
  return normalized.includes(WORKTREE_MARKER);
5901
6022
  }
5902
6023
  function resolveProjectRootFromGitCwd(cwd) {
5903
- const normalized = path14.resolve(cwd);
6024
+ const normalized = path15.resolve(cwd);
5904
6025
  const markerIndex = normalized.lastIndexOf(WORKTREE_MARKER);
5905
6026
  if (markerIndex <= 0) return normalized;
5906
6027
  const projectRoot = normalized.slice(0, markerIndex);
@@ -5919,7 +6040,7 @@ function getGitTopLevel(ctx, cwd) {
5919
6040
  }
5920
6041
  function listGitWorktrees(ctx, cwd) {
5921
6042
  const topLevel = getGitTopLevel(ctx, cwd) || cwd;
5922
- const cacheKey = path14.resolve(topLevel);
6043
+ const cacheKey = path15.resolve(topLevel);
5923
6044
  const cached = GIT_WORKTREE_CACHE.get(cacheKey);
5924
6045
  if (cached) return cached;
5925
6046
  try {
@@ -6407,17 +6528,17 @@ function resolveLocalEvidencePathCandidates(rawValue, context) {
6407
6528
  if (!evidencePath) return [];
6408
6529
  if (/^https?:\/\//i.test(evidencePath)) return [];
6409
6530
  const candidates = /* @__PURE__ */ new Set();
6410
- if (path14.isAbsolute(evidencePath)) {
6411
- candidates.add(path14.resolve(evidencePath));
6531
+ if (path15.isAbsolute(evidencePath)) {
6532
+ candidates.add(path15.resolve(evidencePath));
6412
6533
  } else {
6413
- candidates.add(path14.resolve(context.featurePath, evidencePath));
6414
- candidates.add(path14.resolve(context.docsDir, evidencePath));
6415
- candidates.add(path14.resolve(path14.dirname(context.docsDir), evidencePath));
6534
+ candidates.add(path15.resolve(context.featurePath, evidencePath));
6535
+ candidates.add(path15.resolve(context.docsDir, evidencePath));
6536
+ candidates.add(path15.resolve(path15.dirname(context.docsDir), evidencePath));
6416
6537
  const normalizedEvidencePath = evidencePath.replace(/\\/g, "/");
6417
6538
  if (normalizedEvidencePath.startsWith("docs/")) {
6418
6539
  const withoutDocsPrefix = normalizedEvidencePath.slice("docs/".length);
6419
6540
  if (withoutDocsPrefix) {
6420
- candidates.add(path14.resolve(context.docsDir, withoutDocsPrefix));
6541
+ candidates.add(path15.resolve(context.docsDir, withoutDocsPrefix));
6421
6542
  }
6422
6543
  }
6423
6544
  }
@@ -6479,13 +6600,13 @@ function parsePrLink(value) {
6479
6600
  return trimmed;
6480
6601
  }
6481
6602
  function normalizeGitPath(value) {
6482
- return value.split(path14.sep).join("/");
6603
+ return value.split(path15.sep).join("/");
6483
6604
  }
6484
6605
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
6485
- const relativeDocsDir = path14.relative(projectGitCwd, docsDir);
6606
+ const relativeDocsDir = path15.relative(projectGitCwd, docsDir);
6486
6607
  if (!relativeDocsDir) return [];
6487
- if (path14.isAbsolute(relativeDocsDir)) return [];
6488
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path14.sep}`)) {
6608
+ if (path15.isAbsolute(relativeDocsDir)) return [];
6609
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path15.sep}`)) {
6489
6610
  return [];
6490
6611
  }
6491
6612
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(
@@ -6523,7 +6644,7 @@ function getExpectedWorktreeCandidates(projectGitCwd, issueNumber, slug, folderN
6523
6644
  const seen = /* @__PURE__ */ new Set();
6524
6645
  const out = [];
6525
6646
  for (const name of names) {
6526
- const candidate = path14.resolve(projectRoot, ".worktrees", name);
6647
+ const candidate = path15.resolve(projectRoot, ".worktrees", name);
6527
6648
  if (seen.has(candidate)) continue;
6528
6649
  seen.add(candidate);
6529
6650
  out.push(candidate);
@@ -6537,7 +6658,7 @@ function resolveExistingExpectedWorktreePath(projectGitCwd, issueNumber, slug, f
6537
6658
  slug,
6538
6659
  folderName
6539
6660
  )) {
6540
- if (!fs11.existsSync(candidate)) continue;
6661
+ if (!fs12.existsSync(candidate)) continue;
6541
6662
  return candidate;
6542
6663
  }
6543
6664
  return void 0;
@@ -6567,7 +6688,7 @@ function resolveFeatureWorktreePath(ctx, projectGitCwd, issueNumber, slug, folde
6567
6688
  slug,
6568
6689
  folderName
6569
6690
  )) {
6570
- if (!fs11.existsSync(candidate)) continue;
6691
+ if (!fs12.existsSync(candidate)) continue;
6571
6692
  const branchName = getCurrentBranch(ctx, candidate);
6572
6693
  if (!expectedBranchesSet.has(branchName)) continue;
6573
6694
  return {
@@ -6708,10 +6829,10 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6708
6829
  const normalizedCandidates = uniqueNormalizedPaths(
6709
6830
  candidates.map((candidate) => {
6710
6831
  if (!candidate) return "";
6711
- if (!path14.isAbsolute(candidate)) return candidate;
6712
- const relative = path14.relative(projectGitCwd, candidate);
6832
+ if (!path15.isAbsolute(candidate)) return candidate;
6833
+ const relative = path15.relative(projectGitCwd, candidate);
6713
6834
  if (!relative) return "";
6714
- if (relative === ".." || relative.startsWith(`..${path14.sep}`))
6835
+ if (relative === ".." || relative.startsWith(`..${path15.sep}`))
6715
6836
  return "";
6716
6837
  return relative;
6717
6838
  }).filter(Boolean)
@@ -6725,7 +6846,7 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6725
6846
  if (cached) return [...cached];
6726
6847
  const existing = [];
6727
6848
  for (const candidate of normalizedCandidates) {
6728
- if (await ctx.fs.pathExists(path14.join(projectGitCwd, candidate))) {
6849
+ if (await ctx.fs.pathExists(path15.join(projectGitCwd, candidate))) {
6729
6850
  existing.push(candidate);
6730
6851
  }
6731
6852
  }
@@ -6813,16 +6934,16 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6813
6934
  const lang = options.lang;
6814
6935
  const workflowPolicy = resolveWorkflowPolicy(options.workflow);
6815
6936
  const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
6816
- const folderName = path14.basename(featurePath);
6937
+ const folderName = path15.basename(featurePath);
6817
6938
  const match = folderName.match(/^(F\d+)-(.+)$/);
6818
6939
  const id = match?.[1];
6819
6940
  const slug = match?.[2] || folderName;
6820
- const specPath = path14.join(featurePath, "spec.md");
6821
- const planPath = path14.join(featurePath, "plan.md");
6822
- const tasksPath = path14.join(featurePath, "tasks.md");
6823
- const decisionsPath = path14.join(featurePath, "decisions.md");
6824
- const issueDocPath = path14.join(featurePath, "issue.md");
6825
- const prDocPath = path14.join(featurePath, "pr.md");
6941
+ const specPath = path15.join(featurePath, "spec.md");
6942
+ const planPath = path15.join(featurePath, "plan.md");
6943
+ const tasksPath = path15.join(featurePath, "tasks.md");
6944
+ const decisionsPath = path15.join(featurePath, "decisions.md");
6945
+ const issueDocPath = path15.join(featurePath, "issue.md");
6946
+ const prDocPath = path15.join(featurePath, "pr.md");
6826
6947
  let specStatus;
6827
6948
  let issueNumber;
6828
6949
  const specExists = await ctx.fs.pathExists(specPath);
@@ -7084,7 +7205,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7084
7205
  } else if (workflowPolicy.requireWorktree && tasksSummary.total > tasksSummary.done && !projectInManagedWorktree) {
7085
7206
  warnings.push(tr(lang, "warnings", "workflowWorktreeRequired"));
7086
7207
  }
7087
- const relativeFeaturePathFromDocs = path14.relative(
7208
+ const relativeFeaturePathFromDocs = path15.relative(
7088
7209
  context.docsDir,
7089
7210
  featurePath
7090
7211
  );
@@ -7261,7 +7382,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7261
7382
  }
7262
7383
  const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
7263
7384
  const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
7264
- const workflowDone = implementationDone && !docsHasCommitRequiredChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({
7385
+ const workflowBaseDone = implementationDone && !docsHasCommitRequiredChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({
7265
7386
  docs: { prFieldExists, prStatusFieldExists }
7266
7387
  }) && !!prLink) && (!workflowPolicy.requireMerge || prStatus === "Approved") && isPrePrReviewSatisfied2(
7267
7388
  {
@@ -7279,6 +7400,8 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7279
7400
  },
7280
7401
  prePrReviewPolicy
7281
7402
  );
7403
+ const cleanupPending = workflowBaseDone && (projectInManagedWorktree || !!expectedWorktreePath);
7404
+ const workflowDone = workflowBaseDone && !cleanupPending;
7282
7405
  if (implementationDone && !workflowDone) {
7283
7406
  if (specStatus !== "Approved") {
7284
7407
  warnings.push(tr(lang, "warnings", "workflowSpecNotApproved"));
@@ -7340,7 +7463,8 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7340
7463
  path: featurePath,
7341
7464
  completion: {
7342
7465
  implementationDone,
7343
- workflowDone
7466
+ workflowDone,
7467
+ cleanupPending
7344
7468
  },
7345
7469
  issueNumber,
7346
7470
  specStatus,
@@ -7447,7 +7571,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7447
7571
  async function listFeatureDirs(ctx, rootDir) {
7448
7572
  const dirs = await listSubdirectories(ctx.fs, rootDir);
7449
7573
  return dirs.filter(
7450
- (value) => path14.basename(value).trim().toLowerCase() !== "feature-base"
7574
+ (value) => path15.basename(value).trim().toLowerCase() !== "feature-base"
7451
7575
  );
7452
7576
  }
7453
7577
  function normalizeRelPath(value) {
@@ -7588,7 +7712,7 @@ async function scanFeatures(ctx) {
7588
7712
  if (config.projectType === "single") {
7589
7713
  const featureDirs = await listFeatureDirs(
7590
7714
  ctx,
7591
- path14.join(config.docsDir, "features")
7715
+ path15.join(config.docsDir, "features")
7592
7716
  );
7593
7717
  componentFeatureDirs.set("single", featureDirs);
7594
7718
  allFeatureDirs.push(...featureDirs);
@@ -7600,14 +7724,14 @@ async function scanFeatures(ctx) {
7600
7724
  for (const component of components) {
7601
7725
  const componentDirs = await listFeatureDirs(
7602
7726
  ctx,
7603
- path14.join(config.docsDir, "features", component)
7727
+ path15.join(config.docsDir, "features", component)
7604
7728
  );
7605
7729
  componentFeatureDirs.set(component, componentDirs);
7606
7730
  allFeatureDirs.push(...componentDirs);
7607
7731
  }
7608
7732
  }
7609
7733
  const relativeFeaturePaths = allFeatureDirs.map(
7610
- (dir) => normalizeRelPath(path14.relative(config.docsDir, dir))
7734
+ (dir) => normalizeRelPath(path15.relative(config.docsDir, dir))
7611
7735
  );
7612
7736
  const docsGitMeta = buildDocsFeatureGitMeta(
7613
7737
  ctx,
@@ -7624,7 +7748,7 @@ async function scanFeatures(ctx) {
7624
7748
  const parsed = await Promise.all(
7625
7749
  target.dirs.map(async (dir) => {
7626
7750
  const relativeFeaturePathFromDocs = normalizeRelPath(
7627
- path14.relative(config.docsDir, dir)
7751
+ path15.relative(config.docsDir, dir)
7628
7752
  );
7629
7753
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
7630
7754
  return parseFeature(
@@ -7694,13 +7818,13 @@ async function runStatus(options) {
7694
7818
  );
7695
7819
  }
7696
7820
  const { docsDir, projectType, projectName, lang } = ctx.config;
7697
- const featuresDir = path14.join(docsDir, "features");
7821
+ const featuresDir = path15.join(docsDir, "features");
7698
7822
  const scan = await scanFeatures(ctx);
7699
7823
  const features = [];
7700
7824
  const idMap = /* @__PURE__ */ new Map();
7701
7825
  for (const f of scan.features) {
7702
7826
  const id = f.id || "UNKNOWN";
7703
- const relPath = path14.relative(docsDir, f.path);
7827
+ const relPath = path15.relative(docsDir, f.path);
7704
7828
  if (!idMap.has(id)) idMap.set(id, []);
7705
7829
  idMap.get(id).push(relPath);
7706
7830
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -7791,7 +7915,7 @@ async function runStatus(options) {
7791
7915
  }
7792
7916
  console.log();
7793
7917
  if (options.write) {
7794
- const outputPath = path14.join(featuresDir, "status.md");
7918
+ const outputPath = path15.join(featuresDir, "status.md");
7795
7919
  const date = getLocalDateString();
7796
7920
  const content = [
7797
7921
  "# Feature Status",
@@ -7817,7 +7941,7 @@ function escapeRegExp4(value) {
7817
7941
  }
7818
7942
  async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallbackFolderName) {
7819
7943
  try {
7820
- const specPath = path14.join(featureDir, "spec.md");
7944
+ const specPath = path15.join(featureDir, "spec.md");
7821
7945
  if (!await fsAdapter.pathExists(specPath)) return fallbackSlug;
7822
7946
  const content = await fsAdapter.readFile(specPath, "utf-8");
7823
7947
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
@@ -7901,8 +8025,8 @@ async function runUpdate(options) {
7901
8025
  console.log(chalk9.blue(tr(lang, "cli", "update.updatingAgents")));
7902
8026
  }
7903
8027
  if (agentsMode === "all") {
7904
- const commonAgentsBase = path14.join(templatesDir, lang, "common", "agents");
7905
- const targetAgentsBase = path14.join(docsDir, "agents");
8028
+ const commonAgentsBase = path15.join(templatesDir, lang, "common", "agents");
8029
+ const targetAgentsBase = path15.join(docsDir, "agents");
7906
8030
  const commonAgents = commonAgentsBase;
7907
8031
  const targetAgents = targetAgentsBase;
7908
8032
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
@@ -8000,21 +8124,21 @@ async function collectAgentsMdTargets(cwd, config) {
8000
8124
  const targets = /* @__PURE__ */ new Set();
8001
8125
  const docsRepo = config.docsRepo ?? "embedded";
8002
8126
  if (docsRepo === "embedded") {
8003
- const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path14.resolve(config.docsDir, "..");
8004
- targets.add(path14.join(repoRoot, "AGENTS.md"));
8127
+ const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path15.resolve(config.docsDir, "..");
8128
+ targets.add(path15.join(repoRoot, "AGENTS.md"));
8005
8129
  return [...targets];
8006
8130
  }
8007
- targets.add(path14.join(config.docsDir, "AGENTS.md"));
8131
+ targets.add(path15.join(config.docsDir, "AGENTS.md"));
8008
8132
  const baseDir = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || process.cwd();
8009
8133
  const rawRoots = typeof config.projectRoot === "string" ? [config.projectRoot] : config.projectRoot && typeof config.projectRoot === "object" ? Object.values(config.projectRoot) : [];
8010
8134
  for (const rawRoot of rawRoots) {
8011
8135
  const value = String(rawRoot || "").trim();
8012
8136
  if (!value) continue;
8013
- const resolved = path14.resolve(baseDir, value);
8137
+ const resolved = path15.resolve(baseDir, value);
8014
8138
  if (!await fs.pathExists(resolved)) continue;
8015
8139
  const stat = await fs.stat(resolved);
8016
8140
  if (!stat.isDirectory()) continue;
8017
- targets.add(path14.join(resolved, "AGENTS.md"));
8141
+ targets.add(path15.join(resolved, "AGENTS.md"));
8018
8142
  }
8019
8143
  return [...targets];
8020
8144
  }
@@ -8054,7 +8178,7 @@ function normalizeDecisionEnumList2(raw) {
8054
8178
  return [...deduped];
8055
8179
  }
8056
8180
  async function backfillMissingConfigDefaults(docsDir) {
8057
- const configPath = path14.join(docsDir, ".lee-spec-kit.json");
8181
+ const configPath = path15.join(docsDir, ".lee-spec-kit.json");
8058
8182
  if (!await fs.pathExists(configPath)) {
8059
8183
  return { changed: false, changedPaths: [] };
8060
8184
  }
@@ -8177,8 +8301,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
8177
8301
  const files = await fs.readdir(sourceDir);
8178
8302
  let updatedCount = 0;
8179
8303
  for (const file of files) {
8180
- const sourcePath = path14.join(sourceDir, file);
8181
- const targetPath = path14.join(targetDir, file);
8304
+ const sourcePath = path15.join(sourceDir, file);
8305
+ const targetPath = path15.join(targetDir, file);
8182
8306
  const stat = await fs.stat(sourcePath);
8183
8307
  if (stat.isFile()) {
8184
8308
  if (protectedFiles.has(file)) {
@@ -8261,7 +8385,7 @@ function extractPorcelainPaths(line) {
8261
8385
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
8262
8386
  const top = getGitTopLevel2(docsDir);
8263
8387
  if (!top) return null;
8264
- const rel = path14.relative(top, docsDir) || ".";
8388
+ const rel = path15.relative(top, docsDir) || ".";
8265
8389
  try {
8266
8390
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
8267
8391
  cwd: top,
@@ -8273,7 +8397,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
8273
8397
  }
8274
8398
  const ignoredRelPaths = new Set(
8275
8399
  ignoredAbsPaths.map(
8276
- (absPath) => normalizeGitPath2(path14.relative(top, absPath) || ".")
8400
+ (absPath) => normalizeGitPath2(path15.relative(top, absPath) || ".")
8277
8401
  )
8278
8402
  );
8279
8403
  const filtered = output.split("\n").filter((line) => {
@@ -8331,7 +8455,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
8331
8455
  }
8332
8456
  async function runConfig(options) {
8333
8457
  const cwd = process.cwd();
8334
- const targetCwd = options.dir ? path14.resolve(cwd, options.dir) : cwd;
8458
+ const targetCwd = options.dir ? path15.resolve(cwd, options.dir) : cwd;
8335
8459
  const config = await getConfig(targetCwd);
8336
8460
  if (!config) {
8337
8461
  throw createCliError(
@@ -8339,7 +8463,7 @@ async function runConfig(options) {
8339
8463
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
8340
8464
  );
8341
8465
  }
8342
- const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
8466
+ const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
8343
8467
  if (!options.projectRoot) {
8344
8468
  console.log();
8345
8469
  console.log(chalk9.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -8445,47 +8569,47 @@ var BUILTIN_DOC_DEFINITIONS = [
8445
8569
  {
8446
8570
  id: "agents",
8447
8571
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
8448
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "agents.md")
8572
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "agents.md")
8449
8573
  },
8450
8574
  {
8451
8575
  id: "git-workflow",
8452
8576
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
8453
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "git-workflow.md")
8577
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "git-workflow.md")
8454
8578
  },
8455
8579
  {
8456
8580
  id: "issue-doc",
8457
8581
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
8458
- relativePath: (_, lang) => path14.join(lang, "common", "features", "feature-base", "issue.md")
8582
+ relativePath: (_, lang) => path15.join(lang, "common", "features", "feature-base", "issue.md")
8459
8583
  },
8460
8584
  {
8461
8585
  id: "pr-doc",
8462
8586
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
8463
- relativePath: (_, lang) => path14.join(lang, "common", "features", "feature-base", "pr.md")
8587
+ relativePath: (_, lang) => path15.join(lang, "common", "features", "feature-base", "pr.md")
8464
8588
  },
8465
8589
  {
8466
8590
  id: "create-feature",
8467
8591
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
8468
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-feature.md")
8592
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-feature.md")
8469
8593
  },
8470
8594
  {
8471
8595
  id: "execute-task",
8472
8596
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
8473
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "execute-task.md")
8597
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "execute-task.md")
8474
8598
  },
8475
8599
  {
8476
8600
  id: "create-issue",
8477
8601
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
8478
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-issue.md")
8602
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-issue.md")
8479
8603
  },
8480
8604
  {
8481
8605
  id: "create-pr",
8482
8606
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
8483
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "create-pr.md")
8607
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-pr.md")
8484
8608
  },
8485
8609
  {
8486
8610
  id: "split-feature",
8487
8611
  title: { ko: "feature \uBD84\uD560 \uAC00\uC774\uB4DC", en: "feature split guide" },
8488
- relativePath: (_, lang) => path14.join(lang, "common", "agents", "skills", "split-feature.md")
8612
+ relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "split-feature.md")
8489
8613
  }
8490
8614
  ];
8491
8615
  var DOC_FOLLOWUPS = {
@@ -8582,7 +8706,7 @@ function listBuiltinDocs(projectType, lang) {
8582
8706
  id: doc.id,
8583
8707
  title: doc.title[lang],
8584
8708
  relativePath,
8585
- absolutePath: path14.join(templatesDir, relativePath)
8709
+ absolutePath: path15.join(templatesDir, relativePath)
8586
8710
  };
8587
8711
  });
8588
8712
  }
@@ -9449,6 +9573,9 @@ function buildRequiredDocHints(actionOptions) {
9449
9573
  }
9450
9574
  function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
9451
9575
  if (f.completion.implementationDone && !f.completion.workflowDone) {
9576
+ if (f.completion.cleanupPending) {
9577
+ return tr(lang, "cli", "context.list.cleanupPending");
9578
+ }
9452
9579
  if (f.git.docsHasCommitRequiredChanges) {
9453
9580
  return tr(lang, "cli", "context.list.docsCommitNeeded");
9454
9581
  }
@@ -9627,7 +9754,7 @@ function getApprovalSessionId() {
9627
9754
  function getApprovalTicketPaths(config) {
9628
9755
  return {
9629
9756
  runtimePath: getApprovalTicketStorePath(config.docsDir),
9630
- legacyPath: path14.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9757
+ legacyPath: path15.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
9631
9758
  };
9632
9759
  }
9633
9760
  async function loadApprovalTicketStore(storePath) {
@@ -9641,7 +9768,7 @@ async function loadApprovalTicketStore(storePath) {
9641
9768
  }
9642
9769
  }
9643
9770
  async function saveApprovalTicketStore(storePath, payload) {
9644
- await fs.ensureDir(path14.dirname(storePath));
9771
+ await fs.ensureDir(path15.dirname(storePath));
9645
9772
  await fs.writeJson(storePath, payload, { spaces: 2 });
9646
9773
  }
9647
9774
  function pruneApprovalTickets(tickets, nowMs) {
@@ -10726,7 +10853,7 @@ async function runContext(featureName, options) {
10726
10853
  if (f.issueNumber) {
10727
10854
  console.log(` \u2022 Issue: #${f.issueNumber}`);
10728
10855
  }
10729
- console.log(` \u2022 Path: ${path14.relative(cwd, f.path)}`);
10856
+ console.log(` \u2022 Path: ${path15.relative(cwd, f.path)}`);
10730
10857
  if (f.git.projectBranch) {
10731
10858
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
10732
10859
  }
@@ -10878,7 +11005,7 @@ function extractTitleAfterId(line, id) {
10878
11005
  return cleaned ? cleaned : void 0;
10879
11006
  }
10880
11007
  async function scanPrdRequirements(fsAdapter, docsDir) {
10881
- const prdDir = path14.join(docsDir, "prd");
11008
+ const prdDir = path15.join(docsDir, "prd");
10882
11009
  const files = await walkFiles(fsAdapter, prdDir, {
10883
11010
  extensions: [".md"],
10884
11011
  ignoreDirs: [".git", "node_modules", "dist", "tmp"]
@@ -10886,7 +11013,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10886
11013
  const definitions = /* @__PURE__ */ new Map();
10887
11014
  const duplicates = [];
10888
11015
  for (const filePath of files) {
10889
- if (path14.basename(filePath).toLowerCase() === "readme.md") {
11016
+ if (path15.basename(filePath).toLowerCase() === "readme.md") {
10890
11017
  continue;
10891
11018
  }
10892
11019
  let content = "";
@@ -10895,7 +11022,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
10895
11022
  } catch {
10896
11023
  continue;
10897
11024
  }
10898
- const relFile = normalizeRelPath2(path14.relative(docsDir, filePath));
11025
+ const relFile = normalizeRelPath2(path15.relative(docsDir, filePath));
10899
11026
  const lines = content.split(/\r?\n/);
10900
11027
  let inCodeBlock = false;
10901
11028
  for (let i = 0; i < lines.length; i += 1) {
@@ -10970,7 +11097,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
10970
11097
  ]);
10971
11098
  function formatPath(cwd, p) {
10972
11099
  if (!p) return "";
10973
- return path14.isAbsolute(p) ? path14.relative(cwd, p) : p;
11100
+ return path15.isAbsolute(p) ? path15.relative(cwd, p) : p;
10974
11101
  }
10975
11102
  function detectPlaceholders(content) {
10976
11103
  const patterns = [
@@ -11129,7 +11256,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
11129
11256
  const placeholderContext = {
11130
11257
  projectName: config.projectName,
11131
11258
  featureName: f.slug,
11132
- featurePath: f.docs.featurePathFromDocs || path14.relative(config.docsDir, f.path),
11259
+ featurePath: f.docs.featurePathFromDocs || path15.relative(config.docsDir, f.path),
11133
11260
  repoType: f.type,
11134
11261
  featureNumber
11135
11262
  };
@@ -11139,7 +11266,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
11139
11266
  "tasks.md"
11140
11267
  ];
11141
11268
  for (const file of files) {
11142
- const fullPath = path14.join(f.path, file);
11269
+ const fullPath = path15.join(f.path, file);
11143
11270
  if (!await fs.pathExists(fullPath)) continue;
11144
11271
  const original = await fs.readFile(fullPath, "utf-8");
11145
11272
  let next = original;
@@ -11192,7 +11319,7 @@ async function checkDocsStructure(config, cwd) {
11192
11319
  const issues = [];
11193
11320
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
11194
11321
  for (const dir of requiredDirs) {
11195
- const p = path14.join(config.docsDir, dir);
11322
+ const p = path15.join(config.docsDir, dir);
11196
11323
  if (!await fs.pathExists(p)) {
11197
11324
  issues.push({
11198
11325
  level: "error",
@@ -11204,7 +11331,7 @@ async function checkDocsStructure(config, cwd) {
11204
11331
  });
11205
11332
  }
11206
11333
  }
11207
- const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
11334
+ const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
11208
11335
  if (!await fs.pathExists(configPath)) {
11209
11336
  issues.push({
11210
11337
  level: "warn",
@@ -11232,7 +11359,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11232
11359
  }
11233
11360
  const idMap = /* @__PURE__ */ new Map();
11234
11361
  for (const f of features) {
11235
- const rel = f.docs.featurePathFromDocs || path14.relative(config.docsDir, f.path);
11362
+ const rel = f.docs.featurePathFromDocs || path15.relative(config.docsDir, f.path);
11236
11363
  const id = f.id || "UNKNOWN";
11237
11364
  if (!idMap.has(id)) idMap.set(id, []);
11238
11365
  idMap.get(id).push(rel);
@@ -11240,7 +11367,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11240
11367
  if (!isInitialTemplateState) {
11241
11368
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
11242
11369
  for (const file of featureDocs) {
11243
- const p = path14.join(f.path, file);
11370
+ const p = path15.join(f.path, file);
11244
11371
  if (!await fs.pathExists(p)) continue;
11245
11372
  const content = await fs.readFile(p, "utf-8");
11246
11373
  const placeholders = detectPlaceholders(content);
@@ -11255,7 +11382,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11255
11382
  });
11256
11383
  }
11257
11384
  if (decisionsPlaceholderMode !== "off") {
11258
- const decisionsPath = path14.join(f.path, "decisions.md");
11385
+ const decisionsPath = path15.join(f.path, "decisions.md");
11259
11386
  if (await fs.pathExists(decisionsPath)) {
11260
11387
  const content = await fs.readFile(decisionsPath, "utf-8");
11261
11388
  const placeholders = detectPlaceholders(content);
@@ -11284,7 +11411,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11284
11411
  level: "warn",
11285
11412
  code: "spec_status_unset",
11286
11413
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
11287
- path: formatPath(cwd, path14.join(f.path, "spec.md"))
11414
+ path: formatPath(cwd, path15.join(f.path, "spec.md"))
11288
11415
  });
11289
11416
  }
11290
11417
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -11292,7 +11419,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11292
11419
  level: "warn",
11293
11420
  code: "plan_status_unset",
11294
11421
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
11295
- path: formatPath(cwd, path14.join(f.path, "plan.md"))
11422
+ path: formatPath(cwd, path15.join(f.path, "plan.md"))
11296
11423
  });
11297
11424
  }
11298
11425
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -11300,11 +11427,11 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11300
11427
  level: "warn",
11301
11428
  code: "tasks_empty",
11302
11429
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
11303
- path: formatPath(cwd, path14.join(f.path, "tasks.md"))
11430
+ path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11304
11431
  });
11305
11432
  }
11306
11433
  if (f.docs.tasksExists) {
11307
- const tasksPath = path14.join(f.path, "tasks.md");
11434
+ const tasksPath = path15.join(f.path, "tasks.md");
11308
11435
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
11309
11436
  const unknownPrdTags = [...new Set(
11310
11437
  parseTaskLines(tasksContent).flatMap((task) => task.tags).filter((tag) => isPrdRequirementId(tag)).map((tag) => tag.trim().toUpperCase()).filter((tag) => !prdDefinitions.has(tag))
@@ -11326,7 +11453,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11326
11453
  level: "warn",
11327
11454
  code: "tasks_doc_status_missing",
11328
11455
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
11329
- path: formatPath(cwd, path14.join(f.path, "tasks.md"))
11456
+ path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11330
11457
  });
11331
11458
  }
11332
11459
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -11334,7 +11461,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11334
11461
  level: "warn",
11335
11462
  code: "tasks_doc_status_unset",
11336
11463
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
11337
- path: formatPath(cwd, path14.join(f.path, "tasks.md"))
11464
+ path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11338
11465
  });
11339
11466
  }
11340
11467
  }
@@ -11358,7 +11485,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11358
11485
  level: "warn",
11359
11486
  code: "missing_feature_id",
11360
11487
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
11361
- path: formatPath(cwd, path14.join(config.docsDir, p))
11488
+ path: formatPath(cwd, path15.join(config.docsDir, p))
11362
11489
  });
11363
11490
  }
11364
11491
  return issues;
@@ -11489,7 +11616,7 @@ function doctorCommand(program2) {
11489
11616
  }
11490
11617
  console.log();
11491
11618
  console.log(chalk9.bold(tr(lang, "cli", "doctor.title")));
11492
- console.log(chalk9.gray(`- Docs: ${path14.relative(cwd, docsDir)}`));
11619
+ console.log(chalk9.gray(`- Docs: ${path15.relative(cwd, docsDir)}`));
11493
11620
  console.log(chalk9.gray(`- Type: ${projectType}`));
11494
11621
  console.log(chalk9.gray(`- Lang: ${lang}`));
11495
11622
  console.log();
@@ -11670,7 +11797,7 @@ async function runView(featureName, options) {
11670
11797
  }
11671
11798
  console.log();
11672
11799
  console.log(chalk9.bold("\u{1F4CA} Workflow View"));
11673
- console.log(chalk9.gray(`- Docs: ${path14.relative(cwd, config.docsDir)}`));
11800
+ console.log(chalk9.gray(`- Docs: ${path15.relative(cwd, config.docsDir)}`));
11674
11801
  console.log(
11675
11802
  chalk9.gray(
11676
11803
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
@@ -11755,13 +11882,13 @@ function normalizeRunId(raw) {
11755
11882
  return value;
11756
11883
  }
11757
11884
  function getFlowRunBaseDir(cwd) {
11758
- return path14.join(getRuntimeStateDir(cwd), "flow-runs");
11885
+ return path15.join(getRuntimeStateDir(cwd), "flow-runs");
11759
11886
  }
11760
11887
  function getFlowRunPath(cwd, runId) {
11761
- return path14.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11888
+ return path15.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11762
11889
  }
11763
11890
  function getFlowRunLockPath(cwd, runId) {
11764
- return path14.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11891
+ return path15.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11765
11892
  }
11766
11893
  async function readFlowRunRecordUnsafe(cwd, runId) {
11767
11894
  const normalized = normalizeRunId(runId);
@@ -11787,7 +11914,7 @@ async function readFlowRunRecordUnsafe(cwd, runId) {
11787
11914
  }
11788
11915
  async function writeFlowRunRecord(cwd, record) {
11789
11916
  const filePath = getFlowRunPath(cwd, record.runId);
11790
- await fs.ensureDir(path14.dirname(filePath));
11917
+ await fs.ensureDir(path15.dirname(filePath));
11791
11918
  await fs.writeJson(filePath, record, { spaces: 2 });
11792
11919
  }
11793
11920
  async function createFlowRunRecord(cwd, input) {
@@ -13192,27 +13319,27 @@ function tg(lang, key, vars = {}) {
13192
13319
  function detectGithubCliLangSync(cwd) {
13193
13320
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
13194
13321
  const startDirs = [
13195
- explicitDocsDir ? path14.resolve(explicitDocsDir) : "",
13196
- path14.resolve(cwd)
13322
+ explicitDocsDir ? path15.resolve(explicitDocsDir) : "",
13323
+ path15.resolve(cwd)
13197
13324
  ].filter(Boolean);
13198
13325
  const scanOrder = [];
13199
13326
  const seen = /* @__PURE__ */ new Set();
13200
13327
  for (const start of startDirs) {
13201
13328
  let current = start;
13202
13329
  while (true) {
13203
- const abs = path14.resolve(current);
13330
+ const abs = path15.resolve(current);
13204
13331
  if (!seen.has(abs)) {
13205
13332
  scanOrder.push(abs);
13206
13333
  seen.add(abs);
13207
13334
  }
13208
- const parent = path14.dirname(abs);
13335
+ const parent = path15.dirname(abs);
13209
13336
  if (parent === abs) break;
13210
13337
  current = parent;
13211
13338
  }
13212
13339
  }
13213
13340
  for (const base of scanOrder) {
13214
- for (const docsDir of [path14.join(base, "docs"), base]) {
13215
- const configPath = path14.join(docsDir, ".lee-spec-kit.json");
13341
+ for (const docsDir of [path15.join(base, "docs"), base]) {
13342
+ const configPath = path15.join(docsDir, ".lee-spec-kit.json");
13216
13343
  if (fs.existsSync(configPath)) {
13217
13344
  try {
13218
13345
  const parsed = fs.readJsonSync(configPath);
@@ -13221,11 +13348,11 @@ function detectGithubCliLangSync(cwd) {
13221
13348
  } catch {
13222
13349
  }
13223
13350
  }
13224
- const agentsPath = path14.join(docsDir, "agents");
13225
- const featuresPath = path14.join(docsDir, "features");
13351
+ const agentsPath = path15.join(docsDir, "agents");
13352
+ const featuresPath = path15.join(docsDir, "features");
13226
13353
  if (!fs.existsSync(agentsPath) || !fs.existsSync(featuresPath)) continue;
13227
13354
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
13228
- const file = path14.join(agentsPath, probe);
13355
+ const file = path15.join(agentsPath, probe);
13229
13356
  if (!fs.existsSync(file)) continue;
13230
13357
  try {
13231
13358
  const content = fs.readFileSync(file, "utf-8");
@@ -13330,7 +13457,7 @@ async function prepareGithubBody(params) {
13330
13457
  };
13331
13458
  }
13332
13459
  }
13333
- await fs.ensureDir(path14.dirname(defaultBodyFile));
13460
+ await fs.ensureDir(path15.dirname(defaultBodyFile));
13334
13461
  await fs.writeFile(defaultBodyFile, generatedBody, "utf-8");
13335
13462
  return {
13336
13463
  body: generatedBody,
@@ -13400,7 +13527,7 @@ function ensureSections(body, sections, kind, lang) {
13400
13527
  }
13401
13528
  function ensureDocsExist(docsDir, relativePaths, lang) {
13402
13529
  const missing = relativePaths.filter(
13403
- (relativePath) => !fs.existsSync(path14.join(docsDir, relativePath))
13530
+ (relativePath) => !fs.existsSync(path15.join(docsDir, relativePath))
13404
13531
  );
13405
13532
  if (missing.length > 0) {
13406
13533
  throw createCliError(
@@ -13410,18 +13537,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
13410
13537
  }
13411
13538
  }
13412
13539
  function buildDefaultBodyFileName(kind, docsDir, component) {
13413
- const key = `${path14.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13540
+ const key = `${path15.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13414
13541
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
13415
13542
  return `lee-spec-kit.${digest}.${kind}.md`;
13416
13543
  }
13417
13544
  function toBodyFilePath(raw, kind, docsDir, component, lang) {
13418
- const selected = raw?.trim() || path14.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13545
+ const selected = raw?.trim() || path15.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13419
13546
  assertValid(
13420
13547
  validatePathWithLang(selected, lang),
13421
13548
  `github.${kind}.bodyFile`,
13422
13549
  lang
13423
13550
  );
13424
- return path14.resolve(selected);
13551
+ return path15.resolve(selected);
13425
13552
  }
13426
13553
  function toProjectRootDocsPath(relativePathFromDocs) {
13427
13554
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -14273,6 +14400,85 @@ function ensureIssueClosingLine(body, issueNumber) {
14273
14400
  return `${trimmed}${separator}Closes #${issueNumber}
14274
14401
  `;
14275
14402
  }
14403
+ function extractTasksIssueReference(tasksContent) {
14404
+ return extractDraftMetadataValue(tasksContent, [
14405
+ "Issue",
14406
+ "Issue Number",
14407
+ "\uC774\uC288",
14408
+ "\uC774\uC288 \uBC88\uD638"
14409
+ ]);
14410
+ }
14411
+ function parseStrictIssueReference(raw) {
14412
+ const value = (raw || "").trim();
14413
+ if (!value) return void 0;
14414
+ const match = value.match(/^#?\s*(\d+)\s*$/);
14415
+ return match?.[1];
14416
+ }
14417
+ function resolvePrClosingIssueNumber(tasksContent, featureIssueNumber, lang) {
14418
+ const rawIssueReference = extractTasksIssueReference(tasksContent);
14419
+ const parsedRawIssueNumber = parseStrictIssueReference(rawIssueReference);
14420
+ if (rawIssueReference && !parsedRawIssueNumber) {
14421
+ throw createCliError(
14422
+ "PRECONDITION_FAILED",
14423
+ tg(lang, "invalidIssueReference", {
14424
+ value: rawIssueReference.trim()
14425
+ })
14426
+ );
14427
+ }
14428
+ if (parsedRawIssueNumber && /^0+$/.test(parsedRawIssueNumber)) {
14429
+ throw createCliError(
14430
+ "PRECONDITION_FAILED",
14431
+ tg(lang, "invalidIssueReference", {
14432
+ value: rawIssueReference?.trim() || parsedRawIssueNumber
14433
+ })
14434
+ );
14435
+ }
14436
+ const issueNumber = (featureIssueNumber || parsedRawIssueNumber || "").trim();
14437
+ if (!issueNumber) return void 0;
14438
+ return issueNumber;
14439
+ }
14440
+ function assertRemoteIssueExists(issueNumber, cwd, lang) {
14441
+ if (!issueNumber) return;
14442
+ const result = runProcess(
14443
+ "gh",
14444
+ ["issue", "view", issueNumber, "--json", "number,state"],
14445
+ cwd
14446
+ );
14447
+ if (result.code !== 0) {
14448
+ const detail = (result.stderr || result.stdout || "").trim();
14449
+ if (/not found|could not resolve|404|no issue/i.test(detail)) {
14450
+ throw createCliError(
14451
+ "PRECONDITION_FAILED",
14452
+ tg(lang, "issueNotFound", {
14453
+ issue: `#${issueNumber}`
14454
+ })
14455
+ );
14456
+ }
14457
+ throw createCliError(
14458
+ "EXECUTION_FAILED",
14459
+ `${tg(lang, "issueLookupFailed")}${detail ? `: ${detail}` : ""}`
14460
+ );
14461
+ }
14462
+ let payload;
14463
+ try {
14464
+ payload = JSON.parse((result.stdout || "").trim());
14465
+ } catch {
14466
+ throw createCliError(
14467
+ "EXECUTION_FAILED",
14468
+ tg(lang, "ghInvalidJson", {
14469
+ snippet: (result.stdout || "").trim().slice(0, 160)
14470
+ })
14471
+ );
14472
+ }
14473
+ if (String(payload?.number || "") !== String(issueNumber)) {
14474
+ throw createCliError(
14475
+ "PRECONDITION_FAILED",
14476
+ tg(lang, "issueNotFound", {
14477
+ issue: `#${issueNumber}`
14478
+ })
14479
+ );
14480
+ }
14481
+ }
14276
14482
  function getRequiredIssueSections(lang) {
14277
14483
  return getGithubDraftRequiredSections("issue", lang);
14278
14484
  }
@@ -14476,7 +14682,7 @@ function ensureCleanWorktree(cwd, lang) {
14476
14682
  function commitAndPushPaths(cwd, absPaths, message, lang, options) {
14477
14683
  const uniqueRelativePaths = [
14478
14684
  ...new Set(
14479
- absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path14.relative(cwd, absPath) || absPath)
14685
+ absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path15.relative(cwd, absPath) || absPath)
14480
14686
  )
14481
14687
  ];
14482
14688
  if (uniqueRelativePaths.length === 0) return;
@@ -14672,15 +14878,15 @@ function githubCommand(program2) {
14672
14878
  config.lang
14673
14879
  );
14674
14880
  const specContent = await fs.readFile(
14675
- path14.join(config.docsDir, paths.specPath),
14881
+ path15.join(config.docsDir, paths.specPath),
14676
14882
  "utf-8"
14677
14883
  );
14678
14884
  const planContent = await fs.readFile(
14679
- path14.join(config.docsDir, paths.planPath),
14885
+ path15.join(config.docsDir, paths.planPath),
14680
14886
  "utf-8"
14681
14887
  );
14682
14888
  const tasksContent = await fs.readFile(
14683
- path14.join(config.docsDir, paths.tasksPath),
14889
+ path15.join(config.docsDir, paths.tasksPath),
14684
14890
  "utf-8"
14685
14891
  );
14686
14892
  const overview = resolveOverviewFromSpec(
@@ -14723,7 +14929,7 @@ function githubCommand(program2) {
14723
14929
  create: options.create,
14724
14930
  explicitBodyFile,
14725
14931
  defaultBodyFile,
14726
- workflowDraftPath: path14.join(config.docsDir, paths.issuePath),
14932
+ workflowDraftPath: path15.join(config.docsDir, paths.issuePath),
14727
14933
  generatedBody,
14728
14934
  requiredSections: getRequiredIssueSections(config.lang),
14729
14935
  kindLabel: tg(config.lang, "kindIssue"),
@@ -14739,7 +14945,7 @@ function githubCommand(program2) {
14739
14945
  `${feature.type}-issue-sanitized`,
14740
14946
  config.lang
14741
14947
  );
14742
- await fs.ensureDir(path14.dirname(sanitizedBodyFile));
14948
+ await fs.ensureDir(path15.dirname(sanitizedBodyFile));
14743
14949
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14744
14950
  bodyFile = sanitizedBodyFile;
14745
14951
  }
@@ -14788,12 +14994,12 @@ function githubCommand(program2) {
14788
14994
  const syncedIssueNumber = extractIssueNumberFromUrl(issueUrl);
14789
14995
  if (syncedIssueNumber) {
14790
14996
  const synced = syncTasksIssueMetadata(
14791
- path14.join(config.docsDir, paths.tasksPath),
14997
+ path15.join(config.docsDir, paths.tasksPath),
14792
14998
  syncedIssueNumber,
14793
14999
  config.lang
14794
15000
  );
14795
15001
  const draftSynced = syncIssueDraftMetadata(
14796
- path14.join(config.docsDir, paths.issuePath),
15002
+ path15.join(config.docsDir, paths.issuePath),
14797
15003
  syncedIssueNumber
14798
15004
  );
14799
15005
  syncChanged = synced.changed || draftSynced.changed;
@@ -14904,13 +15110,13 @@ function githubCommand(program2) {
14904
15110
  config.lang
14905
15111
  );
14906
15112
  const specContent = await fs.readFile(
14907
- path14.join(config.docsDir, paths.specPath),
15113
+ path15.join(config.docsDir, paths.specPath),
14908
15114
  "utf-8"
14909
15115
  );
14910
- const planPath = path14.join(config.docsDir, paths.planPath);
15116
+ const planPath = path15.join(config.docsDir, paths.planPath);
14911
15117
  const planContent = await fs.pathExists(planPath) ? await fs.readFile(planPath, "utf-8") : "";
14912
15118
  const tasksContent = await fs.readFile(
14913
- path14.join(config.docsDir, paths.tasksPath),
15119
+ path15.join(config.docsDir, paths.tasksPath),
14914
15120
  "utf-8"
14915
15121
  );
14916
15122
  const overview = resolveOverviewFromSpec(
@@ -14958,7 +15164,7 @@ function githubCommand(program2) {
14958
15164
  create: options.create,
14959
15165
  explicitBodyFile,
14960
15166
  defaultBodyFile,
14961
- workflowDraftPath: path14.join(config.docsDir, paths.prPath),
15167
+ workflowDraftPath: path15.join(config.docsDir, paths.prPath),
14962
15168
  generatedBody,
14963
15169
  requiredSections: getRequiredPrSections(config.lang),
14964
15170
  kindLabel: tg(config.lang, "kindPr"),
@@ -14976,7 +15182,7 @@ function githubCommand(program2) {
14976
15182
  `${feature.type}-pr-sanitized`,
14977
15183
  config.lang
14978
15184
  );
14979
- await fs.ensureDir(path14.dirname(sanitizedBodyFile));
15185
+ await fs.ensureDir(path15.dirname(sanitizedBodyFile));
14980
15186
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
14981
15187
  bodyFile = sanitizedBodyFile;
14982
15188
  }
@@ -14994,9 +15200,23 @@ function githubCommand(program2) {
14994
15200
  let syncChanged = false;
14995
15201
  const pushDocsSync = shouldPushDocsSync(config);
14996
15202
  if (options.create) {
15203
+ const projectGitCwd = resolveGithubProjectCwd(
15204
+ config,
15205
+ feature
15206
+ );
15207
+ const closingIssueNumber = resolvePrClosingIssueNumber(
15208
+ tasksContent,
15209
+ feature.issueNumber,
15210
+ config.lang
15211
+ );
15212
+ assertRemoteIssueExists(
15213
+ closingIssueNumber,
15214
+ projectGitCwd,
15215
+ config.lang
15216
+ );
14997
15217
  const normalizedBody = ensureIssueClosingLine(
14998
15218
  body,
14999
- feature.issueNumber
15219
+ closingIssueNumber
15000
15220
  );
15001
15221
  if (normalizedBody !== body) {
15002
15222
  body = normalizedBody;
@@ -15004,15 +15224,11 @@ function githubCommand(program2) {
15004
15224
  if (preparedBody.source === "generated") {
15005
15225
  await fs.writeFile(bodyFile, body, "utf-8");
15006
15226
  } else {
15007
- await fs.ensureDir(path14.dirname(fallbackBodyFile));
15227
+ await fs.ensureDir(path15.dirname(fallbackBodyFile));
15008
15228
  await fs.writeFile(fallbackBodyFile, body, "utf-8");
15009
15229
  bodyFile = fallbackBodyFile;
15010
15230
  }
15011
15231
  }
15012
- const projectGitCwd = resolveGithubProjectCwd(
15013
- config,
15014
- feature
15015
- );
15016
15232
  ensureNoTodoPlaceholders(
15017
15233
  body,
15018
15234
  tg(config.lang, "kindPr"),
@@ -15065,13 +15281,13 @@ function githubCommand(program2) {
15065
15281
  }
15066
15282
  if (prUrl && options.syncTasks !== false) {
15067
15283
  const syncedTasks = syncTasksPrMetadata(
15068
- path14.join(config.docsDir, paths.tasksPath),
15284
+ path15.join(config.docsDir, paths.tasksPath),
15069
15285
  prUrl,
15070
15286
  "Review",
15071
15287
  config.lang
15072
15288
  );
15073
15289
  const syncedDraft = syncPrDraftMetadata(
15074
- path14.join(config.docsDir, paths.prPath),
15290
+ path15.join(config.docsDir, paths.prPath),
15075
15291
  prUrl,
15076
15292
  "Review"
15077
15293
  );
@@ -15112,13 +15328,13 @@ function githubCommand(program2) {
15112
15328
  mergeAlreadyMerged = merged.alreadyMerged;
15113
15329
  if (prUrl && options.syncTasks !== false) {
15114
15330
  const mergedTasksSync = syncTasksPrMetadata(
15115
- path14.join(config.docsDir, paths.tasksPath),
15331
+ path15.join(config.docsDir, paths.tasksPath),
15116
15332
  prUrl,
15117
15333
  "Approved",
15118
15334
  config.lang
15119
15335
  );
15120
15336
  const mergedDraftSync = syncPrDraftMetadata(
15121
- path14.join(config.docsDir, paths.prPath),
15337
+ path15.join(config.docsDir, paths.prPath),
15122
15338
  prUrl,
15123
15339
  "Approved"
15124
15340
  );
@@ -15389,7 +15605,7 @@ function docsCommand(program2) {
15389
15605
  );
15390
15606
  return;
15391
15607
  }
15392
- const relativeFromCwd = path14.relative(process.cwd(), loaded.entry.absolutePath);
15608
+ const relativeFromCwd = path15.relative(process.cwd(), loaded.entry.absolutePath);
15393
15609
  console.log();
15394
15610
  console.log(chalk9.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
15395
15611
  console.log(
@@ -15469,7 +15685,7 @@ function detectCommand(program2) {
15469
15685
  }
15470
15686
  async function runDetect(options) {
15471
15687
  const cwd = process.cwd();
15472
- const targetCwd = options.dir ? path14.resolve(cwd, options.dir) : cwd;
15688
+ const targetCwd = options.dir ? path15.resolve(cwd, options.dir) : cwd;
15473
15689
  const config = await getConfig(targetCwd);
15474
15690
  const detected = !!config;
15475
15691
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
@@ -15496,7 +15712,7 @@ async function runDetect(options) {
15496
15712
  );
15497
15713
  return;
15498
15714
  }
15499
- const configPath2 = path14.join(config.docsDir, ".lee-spec-kit.json");
15715
+ const configPath2 = path15.join(config.docsDir, ".lee-spec-kit.json");
15500
15716
  const configFilePresent2 = await fs.pathExists(configPath2);
15501
15717
  const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
15502
15718
  console.log(
@@ -15530,7 +15746,7 @@ async function runDetect(options) {
15530
15746
  console.log();
15531
15747
  return;
15532
15748
  }
15533
- const configPath = path14.join(config.docsDir, ".lee-spec-kit.json");
15749
+ const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
15534
15750
  const configFilePresent = await fs.pathExists(configPath);
15535
15751
  const detectionSource = configFilePresent ? "config" : "heuristic";
15536
15752
  console.log(chalk9.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
@@ -15607,19 +15823,19 @@ function hasTemplateMarkers(content) {
15607
15823
  return patterns.some((pattern) => pattern.test(content));
15608
15824
  }
15609
15825
  async function countFeatureDirs(ctx, docsDir, projectType) {
15610
- const featuresRoot = path14.join(docsDir, "features");
15826
+ const featuresRoot = path15.join(docsDir, "features");
15611
15827
  if (projectType === "single") {
15612
15828
  const dirs = await listSubdirectories(ctx.fs, featuresRoot);
15613
- return dirs.filter((value) => path14.basename(value) !== "feature-base").length;
15829
+ return dirs.filter((value) => path15.basename(value) !== "feature-base").length;
15614
15830
  }
15615
15831
  const components = await listSubdirectories(ctx.fs, featuresRoot);
15616
15832
  let total = 0;
15617
15833
  for (const componentDir of components) {
15618
- const componentName = path14.basename(componentDir).trim().toLowerCase();
15834
+ const componentName = path15.basename(componentDir).trim().toLowerCase();
15619
15835
  if (!componentName || componentName === "feature-base") continue;
15620
15836
  const dirs = await listSubdirectories(ctx.fs, componentDir);
15621
15837
  total += dirs.filter(
15622
- (value) => path14.basename(value) !== "feature-base"
15838
+ (value) => path15.basename(value) !== "feature-base"
15623
15839
  ).length;
15624
15840
  }
15625
15841
  return total;
@@ -15631,7 +15847,7 @@ async function hasUserPrdFile(ctx, prdDir) {
15631
15847
  ignoreDirs: ["node_modules"]
15632
15848
  });
15633
15849
  return files.some(
15634
- (absolutePath) => path14.basename(absolutePath).toLowerCase() !== "readme.md"
15850
+ (absolutePath) => path15.basename(absolutePath).toLowerCase() !== "readme.md"
15635
15851
  );
15636
15852
  }
15637
15853
  function finalizeChecks(checks) {
@@ -15800,7 +16016,7 @@ async function runOnboardChecks(ctx) {
15800
16016
  });
15801
16017
  }
15802
16018
  }
15803
- const constitutionPath = path14.join(docsDir, "agents", "constitution.md");
16019
+ const constitutionPath = path15.join(docsDir, "agents", "constitution.md");
15804
16020
  if (!await fs.pathExists(constitutionPath)) {
15805
16021
  checks.push({
15806
16022
  id: "constitution_exists",
@@ -15842,7 +16058,7 @@ async function runOnboardChecks(ctx) {
15842
16058
  });
15843
16059
  }
15844
16060
  }
15845
- const customPath = path14.join(docsDir, "agents", "custom.md");
16061
+ const customPath = path15.join(docsDir, "agents", "custom.md");
15846
16062
  if (await fs.pathExists(customPath)) {
15847
16063
  const content = await fs.readFile(customPath, "utf-8");
15848
16064
  if (hasTemplateMarkers(content)) {
@@ -15871,7 +16087,7 @@ async function runOnboardChecks(ctx) {
15871
16087
  });
15872
16088
  }
15873
16089
  }
15874
- const prdDir = path14.join(docsDir, "prd");
16090
+ const prdDir = path15.join(docsDir, "prd");
15875
16091
  const featureCount = await countFeatureDirs(ctx, docsDir, config.projectType);
15876
16092
  const prdReady = await hasUserPrdFile(ctx, prdDir);
15877
16093
  if (!prdReady) {
@@ -15889,7 +16105,7 @@ async function runOnboardChecks(ctx) {
15889
16105
  "PRD is empty. If features already exist, fill PRD as soon as possible."
15890
16106
  ),
15891
16107
  path: prdDir,
15892
- suggestedCommand: `touch ${quotePath(path14.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
16108
+ suggestedCommand: `touch ${quotePath(path15.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
15893
16109
  });
15894
16110
  } else {
15895
16111
  checks.push({
@@ -16327,7 +16543,7 @@ var PrePrReviewValidator = class {
16327
16543
  return result.evidence;
16328
16544
  }
16329
16545
  async validateEvidenceWithScope(evidencePath, projectRoot) {
16330
- const fullPath = path14.resolve(evidencePath);
16546
+ const fullPath = path15.resolve(evidencePath);
16331
16547
  if (!await fs.pathExists(fullPath)) {
16332
16548
  throw createCliError(
16333
16549
  "INVALID_ARGUMENT",
@@ -16425,9 +16641,9 @@ var PrePrReviewValidator = class {
16425
16641
  ]);
16426
16642
  const reviewedFiles = new Set(
16427
16643
  normalizedEvidence.files.map(
16428
- (f) => path14.relative(
16644
+ (f) => path15.relative(
16429
16645
  projectRoot,
16430
- path14.resolve(projectRoot, f.path)
16646
+ path15.resolve(projectRoot, f.path)
16431
16647
  )
16432
16648
  ).map((entry) => normalizeGitPath3(entry)).filter(Boolean)
16433
16649
  );
@@ -16888,7 +17104,7 @@ async function runPrePrReviewRun(featureName, options) {
16888
17104
  );
16889
17105
  const policy = resolvePrePrReviewPolicy(config.workflow);
16890
17106
  const preferred = getPreferredKeys(config.lang);
16891
- const tasksPath = path14.join(feature.path, "tasks.md");
17107
+ const tasksPath = path15.join(feature.path, "tasks.md");
16892
17108
  let tasksUpdated = false;
16893
17109
  if (await fs.pathExists(tasksPath)) {
16894
17110
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -16997,7 +17213,7 @@ async function runPrePrReview(featureName, options) {
16997
17213
  `tasks.md not found for feature: ${feature.folderName}`
16998
17214
  );
16999
17215
  }
17000
- const tasksPath = path14.join(feature.path, "tasks.md");
17216
+ const tasksPath = path15.join(feature.path, "tasks.md");
17001
17217
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
17002
17218
  const policy = resolvePrePrReviewPolicy(config.workflow);
17003
17219
  const preferred = getPreferredKeys(config.lang);
@@ -17095,7 +17311,7 @@ async function runPrePrReview(featureName, options) {
17095
17311
  }
17096
17312
  }
17097
17313
  }
17098
- const decisionsPath = path14.join(feature.path, "decisions.md");
17314
+ const decisionsPath = path15.join(feature.path, "decisions.md");
17099
17315
  const decisionLogEntry = buildReportContent({
17100
17316
  folderName: feature.folderName,
17101
17317
  date,
@@ -17111,9 +17327,9 @@ async function runPrePrReview(featureName, options) {
17111
17327
  await fs.writeFile(decisionsPath, nextDecisions, "utf-8");
17112
17328
  }
17113
17329
  const decisionsPathFromDocs = normalizePathForDoc(
17114
- path14.join(feature.docs.featurePathFromDocs, "decisions.md")
17330
+ path15.join(feature.docs.featurePathFromDocs, "decisions.md")
17115
17331
  );
17116
- const evidencePath = path14.basename(config.docsDir) === "docs" ? normalizePathForDoc(path14.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
17332
+ const evidencePath = path15.basename(config.docsDir) === "docs" ? normalizePathForDoc(path15.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
17117
17333
  let nextTasks = tasksContent;
17118
17334
  nextTasks = upsertSpecLine(
17119
17335
  nextTasks,
@@ -17254,7 +17470,7 @@ async function runCodeReviewRun(featureName, options) {
17254
17470
  );
17255
17471
  }
17256
17472
  const feature = state.matchedFeature;
17257
- const tasksPath = path14.join(feature.path, "tasks.md");
17473
+ const tasksPath = path15.join(feature.path, "tasks.md");
17258
17474
  let tasksUpdated = false;
17259
17475
  if (await fs.pathExists(tasksPath)) {
17260
17476
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -17287,7 +17503,7 @@ async function runCodeReviewRun(featureName, options) {
17287
17503
  nextMainState: "code_review_running",
17288
17504
  tasksUpdated,
17289
17505
  tasksPath,
17290
- decisionsPath: path14.join(feature.path, "decisions.md"),
17506
+ decisionsPath: path15.join(feature.path, "decisions.md"),
17291
17507
  prompt,
17292
17508
  recordedAt: getLocalDateString()
17293
17509
  };
@@ -17406,7 +17622,7 @@ async function runRequirements(options) {
17406
17622
  }
17407
17623
  for (const feature of scan.features) {
17408
17624
  if (!feature.docs.tasksExists) continue;
17409
- const tasksPath = path14.join(feature.path, "tasks.md");
17625
+ const tasksPath = path15.join(feature.path, "tasks.md");
17410
17626
  let tasksContent = "";
17411
17627
  try {
17412
17628
  tasksContent = await ctx.fs.readFile(tasksPath, "utf-8");
@@ -17540,7 +17756,7 @@ async function runRequirements(options) {
17540
17756
  process.stdout.write(`${lines.join("\n")}
17541
17757
  `);
17542
17758
  if (options.write) {
17543
- const outputPath = path14.join(docsDir, "prd", "status.md");
17759
+ const outputPath = path15.join(docsDir, "prd", "status.md");
17544
17760
  await ctx.fs.writeFile(outputPath, `${lines.join("\n")}
17545
17761
  `, "utf-8");
17546
17762
  console.log(chalk9.green(`\u2705 wrote: ${outputPath}`));
@@ -17625,7 +17841,7 @@ async function resolveTaskRunContext(featureName, options) {
17625
17841
  }
17626
17842
  async function runTaskRun(featureName, options) {
17627
17843
  const { config, feature } = await resolveTaskRunContext(featureName, options);
17628
- const tasksPath = path14.join(feature.path, "tasks.md");
17844
+ const tasksPath = path15.join(feature.path, "tasks.md");
17629
17845
  if (!await fs.pathExists(tasksPath)) {
17630
17846
  throw createCliError(
17631
17847
  "PRECONDITION_FAILED",
@@ -17776,7 +17992,7 @@ async function resolveTaskCompleteContext(featureName, options) {
17776
17992
  }
17777
17993
  async function runTaskComplete(featureName, options) {
17778
17994
  const { feature } = await resolveTaskCompleteContext(featureName, options);
17779
- const tasksPath = path14.join(feature.path, "tasks.md");
17995
+ const tasksPath = path15.join(feature.path, "tasks.md");
17780
17996
  if (!await fs.pathExists(tasksPath)) {
17781
17997
  throw createCliError(
17782
17998
  "PRECONDITION_FAILED",
@@ -17872,6 +18088,42 @@ function taskCompleteCommand(program2) {
17872
18088
  }
17873
18089
  );
17874
18090
  }
18091
+ function setupCommand(program2) {
18092
+ const setup = program2.command("setup").description("Developer environment setup helpers");
18093
+ setup.command("codex-bootstrap").description(
18094
+ "Install a small Codex global bootstrap that reads ./docs/AGENTS.md"
18095
+ ).option(
18096
+ "--remove",
18097
+ "Remove the lee-spec-kit managed Codex bootstrap block"
18098
+ ).action(async (options) => {
18099
+ const lang = DEFAULT_LANG;
18100
+ try {
18101
+ const filePath = getCodexConfigPath();
18102
+ if (options.remove) {
18103
+ const result2 = await removeLeeSpecKitCodexBootstrap(filePath);
18104
+ const message = result2.changed ? tr(lang, "cli", "setup.codexBootstrapRemoved", {
18105
+ path: filePath
18106
+ }) : tr(lang, "cli", "setup.codexBootstrapAlreadyAbsent", {
18107
+ path: filePath
18108
+ });
18109
+ console.log(chalk9.green(message));
18110
+ return;
18111
+ }
18112
+ const result = await upsertLeeSpecKitCodexBootstrap(filePath);
18113
+ const key = result.action === "noop" ? "setup.codexBootstrapAlreadyInstalled" : "setup.codexBootstrapInstalled";
18114
+ console.log(chalk9.green(tr(lang, "cli", key, { path: filePath })));
18115
+ } catch (error) {
18116
+ const cliError = toCliError(error);
18117
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
18118
+ console.error(
18119
+ chalk9.red(tr(lang, "cli", "common.errorLabel")),
18120
+ chalk9.red(`[${cliError.code}] ${cliError.message}`)
18121
+ );
18122
+ printCliErrorSuggestions(suggestions, lang);
18123
+ process.exitCode = 1;
18124
+ }
18125
+ });
18126
+ }
17875
18127
  function isBannerDisabled() {
17876
18128
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
17877
18129
  return v === "1";
@@ -17915,11 +18167,11 @@ ${version}
17915
18167
  }
17916
18168
  return `${ascii}${footer}`;
17917
18169
  }
17918
- var CACHE_FILE = path14.join(os.homedir(), ".lee-spec-kit-version-cache.json");
18170
+ var CACHE_FILE = path15.join(os.homedir(), ".lee-spec-kit-version-cache.json");
17919
18171
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
17920
18172
  function getCurrentVersion() {
17921
18173
  try {
17922
- const packageJsonPath = path14.join(__dirname$1, "..", "package.json");
18174
+ const packageJsonPath = path15.join(__dirname$1, "..", "package.json");
17923
18175
  if (fs.existsSync(packageJsonPath)) {
17924
18176
  const pkg = fs.readJsonSync(packageJsonPath);
17925
18177
  return pkg.version;
@@ -18023,7 +18275,7 @@ function shouldCheckForUpdates() {
18023
18275
  if (shouldCheckForUpdates()) checkForUpdates();
18024
18276
  function getCliVersion() {
18025
18277
  try {
18026
- const packageJsonPath = path14.join(__dirname$1, "..", "package.json");
18278
+ const packageJsonPath = path15.join(__dirname$1, "..", "package.json");
18027
18279
  if (fs.existsSync(packageJsonPath)) {
18028
18280
  const pkg = fs.readJsonSync(packageJsonPath);
18029
18281
  if (pkg?.version) return String(pkg.version);
@@ -18058,6 +18310,7 @@ codeReviewRunCommand(program);
18058
18310
  taskRunCommand(program);
18059
18311
  taskCompleteCommand(program);
18060
18312
  requirementsCommand(program);
18313
+ setupCommand(program);
18061
18314
  await program.parseAsync();
18062
18315
  //# sourceMappingURL=index.js.map
18063
18316
  //# sourceMappingURL=index.js.map