lee-spec-kit 0.7.8 → 0.7.10

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 path15 from 'path';
2
+ import path16 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 fs12 from 'fs';
11
+ import fs13 from 'fs';
12
12
  import { Buffer as Buffer$1 } from 'buffer';
13
13
 
14
14
  var getFilename = () => fileURLToPath(import.meta.url);
15
- var getDirname = () => path15.dirname(getFilename());
15
+ var getDirname = () => path16.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 = path15.join(current, entryName);
28
+ const absolute = path16.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 = path15.extname(entryName).toLowerCase();
37
+ const ext = path16.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 = path15.join(rootDir, entryName);
53
+ const absolute = path16.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 = path15.dirname(__filename2);
154
+ var __dirname2 = path16.dirname(__filename2);
155
155
  function getTemplatesDir() {
156
- const rootDir = path15.resolve(__dirname2, "..");
157
- return path15.join(rootDir, "templates");
156
+ const rootDir = path16.resolve(__dirname2, "..");
157
+ return path16.join(rootDir, "templates");
158
158
  }
159
159
 
160
160
  // src/utils/locales/ko/cli.ts
@@ -255,7 +255,7 @@ var koCli = {
255
255
  "init.log.nextSteps1": " 1. {docsDir}/prd/README.md \uC791\uC131",
256
256
  "init.log.nextSteps2": " 2. npx lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00",
257
257
  "init.log.nextSteps3": " 3. npx lee-spec-kit onboard --strict \uB85C \uCD08\uAE30 \uC124\uC815 \uC810\uAC80",
258
- "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",
258
+ "init.log.nextSteps4": " 4. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 AGENTS.md \uC5C6\uC774 Codex\uB97C \uC4F4\uB2E4\uBA74 bootstrap helper \uB97C \uC120\uD0DD\uC801\uC73C\uB85C \uC124\uCE58: npx lee-spec-kit integrations codex",
259
259
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0, docs \uCEE4\uBC0B \uC911...",
260
260
  "init.log.gitInit": "\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911...",
261
261
  "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)',
@@ -274,10 +274,10 @@ var koCli = {
274
274
  "idea.nextSteps1": " 1. \uBC94\uC704, PRD Refs, \uC2B9\uACA9 \uBA54\uBAA8\uB97C \uC791\uC131",
275
275
  "idea.nextSteps2": " 2. Feature\uB85C \uC2B9\uACA9: npx lee-spec-kit feature <name> --idea {ideaId}",
276
276
  "idea.nextSteps3": " 3. Feature\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uC744 \uACBD\uC6B0 Dropped\uB85C \uD45C\uC2DC",
277
- "setup.codexBootstrapInstalled": "\u2705 Codex bootstrap \uC124\uCE58 \uC644\uB8CC: {path}",
278
- "setup.codexBootstrapAlreadyInstalled": "\u2705 Codex bootstrap \uC774 \uC774\uBBF8 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: {path}",
279
- "setup.codexBootstrapRemoved": "\u2705 Codex bootstrap \uC81C\uAC70 \uC644\uB8CC: {path}",
280
- "setup.codexBootstrapAlreadyAbsent": "\u2705 Codex bootstrap \uC774 \uC774\uBBF8 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
277
+ "setup.codexBootstrapInstalled": "\u2705 \uC120\uD0DD\uC801 Codex bootstrap \uC124\uCE58 \uC644\uB8CC: {path}",
278
+ "setup.codexBootstrapAlreadyInstalled": "\u2705 \uC120\uD0DD\uC801 Codex bootstrap \uC774 \uC774\uBBF8 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: {path}",
279
+ "setup.codexBootstrapRemoved": "\u2705 \uC120\uD0DD\uC801 Codex bootstrap \uC81C\uAC70 \uC644\uB8CC: {path}",
280
+ "setup.codexBootstrapAlreadyAbsent": "\u2705 \uC120\uD0DD\uC801 Codex bootstrap \uC774 \uC774\uBBF8 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
281
281
  "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)",
282
282
  "github.cmdIssueDescription": "feature \uBB38\uC11C \uAE30\uBC18 GitHub issue \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131",
283
283
  "github.cmdPrDescription": "GitHub PR \uBCF8\uBB38 \uC0DD\uC131/\uC0DD\uC131 + tasks \uB3D9\uAE30\uD654 + merge \uC7AC\uC2DC\uB3C4",
@@ -832,7 +832,7 @@ var enCli = {
832
832
  "init.log.nextSteps1": " 1. Write {docsDir}/prd/README.md",
833
833
  "init.log.nextSteps2": " 2. Add a feature with: npx lee-spec-kit feature <name>",
834
834
  "init.log.nextSteps3": " 3. Run setup checks: npx lee-spec-kit onboard --strict",
835
- "init.log.nextSteps4": " 4. If you use Codex without repo-root AGENTS.md, install bootstrap: npx lee-spec-kit setup codex-bootstrap",
835
+ "init.log.nextSteps4": " 4. If you use Codex without repo-root AGENTS.md, you can optionally install the bootstrap helper: npx lee-spec-kit integrations codex",
836
836
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git repo detected, committing docs...",
837
837
  "init.log.gitInit": "\u{1F4E6} Initializing Git...",
838
838
  "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.)',
@@ -851,10 +851,10 @@ var enCli = {
851
851
  "idea.nextSteps1": " 1. Fill scope, PRD refs, and promotion notes",
852
852
  "idea.nextSteps2": " 2. Promote it with: npx lee-spec-kit feature <name> --idea {ideaId}",
853
853
  "idea.nextSteps3": " 3. Mark it dropped if it should not become a feature",
854
- "setup.codexBootstrapInstalled": "\u2705 Codex bootstrap installed: {path}",
855
- "setup.codexBootstrapAlreadyInstalled": "\u2705 Codex bootstrap already installed: {path}",
856
- "setup.codexBootstrapRemoved": "\u2705 Codex bootstrap removed: {path}",
857
- "setup.codexBootstrapAlreadyAbsent": "\u2705 Codex bootstrap is already absent: {path}",
854
+ "setup.codexBootstrapInstalled": "\u2705 Optional Codex bootstrap installed: {path}",
855
+ "setup.codexBootstrapAlreadyInstalled": "\u2705 Optional Codex bootstrap already installed: {path}",
856
+ "setup.codexBootstrapRemoved": "\u2705 Optional Codex bootstrap removed: {path}",
857
+ "setup.codexBootstrapAlreadyAbsent": "\u2705 Optional Codex bootstrap is already absent: {path}",
858
858
  "github.cmdGithubDescription": "GitHub workflow helpers (issue/pr templates, validation, merge retry)",
859
859
  "github.cmdIssueDescription": "Generate/create GitHub issue body from feature docs with validation",
860
860
  "github.cmdPrDescription": "Generate/create GitHub PR body with validation, tasks PR sync, and merge retry",
@@ -1757,10 +1757,10 @@ var DEFAULT_STALE_MS = 2 * 6e4;
1757
1757
  var RUNTIME_GIT_DIRNAME = "lee-spec-kit.runtime";
1758
1758
  var RUNTIME_TEMP_DIRNAME = "lee-spec-kit-runtime";
1759
1759
  function toScopeKey(value) {
1760
- return createHash("sha1").update(path15.resolve(value)).digest("hex").slice(0, 16);
1760
+ return createHash("sha1").update(path16.resolve(value)).digest("hex").slice(0, 16);
1761
1761
  }
1762
1762
  function getTempRuntimeDir(scopePath) {
1763
- return path15.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1763
+ return path16.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
1764
1764
  }
1765
1765
  function resolveGitRuntimeDir(cwd) {
1766
1766
  try {
@@ -1774,38 +1774,38 @@ function resolveGitRuntimeDir(cwd) {
1774
1774
  }
1775
1775
  ).trim();
1776
1776
  if (!out) return null;
1777
- return path15.isAbsolute(out) ? out : path15.resolve(cwd, out);
1777
+ return path16.isAbsolute(out) ? out : path16.resolve(cwd, out);
1778
1778
  } catch {
1779
1779
  return null;
1780
1780
  }
1781
1781
  }
1782
1782
  function getRuntimeStateDir(cwd) {
1783
- const resolved = path15.resolve(cwd);
1783
+ const resolved = path16.resolve(cwd);
1784
1784
  return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
1785
1785
  }
1786
1786
  function getDocsLockPath(docsDir) {
1787
- return path15.join(
1787
+ return path16.join(
1788
1788
  getRuntimeStateDir(docsDir),
1789
1789
  "locks",
1790
1790
  `docs-${toScopeKey(docsDir)}.lock`
1791
1791
  );
1792
1792
  }
1793
1793
  function getInitLockPath(targetDir) {
1794
- return path15.join(
1795
- getRuntimeStateDir(path15.dirname(path15.resolve(targetDir))),
1794
+ return path16.join(
1795
+ getRuntimeStateDir(path16.dirname(path16.resolve(targetDir))),
1796
1796
  "locks",
1797
1797
  `init-${toScopeKey(targetDir)}.lock`
1798
1798
  );
1799
1799
  }
1800
1800
  function getApprovalTicketStorePath(docsDir) {
1801
- return path15.join(
1801
+ return path16.join(
1802
1802
  getRuntimeStateDir(docsDir),
1803
1803
  "tickets",
1804
1804
  `approval-${toScopeKey(docsDir)}.json`
1805
1805
  );
1806
1806
  }
1807
1807
  function getProjectExecutionLockPath(cwd) {
1808
- return path15.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1808
+ return path16.join(getRuntimeStateDir(cwd), "locks", "project.lock");
1809
1809
  }
1810
1810
  async function isStaleLock(lockPath, staleMs) {
1811
1811
  try {
@@ -1846,7 +1846,7 @@ function isProcessAlive(pid) {
1846
1846
  }
1847
1847
  }
1848
1848
  async function tryAcquire(lockPath, owner) {
1849
- await fs.ensureDir(path15.dirname(lockPath));
1849
+ await fs.ensureDir(path16.dirname(lockPath));
1850
1850
  try {
1851
1851
  const fd = await fs.open(lockPath, "wx");
1852
1852
  const payload = JSON.stringify(
@@ -1923,30 +1923,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
1923
1923
  "pr-template.md"
1924
1924
  ];
1925
1925
  var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
1926
- var ENGINE_MANAGED_FEATURE_PATH = path15.join(
1926
+ var ENGINE_MANAGED_FEATURE_PATH = path16.join(
1927
1927
  "features",
1928
1928
  "feature-base"
1929
1929
  );
1930
1930
  async function pruneEngineManagedDocs(docsDir) {
1931
1931
  const removed = [];
1932
1932
  for (const file of ENGINE_MANAGED_AGENT_FILES) {
1933
- const target = path15.join(docsDir, "agents", file);
1933
+ const target = path16.join(docsDir, "agents", file);
1934
1934
  if (await fs.pathExists(target)) {
1935
1935
  await fs.remove(target);
1936
- removed.push(path15.relative(docsDir, target));
1936
+ removed.push(path16.relative(docsDir, target));
1937
1937
  }
1938
1938
  }
1939
1939
  for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
1940
- const target = path15.join(docsDir, "agents", dir);
1940
+ const target = path16.join(docsDir, "agents", dir);
1941
1941
  if (await fs.pathExists(target)) {
1942
1942
  await fs.remove(target);
1943
- removed.push(path15.relative(docsDir, target));
1943
+ removed.push(path16.relative(docsDir, target));
1944
1944
  }
1945
1945
  }
1946
- const featureBasePath = path15.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1946
+ const featureBasePath = path16.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
1947
1947
  if (await fs.pathExists(featureBasePath)) {
1948
1948
  await fs.remove(featureBasePath);
1949
- removed.push(path15.relative(docsDir, featureBasePath));
1949
+ removed.push(path16.relative(docsDir, featureBasePath));
1950
1950
  }
1951
1951
  return removed;
1952
1952
  }
@@ -2127,10 +2127,10 @@ function renderManagedBlock2() {
2127
2127
  function getCodexHome() {
2128
2128
  const explicit = String(process.env.CODEX_HOME || "").trim();
2129
2129
  if (explicit) return explicit;
2130
- return path15.join(os.homedir(), ".codex");
2130
+ return path16.join(os.homedir(), ".codex");
2131
2131
  }
2132
2132
  function getCodexConfigPath() {
2133
- return path15.join(getCodexHome(), "config.toml");
2133
+ return path16.join(getCodexHome(), "config.toml");
2134
2134
  }
2135
2135
  function contentIncludesRequiredBootstrap(content) {
2136
2136
  const matchesCurrent = REQUIRED_FALLBACKS.every((value) => content.includes(value)) && REQUIRED_COMPACT_LINES.every((line) => content.includes(line));
@@ -2149,7 +2149,7 @@ async function hasLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
2149
2149
  async function upsertLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
2150
2150
  const block = renderManagedBlock2();
2151
2151
  const segment = renderManagedSegment2();
2152
- await fs.ensureDir(path15.dirname(filePath));
2152
+ await fs.ensureDir(path16.dirname(filePath));
2153
2153
  const exists = await fs.pathExists(filePath);
2154
2154
  if (!exists) {
2155
2155
  await fs.writeFile(filePath, block, "utf-8");
@@ -2283,37 +2283,24 @@ function validatePromptPathValue(value, lang) {
2283
2283
  function validatePromptUrlValue(value, lang) {
2284
2284
  return value.trim() ? true : tr(lang, "cli", "init.validation.enterUrl");
2285
2285
  }
2286
- var DEFAULT_APPROVAL_REQUIRE_CHECK_CATEGORIES = [
2287
- "spec_approve",
2288
- "implementation_approve"
2289
- ];
2290
- function createDefaultApprovalConfig() {
2291
- return {
2292
- mode: "category",
2293
- default: "skip",
2294
- requireCheckCategories: [...DEFAULT_APPROVAL_REQUIRE_CHECK_CATEGORIES]
2295
- };
2296
- }
2297
2286
  function getAncestorDirs(startDir) {
2298
2287
  const dirs = [];
2299
- let current = path15.resolve(startDir);
2288
+ let current = path16.resolve(startDir);
2300
2289
  while (true) {
2301
2290
  dirs.push(current);
2302
- const parent = path15.dirname(current);
2291
+ const parent = path16.dirname(current);
2303
2292
  if (parent === current) break;
2304
2293
  current = parent;
2305
2294
  }
2306
2295
  return dirs;
2307
2296
  }
2308
2297
  function hasWorkspaceBoundary(dir) {
2309
- return fs.existsSync(path15.join(dir, "package.json")) || fs.existsSync(path15.join(dir, ".git"));
2298
+ return fs.existsSync(path16.join(dir, "package.json")) || fs.existsSync(path16.join(dir, ".git"));
2310
2299
  }
2311
2300
  function getSearchBaseDirs(cwd) {
2312
2301
  const ancestors = getAncestorDirs(cwd);
2313
2302
  const boundaryIndex = ancestors.findIndex(hasWorkspaceBoundary);
2314
- if (boundaryIndex === -1) {
2315
- return [ancestors[0]];
2316
- }
2303
+ if (boundaryIndex === -1) return [ancestors[0]];
2317
2304
  return ancestors.slice(0, boundaryIndex + 1);
2318
2305
  }
2319
2306
  var FEATURE_FOLDER_PATTERN = /^F\d{3,}-/i;
@@ -2322,7 +2309,7 @@ function normalizeComponentKeys(value) {
2322
2309
  return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
2323
2310
  }
2324
2311
  async function inferComponentsFromFeaturesDir(docsDir) {
2325
- const featuresPath = path15.join(docsDir, "features");
2312
+ const featuresPath = path16.join(docsDir, "features");
2326
2313
  if (!await fs.pathExists(featuresPath)) return [];
2327
2314
  const entries = await fs.readdir(featuresPath, { withFileTypes: true });
2328
2315
  const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
@@ -2330,24 +2317,42 @@ async function inferComponentsFromFeaturesDir(docsDir) {
2330
2317
  );
2331
2318
  return [...new Set(inferred)];
2332
2319
  }
2333
- async function getConfig(cwd) {
2320
+ function toProjectConfig(docsDir, configFile, projectType, components) {
2321
+ return {
2322
+ schemaId: "lee-spec",
2323
+ docsDir,
2324
+ projectName: configFile.projectName,
2325
+ projectType,
2326
+ components: projectType === "multi" ? components : void 0,
2327
+ lang: configFile.lang,
2328
+ docsRepo: configFile.docsRepo,
2329
+ pushDocs: configFile.pushDocs,
2330
+ docsRemote: configFile.docsRemote,
2331
+ projectRoot: configFile.projectRoot,
2332
+ allowedDocsEntries: configFile.allowedDocsEntries,
2333
+ pr: configFile.pr,
2334
+ workflow: configFile.workflow,
2335
+ approval: configFile.approval
2336
+ };
2337
+ }
2338
+ async function detectLeeSpecProject(cwd) {
2334
2339
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
2335
2340
  const baseDirs = [
2336
- ...explicitDocsDir ? [path15.resolve(explicitDocsDir)] : [],
2341
+ ...explicitDocsDir ? [path16.resolve(explicitDocsDir)] : [],
2337
2342
  ...getSearchBaseDirs(cwd)
2338
2343
  ];
2339
2344
  const visitedBaseDirs = /* @__PURE__ */ new Set();
2340
2345
  const visitedDocsDirs = /* @__PURE__ */ new Set();
2341
2346
  for (const baseDir of baseDirs) {
2342
- const resolvedBaseDir = path15.resolve(baseDir);
2347
+ const resolvedBaseDir = path16.resolve(baseDir);
2343
2348
  if (visitedBaseDirs.has(resolvedBaseDir)) continue;
2344
2349
  visitedBaseDirs.add(resolvedBaseDir);
2345
- const possibleDocsDirs = [path15.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2350
+ const possibleDocsDirs = [path16.join(resolvedBaseDir, "docs"), resolvedBaseDir];
2346
2351
  for (const docsDir of possibleDocsDirs) {
2347
- const resolvedDocsDir = path15.resolve(docsDir);
2352
+ const resolvedDocsDir = path16.resolve(docsDir);
2348
2353
  if (visitedDocsDirs.has(resolvedDocsDir)) continue;
2349
2354
  visitedDocsDirs.add(resolvedDocsDir);
2350
- const configPath = path15.join(resolvedDocsDir, ".lee-spec-kit.json");
2355
+ const configPath = path16.join(resolvedDocsDir, ".lee-spec-kit.json");
2351
2356
  if (await fs.pathExists(configPath)) {
2352
2357
  try {
2353
2358
  const configFile = await fs.readJson(configPath);
@@ -2361,33 +2366,32 @@ async function getConfig(cwd) {
2361
2366
  Array.isArray(configFile.components) && configFile.components.length > 0 ? configFile.components : inferredComponents
2362
2367
  );
2363
2368
  return {
2369
+ detected: true,
2370
+ schemaId: "lee-spec",
2371
+ detectionSource: "config",
2364
2372
  docsDir: resolvedDocsDir,
2365
- projectName: configFile.projectName,
2366
- projectType,
2367
- components: projectType === "multi" ? components : void 0,
2368
- lang: configFile.lang,
2369
- docsRepo: configFile.docsRepo,
2370
- pushDocs: configFile.pushDocs,
2371
- docsRemote: configFile.docsRemote,
2372
- projectRoot: configFile.projectRoot,
2373
- allowedDocsEntries: configFile.allowedDocsEntries,
2374
- pr: configFile.pr,
2375
- workflow: configFile.workflow,
2376
- approval: configFile.approval
2373
+ configPath,
2374
+ configFilePresent: true,
2375
+ config: toProjectConfig(
2376
+ resolvedDocsDir,
2377
+ configFile,
2378
+ projectType,
2379
+ components
2380
+ )
2377
2381
  };
2378
2382
  } catch {
2379
2383
  }
2380
2384
  }
2381
- const agentsPath = path15.join(resolvedDocsDir, "agents");
2382
- const featuresPath = path15.join(resolvedDocsDir, "features");
2385
+ const agentsPath = path16.join(resolvedDocsDir, "agents");
2386
+ const featuresPath = path16.join(resolvedDocsDir, "features");
2383
2387
  if (await fs.pathExists(agentsPath) && await fs.pathExists(featuresPath)) {
2384
2388
  const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
2385
2389
  const projectType = inferredComponents.length > 0 ? "multi" : "single";
2386
2390
  const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
2387
2391
  const langProbeCandidates = [
2388
- path15.join(agentsPath, "custom.md"),
2389
- path15.join(agentsPath, "constitution.md"),
2390
- path15.join(agentsPath, "agents.md")
2392
+ path16.join(agentsPath, "custom.md"),
2393
+ path16.join(agentsPath, "constitution.md"),
2394
+ path16.join(agentsPath, "agents.md")
2391
2395
  ];
2392
2396
  let lang = "en";
2393
2397
  for (const candidate of langProbeCandidates) {
@@ -2398,11 +2402,198 @@ async function getConfig(cwd) {
2398
2402
  break;
2399
2403
  }
2400
2404
  }
2401
- return { docsDir: resolvedDocsDir, projectType, components, lang };
2405
+ return {
2406
+ detected: true,
2407
+ schemaId: "lee-spec",
2408
+ detectionSource: "heuristic",
2409
+ docsDir: resolvedDocsDir,
2410
+ configPath: null,
2411
+ configFilePresent: false,
2412
+ config: {
2413
+ schemaId: "lee-spec",
2414
+ docsDir: resolvedDocsDir,
2415
+ projectType,
2416
+ components,
2417
+ lang
2418
+ }
2419
+ };
2402
2420
  }
2403
2421
  }
2404
2422
  }
2405
- return null;
2423
+ return {
2424
+ detected: false,
2425
+ schemaId: "lee-spec",
2426
+ detectionSource: null,
2427
+ docsDir: null,
2428
+ configPath: null,
2429
+ configFilePresent: false,
2430
+ config: null
2431
+ };
2432
+ }
2433
+ function resolveLeeSpecFeaturePaths(input) {
2434
+ const featureFolderName = `${input.featureId}-${input.featureName}`;
2435
+ const featuresDir = input.projectType === "multi" ? path16.join(input.docsDir, "features", input.component || "") : path16.join(input.docsDir, "features");
2436
+ const featureDir = path16.join(featuresDir, featureFolderName);
2437
+ return {
2438
+ featureFolderName,
2439
+ featuresDir,
2440
+ featureDir,
2441
+ featurePathFromDocs: path16.relative(input.docsDir, featureDir)
2442
+ };
2443
+ }
2444
+ async function getNextLeeSpecFeatureId(docsDir, projectType, components) {
2445
+ const featuresDir = path16.join(docsDir, "features");
2446
+ let max = 0;
2447
+ const scanDirs = [];
2448
+ if (projectType === "multi") {
2449
+ scanDirs.push(
2450
+ ...components.map((component) => path16.join(featuresDir, component))
2451
+ );
2452
+ } else {
2453
+ scanDirs.push(featuresDir);
2454
+ }
2455
+ for (const dir of scanDirs) {
2456
+ if (!await fs.pathExists(dir)) continue;
2457
+ const entries = await fs.readdir(dir, { withFileTypes: true });
2458
+ for (const entry of entries) {
2459
+ if (!entry.isDirectory()) continue;
2460
+ const match = entry.name.match(/^F(\d+)-/);
2461
+ if (!match) continue;
2462
+ const num = parseInt(match[1], 10);
2463
+ if (num > max) max = num;
2464
+ }
2465
+ }
2466
+ const next = max + 1;
2467
+ const width = Math.max(3, String(next).length);
2468
+ return `F${String(next).padStart(width, "0")}`;
2469
+ }
2470
+ function parseFeatureFolderName(folderName, component) {
2471
+ const match = folderName.match(/^(F\d+)-(.+)$/);
2472
+ if (!match) return null;
2473
+ const featureRef = {
2474
+ id: match[1],
2475
+ slug: match[2],
2476
+ folderName
2477
+ };
2478
+ if (component) {
2479
+ featureRef.component = component;
2480
+ }
2481
+ return featureRef;
2482
+ }
2483
+ async function listLeeSpecFeatures(cwd) {
2484
+ const detected = await detectLeeSpecProject(cwd);
2485
+ const docsDir = detected.docsDir;
2486
+ if (!docsDir) return [];
2487
+ const featuresRoot = path16.join(docsDir, "features");
2488
+ if (!await fs.pathExists(featuresRoot)) return [];
2489
+ const refs = [];
2490
+ const topLevelEntries = await fs.readdir(featuresRoot, { withFileTypes: true });
2491
+ for (const entry of topLevelEntries) {
2492
+ if (!entry.isDirectory()) continue;
2493
+ const singleProjectFeature = parseFeatureFolderName(entry.name);
2494
+ if (singleProjectFeature) {
2495
+ refs.push(singleProjectFeature);
2496
+ continue;
2497
+ }
2498
+ const component = entry.name.trim().toLowerCase();
2499
+ if (!component) continue;
2500
+ const componentDir = path16.join(featuresRoot, entry.name);
2501
+ const componentEntries = await fs.readdir(componentDir, { withFileTypes: true });
2502
+ for (const child of componentEntries) {
2503
+ if (!child.isDirectory()) continue;
2504
+ const featureRef = parseFeatureFolderName(child.name, component);
2505
+ if (featureRef) refs.push(featureRef);
2506
+ }
2507
+ }
2508
+ refs.sort((left, right) => {
2509
+ const leftKey = `${left.id || ""}:${left.component || ""}:${left.folderName}`;
2510
+ const rightKey = `${right.id || ""}:${right.component || ""}:${right.folderName}`;
2511
+ return leftKey.localeCompare(rightKey);
2512
+ });
2513
+ return refs;
2514
+ }
2515
+
2516
+ // src/adapters/schema/lee-spec-kit/index.ts
2517
+ var leeSpecSchemaAdapter = {
2518
+ schemaId: "lee-spec",
2519
+ async detect(cwd) {
2520
+ const detection = await detectLeeSpecProject(cwd);
2521
+ return {
2522
+ detected: detection.detected,
2523
+ docsDir: detection.docsDir,
2524
+ schemaId: detection.schemaId,
2525
+ detectionSource: detection.detectionSource,
2526
+ config: detection.config,
2527
+ configPath: detection.configPath,
2528
+ configFilePresent: detection.configFilePresent
2529
+ };
2530
+ },
2531
+ async listFeatures(cwd) {
2532
+ return listLeeSpecFeatures(cwd);
2533
+ },
2534
+ async getNextFeatureId(input) {
2535
+ return getNextLeeSpecFeatureId(
2536
+ input.docsDir,
2537
+ input.projectType,
2538
+ input.components
2539
+ );
2540
+ },
2541
+ resolveFeaturePaths(input) {
2542
+ return resolveLeeSpecFeaturePaths(input);
2543
+ }
2544
+ };
2545
+
2546
+ // src/adapters/schema/index.ts
2547
+ var SCHEMA_ADAPTERS = [leeSpecSchemaAdapter];
2548
+ function createEmptyDetection() {
2549
+ return {
2550
+ detected: false,
2551
+ docsDir: null,
2552
+ schemaId: null,
2553
+ detectionSource: null,
2554
+ config: null,
2555
+ configPath: null,
2556
+ configFilePresent: false,
2557
+ adapter: null
2558
+ };
2559
+ }
2560
+ function getSchemaAdapterById(schemaId) {
2561
+ if (!schemaId) return null;
2562
+ return SCHEMA_ADAPTERS.find((adapter) => adapter.schemaId === schemaId) ?? null;
2563
+ }
2564
+ function getSchemaAdapterForConfig(config) {
2565
+ return getSchemaAdapterById(config?.schemaId ?? null);
2566
+ }
2567
+ async function detectSchemaProject(cwd) {
2568
+ for (const adapter of SCHEMA_ADAPTERS) {
2569
+ const detection = await adapter.detect(cwd);
2570
+ if (detection.detected) {
2571
+ return {
2572
+ ...detection,
2573
+ adapter
2574
+ };
2575
+ }
2576
+ }
2577
+ return createEmptyDetection();
2578
+ }
2579
+
2580
+ // src/config/load.ts
2581
+ async function getConfig(cwd) {
2582
+ const detected = await detectSchemaProject(cwd);
2583
+ return detected.config;
2584
+ }
2585
+
2586
+ // src/config/types.ts
2587
+ var DEFAULT_APPROVAL_REQUIRE_CHECK_CATEGORIES = [
2588
+ "spec_approve",
2589
+ "implementation_approve"
2590
+ ];
2591
+ function createDefaultApprovalConfig() {
2592
+ return {
2593
+ mode: "category",
2594
+ default: "skip",
2595
+ requireCheckCategories: [...DEFAULT_APPROVAL_REQUIRE_CHECK_CATEGORIES]
2596
+ };
2406
2597
  }
2407
2598
 
2408
2599
  // src/commands/init.ts
@@ -2468,7 +2659,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
2468
2659
  }
2469
2660
  async function runInit(options) {
2470
2661
  const cwd = process.cwd();
2471
- const defaultName = path15.basename(cwd);
2662
+ const defaultName = path16.basename(cwd);
2472
2663
  let projectName = options.name || defaultName;
2473
2664
  let projectType = options.type;
2474
2665
  let components = parseComponentsOption(options.components);
@@ -2479,7 +2670,7 @@ async function runInit(options) {
2479
2670
  let docsRemote = options.docsRemote;
2480
2671
  let projectRoot;
2481
2672
  const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
2482
- const targetDir = path15.resolve(cwd, options.dir || "./docs");
2673
+ const targetDir = path16.resolve(cwd, options.dir || "./docs");
2483
2674
  const skipPrompts = !!options.yes || !!options.nonInteractive;
2484
2675
  if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
2485
2676
  throw createCliError(
@@ -2876,7 +3067,7 @@ async function runInit(options) {
2876
3067
  );
2877
3068
  console.log();
2878
3069
  const templatesDir = getTemplatesDir();
2879
- const commonPath = path15.join(templatesDir, lang, "common");
3070
+ const commonPath = path16.join(templatesDir, lang, "common");
2880
3071
  if (!await fs.pathExists(commonPath)) {
2881
3072
  throw new Error(
2882
3073
  tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
@@ -2885,11 +3076,11 @@ async function runInit(options) {
2885
3076
  const fsAdapter = new DefaultFileSystemAdapter();
2886
3077
  await copyTemplates(fsAdapter, commonPath, targetDir);
2887
3078
  if (projectType === "multi") {
2888
- const featuresRoot = path15.join(targetDir, "features");
3079
+ const featuresRoot = path16.join(targetDir, "features");
2889
3080
  for (const component of components) {
2890
- const componentDir = path15.join(featuresRoot, component);
3081
+ const componentDir = path16.join(featuresRoot, component);
2891
3082
  await fs.ensureDir(componentDir);
2892
- const readmePath = path15.join(componentDir, "README.md");
3083
+ const readmePath = path16.join(componentDir, "README.md");
2893
3084
  if (!await fs.pathExists(readmePath)) {
2894
3085
  await fs.writeFile(
2895
3086
  readmePath,
@@ -2916,6 +3107,7 @@ async function runInit(options) {
2916
3107
  createdAt: getLocalDateString(),
2917
3108
  docsRepo,
2918
3109
  workflow: {
3110
+ preset: workflowMode,
2919
3111
  mode: workflowMode,
2920
3112
  requireWorktree: false,
2921
3113
  codeDirtyScope: "auto",
@@ -2948,20 +3140,20 @@ async function runInit(options) {
2948
3140
  config.projectRoot = projectRoot;
2949
3141
  }
2950
3142
  }
2951
- const configPath = path15.join(targetDir, ".lee-spec-kit.json");
3143
+ const configPath = path16.join(targetDir, ".lee-spec-kit.json");
2952
3144
  await fs.writeJson(configPath, config, { spaces: 2 });
2953
3145
  const extraCommitPathsAbs = [];
2954
3146
  try {
2955
3147
  if (docsRepo === "embedded") {
2956
3148
  const repoRoot = getGitTopLevelOrNull(cwd) || cwd;
2957
- const agentsMdPath = path15.join(repoRoot, "AGENTS.md");
3149
+ const agentsMdPath = path16.join(repoRoot, "AGENTS.md");
2958
3150
  const result = await upsertLeeSpecKitAgentsMd(agentsMdPath, {
2959
3151
  lang,
2960
3152
  docsRepo
2961
3153
  });
2962
3154
  if (result.changed) extraCommitPathsAbs.push(agentsMdPath);
2963
3155
  } else {
2964
- await upsertLeeSpecKitAgentsMd(path15.join(targetDir, "AGENTS.md"), {
3156
+ await upsertLeeSpecKitAgentsMd(path16.join(targetDir, "AGENTS.md"), {
2965
3157
  lang,
2966
3158
  docsRepo
2967
3159
  });
@@ -2971,16 +3163,16 @@ async function runInit(options) {
2971
3163
  } else if (projectRoot && typeof projectRoot === "object") {
2972
3164
  roots.push(...Object.values(projectRoot));
2973
3165
  }
2974
- const resolvedCwd = path15.resolve(cwd);
3166
+ const resolvedCwd = path16.resolve(cwd);
2975
3167
  for (const raw of roots) {
2976
3168
  const value = String(raw || "").trim();
2977
3169
  if (!value) continue;
2978
- const abs = path15.resolve(cwd, value);
2979
- if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path15.sep}`)) {
3170
+ const abs = path16.resolve(cwd, value);
3171
+ if (abs === resolvedCwd || abs.startsWith(`${resolvedCwd}${path16.sep}`)) {
2980
3172
  if (await fs.pathExists(abs)) {
2981
3173
  const stat = await fs.stat(abs);
2982
3174
  if (stat.isDirectory()) {
2983
- await upsertLeeSpecKitAgentsMd(path15.join(abs, "AGENTS.md"), {
3175
+ await upsertLeeSpecKitAgentsMd(path16.join(abs, "AGENTS.md"), {
2984
3176
  lang,
2985
3177
  docsRepo
2986
3178
  });
@@ -3073,7 +3265,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
3073
3265
  console.log(chalk9.blue(tr(lang, "cli", "init.log.gitInit")));
3074
3266
  runGitOrThrow(["init"], gitWorkdir);
3075
3267
  }
3076
- const relativePath = docsRepo === "standalone" ? "." : path15.relative(gitWorkdir, targetDir);
3268
+ const relativePath = docsRepo === "standalone" ? "." : path16.relative(gitWorkdir, targetDir);
3077
3269
  const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
3078
3270
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
3079
3271
  console.log(chalk9.yellow(tr(lang, "cli", "init.warn.stagedChangesSkip")));
@@ -3100,7 +3292,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote, ext
3100
3292
  console.log();
3101
3293
  return;
3102
3294
  }
3103
- const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path15.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
3295
+ const extraRelativePaths = extraCommitPathsAbs.map((absPath) => path16.relative(gitWorkdir, absPath)).map((p) => p.replace(/\\/g, "/").trim()).filter((p) => !!p && p !== "." && !p.startsWith("../"));
3104
3296
  const pathsToStage = [relativePath, ...extraRelativePaths];
3105
3297
  for (const p of pathsToStage) {
3106
3298
  runGitOrThrow(["add", p], gitWorkdir);
@@ -3188,13 +3380,13 @@ async function patchMarkdownIfExists(filePath, transform) {
3188
3380
  await fs.writeFile(filePath, transform(content), "utf-8");
3189
3381
  }
3190
3382
  async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
3191
- await patchMarkdownIfExists(path15.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3383
+ await patchMarkdownIfExists(path16.join(featureDir, "spec.md"), sanitizeSpecForLocal);
3192
3384
  await patchMarkdownIfExists(
3193
- path15.join(featureDir, "tasks.md"),
3385
+ path16.join(featureDir, "tasks.md"),
3194
3386
  (content) => sanitizeTasksForLocal(content, lang)
3195
3387
  );
3196
- await fs.remove(path15.join(featureDir, "issue.md"));
3197
- await fs.remove(path15.join(featureDir, "pr.md"));
3388
+ await fs.remove(path16.join(featureDir, "issue.md"));
3389
+ await fs.remove(path16.join(featureDir, "pr.md"));
3198
3390
  }
3199
3391
  var IDEA_REF_PATTERN = /\b(I\d{3,}(?:-[A-Za-z0-9._-]+)?)\b/;
3200
3392
  var IDEA_PATH_PATTERN = /\b(?:\.\/)?docs\/ideas\/[^\s]+\.md\b/;
@@ -3206,7 +3398,7 @@ function extractExplicitIdeaRef(requestText) {
3206
3398
  return null;
3207
3399
  }
3208
3400
  async function resolveIdeaReference(docsDir, ref, lang) {
3209
- const ideasDir = path15.join(docsDir, "ideas");
3401
+ const ideasDir = path16.join(docsDir, "ideas");
3210
3402
  const trimmedRef = ref.trim();
3211
3403
  if (!trimmedRef) {
3212
3404
  throw createCliError(
@@ -3215,7 +3407,7 @@ async function resolveIdeaReference(docsDir, ref, lang) {
3215
3407
  );
3216
3408
  }
3217
3409
  if (trimmedRef.includes("/") || trimmedRef.endsWith(".md")) {
3218
- const candidate = path15.resolve(process.cwd(), trimmedRef);
3410
+ const candidate = path16.resolve(process.cwd(), trimmedRef);
3219
3411
  if (await fs.pathExists(candidate)) {
3220
3412
  return { path: candidate };
3221
3413
  }
@@ -3234,11 +3426,11 @@ async function resolveIdeaReference(docsDir, ref, lang) {
3234
3426
  const files = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md")).map((entry) => entry.name);
3235
3427
  const exactName = `${trimmedRef}.md`;
3236
3428
  if (files.includes(exactName)) {
3237
- return { path: path15.join(ideasDir, exactName) };
3429
+ return { path: path16.join(ideasDir, exactName) };
3238
3430
  }
3239
3431
  const byId = /^I\d{3,}$/.test(trimmedRef) ? files.filter((name) => name.startsWith(`${trimmedRef}-`)) : [];
3240
3432
  if (byId.length === 1) {
3241
- return { path: path15.join(ideasDir, byId[0]) };
3433
+ return { path: path16.join(ideasDir, byId[0]) };
3242
3434
  }
3243
3435
  if (byId.length > 1) {
3244
3436
  throw createCliError(
@@ -3262,7 +3454,7 @@ async function readIdeaMetadataValue(ideaPath, label) {
3262
3454
  async function deriveFeatureNameFromIdea(ideaPath) {
3263
3455
  const ideaName = await readIdeaMetadataValue(ideaPath, "Idea Name");
3264
3456
  if (ideaName && ideaName !== "-") return ideaName;
3265
- const basename = path15.basename(ideaPath, ".md");
3457
+ const basename = path16.basename(ideaPath, ".md");
3266
3458
  return basename.replace(/^I\d{3,}-/, "");
3267
3459
  }
3268
3460
  function escapeRegExp(value) {
@@ -3348,6 +3540,7 @@ async function runFeature(name, options) {
3348
3540
  }
3349
3541
  const { docsDir, projectType, lang } = config;
3350
3542
  const projectName = config.projectName;
3543
+ const schemaAdapter = getSchemaAdapterForConfig(config);
3351
3544
  const configuredComponents = resolveProjectComponents(
3352
3545
  projectType,
3353
3546
  config.components
@@ -3410,27 +3603,38 @@ async function runFeature(name, options) {
3410
3603
  );
3411
3604
  featureId = options.id;
3412
3605
  } else {
3413
- featureId = await getNextFeatureId(
3606
+ if (!schemaAdapter?.getNextFeatureId) {
3607
+ throw createCliError(
3608
+ "PRECONDITION_FAILED",
3609
+ `Schema "${config.schemaId || "unknown"}" does not support feature ID allocation.`
3610
+ );
3611
+ }
3612
+ featureId = await schemaAdapter.getNextFeatureId({
3414
3613
  docsDir,
3415
3614
  projectType,
3416
- configuredComponents
3417
- );
3615
+ components: configuredComponents
3616
+ });
3418
3617
  }
3419
- let featuresDir;
3420
- if (projectType === "multi") {
3421
- featuresDir = path15.join(docsDir, "features", component);
3422
- } else {
3423
- featuresDir = path15.join(docsDir, "features");
3618
+ if (!schemaAdapter?.resolveFeaturePaths) {
3619
+ throw createCliError(
3620
+ "PRECONDITION_FAILED",
3621
+ `Schema "${config.schemaId || "unknown"}" does not support feature path resolution.`
3622
+ );
3424
3623
  }
3425
- const featureFolderName = `${featureId}-${name}`;
3426
- const featureDir = path15.join(featuresDir, featureFolderName);
3624
+ const { featureFolderName, featureDir, featurePathFromDocs } = schemaAdapter.resolveFeaturePaths({
3625
+ docsDir,
3626
+ projectType,
3627
+ component: projectType === "multi" ? component : void 0,
3628
+ featureId,
3629
+ featureName: name
3630
+ });
3427
3631
  if (await fs.pathExists(featureDir)) {
3428
3632
  throw createCliError(
3429
3633
  "INVALID_ARGUMENT",
3430
3634
  tr(lang, "cli", "feature.folderExists", { path: featureDir })
3431
3635
  );
3432
3636
  }
3433
- const featureBasePath = path15.join(
3637
+ const featureBasePath = path16.join(
3434
3638
  getTemplatesDir(),
3435
3639
  lang,
3436
3640
  "common",
@@ -3477,8 +3681,8 @@ async function runFeature(name, options) {
3477
3681
  await replaceInFiles(fsAdapter, featureDir, replacements);
3478
3682
  if (linkedIdea) {
3479
3683
  await stampIdeaReferenceInSpec(
3480
- path15.join(featureDir, "spec.md"),
3481
- path15.relative(featureDir, linkedIdea.path)
3684
+ path16.join(featureDir, "spec.md"),
3685
+ path16.relative(featureDir, linkedIdea.path)
3482
3686
  );
3483
3687
  await markIdeaAsFeatureized(linkedIdea.path, featureFolderName);
3484
3688
  }
@@ -3506,7 +3710,7 @@ async function runFeature(name, options) {
3506
3710
  featureName: name,
3507
3711
  component: projectType === "multi" ? component : void 0,
3508
3712
  featurePath: featureDir,
3509
- featurePathFromDocs: path15.relative(docsDir, featureDir)
3713
+ featurePathFromDocs
3510
3714
  };
3511
3715
  },
3512
3716
  { owner: "feature" }
@@ -3566,9 +3770,9 @@ function escapeRegExp2(value) {
3566
3770
  async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3567
3771
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
3568
3772
  const candidates = [
3569
- ...explicitDocsDir ? [path15.resolve(explicitDocsDir)] : [],
3570
- path15.resolve(cwd, "docs"),
3571
- path15.resolve(cwd)
3773
+ ...explicitDocsDir ? [path16.resolve(explicitDocsDir)] : [],
3774
+ path16.resolve(cwd, "docs"),
3775
+ path16.resolve(cwd)
3572
3776
  ];
3573
3777
  const endAt = Date.now() + timeoutMs;
3574
3778
  while (Date.now() < endAt) {
@@ -3594,33 +3798,6 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
3594
3798
  }
3595
3799
  return getConfig(cwd);
3596
3800
  }
3597
- async function getNextFeatureId(docsDir, projectType, components) {
3598
- const featuresDir = path15.join(docsDir, "features");
3599
- let max = 0;
3600
- const scanDirs = [];
3601
- if (projectType === "multi") {
3602
- scanDirs.push(
3603
- ...components.map((component) => path15.join(featuresDir, component))
3604
- );
3605
- } else {
3606
- scanDirs.push(featuresDir);
3607
- }
3608
- for (const dir of scanDirs) {
3609
- if (!await fs.pathExists(dir)) continue;
3610
- const entries = await fs.readdir(dir, { withFileTypes: true });
3611
- for (const entry of entries) {
3612
- if (!entry.isDirectory()) continue;
3613
- const match = entry.name.match(/^F(\d+)-/);
3614
- if (match) {
3615
- const num = parseInt(match[1], 10);
3616
- if (num > max) max = num;
3617
- }
3618
- }
3619
- }
3620
- const next = max + 1;
3621
- const width = Math.max(3, String(next).length);
3622
- return `F${String(next).padStart(width, "0")}`;
3623
- }
3624
3801
  function ideaCommand(program2) {
3625
3802
  program2.command("idea <name>").description("Create a new indexed idea document").option("--component <component>", "Component name (optional)").option("--id <id>", "Idea ID (default: auto)").option("-d, --desc <description>", "Idea description for the document").option("--non-interactive", "Reserved for parity with other generators").option("--json", "Output in JSON format for agents").action(async (name, options) => {
3626
3803
  try {
@@ -3701,16 +3878,16 @@ async function runIdea(name, options) {
3701
3878
  getDocsLockPath(docsDir),
3702
3879
  async () => {
3703
3880
  const ideaId = options.id ? validateProvidedIdeaId(options.id, lang) : await getNextIdeaId(docsDir);
3704
- const ideasDir = path15.join(docsDir, "ideas");
3881
+ const ideasDir = path16.join(docsDir, "ideas");
3705
3882
  const ideaFileName = `${ideaId}-${name}.md`;
3706
- const ideaPath = path15.join(ideasDir, ideaFileName);
3883
+ const ideaPath = path16.join(ideasDir, ideaFileName);
3707
3884
  if (await fs.pathExists(ideaPath)) {
3708
3885
  throw createCliError(
3709
3886
  "INVALID_ARGUMENT",
3710
3887
  tr(lang, "cli", "idea.fileExists", { path: ideaPath })
3711
3888
  );
3712
3889
  }
3713
- const templatePath = path15.join(
3890
+ const templatePath = path16.join(
3714
3891
  getTemplatesDir(),
3715
3892
  lang,
3716
3893
  "common",
@@ -3748,7 +3925,7 @@ async function runIdea(name, options) {
3748
3925
  ideaName: name,
3749
3926
  component: component || void 0,
3750
3927
  ideaPath,
3751
- ideaPathFromDocs: path15.relative(docsDir, ideaPath)
3928
+ ideaPathFromDocs: path16.relative(docsDir, ideaPath)
3752
3929
  };
3753
3930
  },
3754
3931
  { owner: "idea" }
@@ -3766,7 +3943,7 @@ function applyIdeaTemplate(template, values) {
3766
3943
  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);
3767
3944
  }
3768
3945
  async function getNextIdeaId(docsDir) {
3769
- const ideasDir = path15.join(docsDir, "ideas");
3946
+ const ideasDir = path16.join(docsDir, "ideas");
3770
3947
  let max = 0;
3771
3948
  if (await fs.pathExists(ideasDir)) {
3772
3949
  const entries = await fs.readdir(ideasDir, { withFileTypes: true });
@@ -3895,25 +4072,33 @@ var SUBAGENT_HANDOFF_CATEGORIES = [
3895
4072
  "pre_pr_review_run"
3896
4073
  ];
3897
4074
 
3898
- // src/utils/workflow.ts
3899
- var DEFAULT_PRE_PR_REVIEW_SKILLS = ["code-review-excellence"];
3900
- var DEFAULT_PRE_PR_DECISION_ENUM = [
3901
- "approve",
3902
- "changes_requested",
3903
- "blocked"
3904
- ];
3905
- function resolveWorkflowPolicy(workflow) {
3906
- const mode = workflow?.mode === "local" ? "local" : "github";
3907
- const policy = mode === "local" ? {
3908
- mode,
3909
- requireIssue: false,
3910
- requireBranch: false,
3911
- requireWorktree: false,
3912
- requirePr: false,
3913
- requireReview: false,
3914
- requireMerge: false
3915
- } : {
3916
- mode,
4075
+ // src/core/workflow/presets.ts
4076
+ function resolveWorkflowPreset(preset, mode) {
4077
+ const normalizedPreset = preset === "local" || preset === "strict" || preset === "github" ? preset : void 0;
4078
+ if (normalizedPreset === "local" || mode === "local") {
4079
+ return {
4080
+ mode: "local",
4081
+ requireIssue: false,
4082
+ requireBranch: false,
4083
+ requireWorktree: false,
4084
+ requirePr: false,
4085
+ requireReview: false,
4086
+ requireMerge: false
4087
+ };
4088
+ }
4089
+ if (normalizedPreset === "strict") {
4090
+ return {
4091
+ mode: "github",
4092
+ requireIssue: true,
4093
+ requireBranch: true,
4094
+ requireWorktree: true,
4095
+ requirePr: true,
4096
+ requireReview: true,
4097
+ requireMerge: true
4098
+ };
4099
+ }
4100
+ return {
4101
+ mode: "github",
3917
4102
  requireIssue: true,
3918
4103
  requireBranch: true,
3919
4104
  requireWorktree: false,
@@ -3921,6 +4106,20 @@ function resolveWorkflowPolicy(workflow) {
3921
4106
  requireReview: true,
3922
4107
  requireMerge: true
3923
4108
  };
4109
+ }
4110
+
4111
+ // src/core/workflow/policies.ts
4112
+ var DEFAULT_PRE_PR_REVIEW_SKILLS = ["code-review-excellence"];
4113
+ var DEFAULT_PRE_PR_DECISION_ENUM = [
4114
+ "approve",
4115
+ "changes_requested",
4116
+ "blocked"
4117
+ ];
4118
+ function resolveWorkflowPolicy(workflow) {
4119
+ const policy = resolveWorkflowPreset(
4120
+ workflow?.preset,
4121
+ workflow?.mode
4122
+ );
3924
4123
  if (typeof workflow?.requireIssue === "boolean") {
3925
4124
  policy.requireIssue = workflow.requireIssue;
3926
4125
  }
@@ -3986,18 +4185,9 @@ function normalizeDecisionEnumList(input) {
3986
4185
  for (const raw of input) {
3987
4186
  const value = String(raw || "").trim().toLowerCase();
3988
4187
  if (!value) continue;
3989
- if (value === "approve") {
3990
- deduped.add("approve");
3991
- continue;
3992
- }
3993
- if (value === "changes_requested") {
3994
- deduped.add("changes_requested");
3995
- continue;
3996
- }
3997
- if (value === "blocked") {
3998
- deduped.add("blocked");
3999
- continue;
4000
- }
4188
+ if (value === "approve") deduped.add("approve");
4189
+ if (value === "changes_requested") deduped.add("changes_requested");
4190
+ if (value === "blocked") deduped.add("blocked");
4001
4191
  }
4002
4192
  return [...deduped];
4003
4193
  }
@@ -4034,6 +4224,15 @@ function resolvePrePrReviewPolicy(workflow) {
4034
4224
  };
4035
4225
  }
4036
4226
 
4227
+ // src/core/workflow/engine.ts
4228
+ function resolveWorkflowRuntime(workflow) {
4229
+ return {
4230
+ workflowPolicy: resolveWorkflowPolicy(workflow),
4231
+ prePrReviewPolicy: resolvePrePrReviewPolicy(workflow),
4232
+ taskCommitGatePolicy: resolveTaskCommitGatePolicy(workflow)
4233
+ };
4234
+ }
4235
+
4037
4236
  // src/utils/agent-orchestration.ts
4038
4237
  function getPrePrReviewPrompt(lang, skills, fallbackText) {
4039
4238
  if (lang === "ko") {
@@ -4207,7 +4406,7 @@ function isNonNegativeIntegerValue(value) {
4207
4406
  }
4208
4407
  function isLikelyCurrentPrePrReviewEvidence(evidencePath, feature) {
4209
4408
  try {
4210
- const raw = fs12.readFileSync(evidencePath, "utf-8");
4409
+ const raw = fs13.readFileSync(evidencePath, "utf-8");
4211
4410
  const parsed = JSON.parse(raw);
4212
4411
  const evidenceFeature = (parsed.feature || "").toString().trim();
4213
4412
  if (evidenceFeature && evidenceFeature !== feature.folderName) {
@@ -4223,31 +4422,31 @@ function resolvePrePrReviewEvidencePath(feature) {
4223
4422
  const candidates = [];
4224
4423
  const explicit = (feature.prePrReview.evidence || "").trim();
4225
4424
  if (explicit && explicit !== "-") {
4226
- if (path15.isAbsolute(explicit)) {
4425
+ if (path16.isAbsolute(explicit)) {
4227
4426
  candidates.push(explicit);
4228
4427
  } else {
4229
- candidates.push(path15.resolve(feature.path, explicit));
4230
- candidates.push(path15.resolve(docsRoot, explicit));
4428
+ candidates.push(path16.resolve(feature.path, explicit));
4429
+ candidates.push(path16.resolve(docsRoot, explicit));
4231
4430
  const normalizedExplicit = explicit.replace(/\\/g, "/");
4232
4431
  if (normalizedExplicit.startsWith("docs/")) {
4233
4432
  const withoutDocsPrefix = normalizedExplicit.slice("docs/".length);
4234
4433
  if (withoutDocsPrefix) {
4235
- candidates.push(path15.resolve(docsRoot, withoutDocsPrefix));
4434
+ candidates.push(path16.resolve(docsRoot, withoutDocsPrefix));
4236
4435
  }
4237
4436
  }
4238
4437
  }
4239
4438
  }
4240
- candidates.push(path15.join(feature.path, "review-trace.json"));
4241
- candidates.push(path15.join(docsRoot, "review-trace.json"));
4439
+ candidates.push(path16.join(feature.path, "review-trace.json"));
4440
+ candidates.push(path16.join(docsRoot, "review-trace.json"));
4242
4441
  const seen = /* @__PURE__ */ new Set();
4243
4442
  for (const candidate of candidates) {
4244
- const abs = path15.resolve(candidate);
4443
+ const abs = path16.resolve(candidate);
4245
4444
  if (seen.has(abs)) continue;
4246
4445
  seen.add(abs);
4247
- if (!fs12.existsSync(abs)) continue;
4446
+ if (!fs13.existsSync(abs)) continue;
4248
4447
  if (!abs.toLowerCase().endsWith(".json")) continue;
4249
4448
  if (!isLikelyCurrentPrePrReviewEvidence(abs, feature)) continue;
4250
- const rel = path15.relative(docsRoot, abs).replace(/\\/g, "/");
4449
+ const rel = path16.relative(docsRoot, abs).replace(/\\/g, "/");
4251
4450
  if (rel && !rel.startsWith("../")) {
4252
4451
  return rel;
4253
4452
  }
@@ -4288,8 +4487,8 @@ function getReviewFixCommitGuidance(feature, lang, options) {
4288
4487
  }
4289
4488
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
4290
4489
  if (!projectGitCwd) return null;
4291
- const normalized = path15.resolve(projectGitCwd);
4292
- const marker = `${path15.sep}.worktrees${path15.sep}`;
4490
+ const normalized = path16.resolve(projectGitCwd);
4491
+ const marker = `${path16.sep}.worktrees${path16.sep}`;
4293
4492
  const markerIndex = normalized.lastIndexOf(marker);
4294
4493
  if (markerIndex <= 0) return null;
4295
4494
  const projectRoot = normalized.slice(0, markerIndex);
@@ -4336,7 +4535,7 @@ function toTaskKey(rawTitle) {
4336
4535
  function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
4337
4536
  const docsGitCwd = feature.git.docsGitCwd;
4338
4537
  const tasksRelativePath = normalizeGitRelativePath(
4339
- path15.join(feature.docs.featurePathFromDocs, "tasks.md")
4538
+ path16.join(feature.docs.featurePathFromDocs, "tasks.md")
4340
4539
  );
4341
4540
  const diff = readGitText(ctx, docsGitCwd, [
4342
4541
  "diff",
@@ -4411,7 +4610,7 @@ function checkTaskCommitGate(ctx, feature) {
4411
4610
  return { pass: true };
4412
4611
  }
4413
4612
  const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
4414
- const relativeDocsDir = path15.relative(projectGitCwd, feature.git.docsGitCwd);
4613
+ const relativeDocsDir = path16.relative(projectGitCwd, feature.git.docsGitCwd);
4415
4614
  const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
4416
4615
  if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
4417
4616
  args.push(`:(exclude)${normalizedDocsDir}/**`);
@@ -4448,10 +4647,11 @@ function getTaskCommitGateReasonText(lang, check) {
4448
4647
  }
4449
4648
  function getStepDefinitions(ctx) {
4450
4649
  const lang = ctx.config.lang;
4451
- const workflow = ctx.config.workflow;
4452
- const workflowPolicy = resolveWorkflowPolicy(workflow);
4453
- const prePrReviewPolicy = resolvePrePrReviewPolicy(workflow);
4454
- const taskCommitGatePolicy = resolveTaskCommitGatePolicy(workflow);
4650
+ const {
4651
+ workflowPolicy,
4652
+ prePrReviewPolicy,
4653
+ taskCommitGatePolicy
4654
+ } = resolveWorkflowRuntime(ctx.config.workflow);
4455
4655
  const isTaskExecuteCurrent = (f) => f.docs.tasksExists && f.tasks.total > 0 && (f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && isTasksDocApproved(f) && (!workflowPolicy.requireBranch || f.git.onExpectedBranch || f.tasks.done === f.tasks.total);
4456
4656
  const isTaskExecuteWorktreeBlocked = (f) => isTaskExecuteCurrent(f) && workflowPolicy.requireWorktree && f.tasks.done < f.tasks.total && !!f.issueNumber && !f.git.projectInManagedWorktree;
4457
4657
  const isTaskExecuteFinalize = (f) => isTaskExecuteCurrent(f) && f.tasks.total === f.tasks.done && !isCompletionChecklistDone(f);
@@ -6006,17 +6206,17 @@ function isGitPathIgnored(ctx, cwd, relativePath) {
6006
6206
  }
6007
6207
  }
6008
6208
  var GIT_WORKTREE_CACHE = /* @__PURE__ */ new Map();
6009
- var WORKTREE_MARKER = `${path15.sep}.worktrees${path15.sep}`;
6209
+ var WORKTREE_MARKER = `${path16.sep}.worktrees${path16.sep}`;
6010
6210
  function resetContextGitCaches() {
6011
6211
  GIT_WORKTREE_CACHE.clear();
6012
6212
  }
6013
6213
  function isManagedWorktreePath(cwd) {
6014
6214
  if (!cwd) return false;
6015
- const normalized = path15.resolve(cwd);
6215
+ const normalized = path16.resolve(cwd);
6016
6216
  return normalized.includes(WORKTREE_MARKER);
6017
6217
  }
6018
6218
  function resolveProjectRootFromGitCwd(cwd) {
6019
- const normalized = path15.resolve(cwd);
6219
+ const normalized = path16.resolve(cwd);
6020
6220
  const markerIndex = normalized.lastIndexOf(WORKTREE_MARKER);
6021
6221
  if (markerIndex <= 0) return normalized;
6022
6222
  const projectRoot = normalized.slice(0, markerIndex);
@@ -6035,7 +6235,7 @@ function getGitTopLevel(ctx, cwd) {
6035
6235
  }
6036
6236
  function listGitWorktrees(ctx, cwd) {
6037
6237
  const topLevel = getGitTopLevel(ctx, cwd) || cwd;
6038
- const cacheKey = path15.resolve(topLevel);
6238
+ const cacheKey = path16.resolve(topLevel);
6039
6239
  const cached = GIT_WORKTREE_CACHE.get(cacheKey);
6040
6240
  if (cached) return cached;
6041
6241
  try {
@@ -6523,17 +6723,17 @@ function resolveLocalEvidencePathCandidates(rawValue, context) {
6523
6723
  if (!evidencePath) return [];
6524
6724
  if (/^https?:\/\//i.test(evidencePath)) return [];
6525
6725
  const candidates = /* @__PURE__ */ new Set();
6526
- if (path15.isAbsolute(evidencePath)) {
6527
- candidates.add(path15.resolve(evidencePath));
6726
+ if (path16.isAbsolute(evidencePath)) {
6727
+ candidates.add(path16.resolve(evidencePath));
6528
6728
  } else {
6529
- candidates.add(path15.resolve(context.featurePath, evidencePath));
6530
- candidates.add(path15.resolve(context.docsDir, evidencePath));
6531
- candidates.add(path15.resolve(path15.dirname(context.docsDir), evidencePath));
6729
+ candidates.add(path16.resolve(context.featurePath, evidencePath));
6730
+ candidates.add(path16.resolve(context.docsDir, evidencePath));
6731
+ candidates.add(path16.resolve(path16.dirname(context.docsDir), evidencePath));
6532
6732
  const normalizedEvidencePath = evidencePath.replace(/\\/g, "/");
6533
6733
  if (normalizedEvidencePath.startsWith("docs/")) {
6534
6734
  const withoutDocsPrefix = normalizedEvidencePath.slice("docs/".length);
6535
6735
  if (withoutDocsPrefix) {
6536
- candidates.add(path15.resolve(context.docsDir, withoutDocsPrefix));
6736
+ candidates.add(path16.resolve(context.docsDir, withoutDocsPrefix));
6537
6737
  }
6538
6738
  }
6539
6739
  }
@@ -6595,13 +6795,13 @@ function parsePrLink(value) {
6595
6795
  return trimmed;
6596
6796
  }
6597
6797
  function normalizeGitPath(value) {
6598
- return value.split(path15.sep).join("/");
6798
+ return value.split(path16.sep).join("/");
6599
6799
  }
6600
6800
  function resolveProjectStatusPaths(projectGitCwd, docsDir) {
6601
- const relativeDocsDir = path15.relative(projectGitCwd, docsDir);
6801
+ const relativeDocsDir = path16.relative(projectGitCwd, docsDir);
6602
6802
  if (!relativeDocsDir) return [];
6603
- if (path15.isAbsolute(relativeDocsDir)) return [];
6604
- if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path15.sep}`)) {
6803
+ if (path16.isAbsolute(relativeDocsDir)) return [];
6804
+ if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path16.sep}`)) {
6605
6805
  return [];
6606
6806
  }
6607
6807
  const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(
@@ -6639,7 +6839,7 @@ function getExpectedWorktreeCandidates(projectGitCwd, issueNumber, slug, folderN
6639
6839
  const seen = /* @__PURE__ */ new Set();
6640
6840
  const out = [];
6641
6841
  for (const name of names) {
6642
- const candidate = path15.resolve(projectRoot, ".worktrees", name);
6842
+ const candidate = path16.resolve(projectRoot, ".worktrees", name);
6643
6843
  if (seen.has(candidate)) continue;
6644
6844
  seen.add(candidate);
6645
6845
  out.push(candidate);
@@ -6653,7 +6853,7 @@ function resolveExistingExpectedWorktreePath(projectGitCwd, issueNumber, slug, f
6653
6853
  slug,
6654
6854
  folderName
6655
6855
  )) {
6656
- if (!fs12.existsSync(candidate)) continue;
6856
+ if (!fs13.existsSync(candidate)) continue;
6657
6857
  return candidate;
6658
6858
  }
6659
6859
  return void 0;
@@ -6683,7 +6883,7 @@ function resolveFeatureWorktreePath(ctx, projectGitCwd, issueNumber, slug, folde
6683
6883
  slug,
6684
6884
  folderName
6685
6885
  )) {
6686
- if (!fs12.existsSync(candidate)) continue;
6886
+ if (!fs13.existsSync(candidate)) continue;
6687
6887
  const branchName = getCurrentBranch(ctx, candidate);
6688
6888
  if (!expectedBranchesSet.has(branchName)) continue;
6689
6889
  return {
@@ -6824,10 +7024,10 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6824
7024
  const normalizedCandidates = uniqueNormalizedPaths(
6825
7025
  candidates.map((candidate) => {
6826
7026
  if (!candidate) return "";
6827
- if (!path15.isAbsolute(candidate)) return candidate;
6828
- const relative = path15.relative(projectGitCwd, candidate);
7027
+ if (!path16.isAbsolute(candidate)) return candidate;
7028
+ const relative = path16.relative(projectGitCwd, candidate);
6829
7029
  if (!relative) return "";
6830
- if (relative === ".." || relative.startsWith(`..${path15.sep}`))
7030
+ if (relative === ".." || relative.startsWith(`..${path16.sep}`))
6831
7031
  return "";
6832
7032
  return relative;
6833
7033
  }).filter(Boolean)
@@ -6841,7 +7041,7 @@ async function resolveComponentStatusPaths(ctx, projectGitCwd, component, workfl
6841
7041
  if (cached) return [...cached];
6842
7042
  const existing = [];
6843
7043
  for (const candidate of normalizedCandidates) {
6844
- if (await ctx.fs.pathExists(path15.join(projectGitCwd, candidate))) {
7044
+ if (await ctx.fs.pathExists(path16.join(projectGitCwd, candidate))) {
6845
7045
  existing.push(candidate);
6846
7046
  }
6847
7047
  }
@@ -6927,18 +7127,19 @@ function isPrePrReviewSatisfied2(feature, policy) {
6927
7127
  }
6928
7128
  async function parseFeature(ctx, featurePath, type, context, options) {
6929
7129
  const lang = options.lang;
6930
- const workflowPolicy = resolveWorkflowPolicy(options.workflow);
6931
- const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
6932
- const folderName = path15.basename(featurePath);
7130
+ const { workflowPolicy, prePrReviewPolicy } = resolveWorkflowRuntime(
7131
+ options.workflow
7132
+ );
7133
+ const folderName = path16.basename(featurePath);
6933
7134
  const match = folderName.match(/^(F\d+)-(.+)$/);
6934
7135
  const id = match?.[1];
6935
7136
  const slug = match?.[2] || folderName;
6936
- const specPath = path15.join(featurePath, "spec.md");
6937
- const planPath = path15.join(featurePath, "plan.md");
6938
- const tasksPath = path15.join(featurePath, "tasks.md");
6939
- const decisionsPath = path15.join(featurePath, "decisions.md");
6940
- const issueDocPath = path15.join(featurePath, "issue.md");
6941
- const prDocPath = path15.join(featurePath, "pr.md");
7137
+ const specPath = path16.join(featurePath, "spec.md");
7138
+ const planPath = path16.join(featurePath, "plan.md");
7139
+ const tasksPath = path16.join(featurePath, "tasks.md");
7140
+ const decisionsPath = path16.join(featurePath, "decisions.md");
7141
+ const issueDocPath = path16.join(featurePath, "issue.md");
7142
+ const prDocPath = path16.join(featurePath, "pr.md");
6942
7143
  let specStatus;
6943
7144
  let issueNumber;
6944
7145
  const specExists = await ctx.fs.pathExists(specPath);
@@ -7200,7 +7401,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7200
7401
  } else if (workflowPolicy.requireWorktree && tasksSummary.total > tasksSummary.done && !projectInManagedWorktree) {
7201
7402
  warnings.push(tr(lang, "warnings", "workflowWorktreeRequired"));
7202
7403
  }
7203
- const relativeFeaturePathFromDocs = path15.relative(
7404
+ const relativeFeaturePathFromDocs = path16.relative(
7204
7405
  context.docsDir,
7205
7406
  featurePath
7206
7407
  );
@@ -7566,7 +7767,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
7566
7767
  async function listFeatureDirs(ctx, rootDir) {
7567
7768
  const dirs = await listSubdirectories(ctx.fs, rootDir);
7568
7769
  return dirs.filter(
7569
- (value) => path15.basename(value).trim().toLowerCase() !== "feature-base"
7770
+ (value) => path16.basename(value).trim().toLowerCase() !== "feature-base"
7570
7771
  );
7571
7772
  }
7572
7773
  function normalizeRelPath(value) {
@@ -7707,7 +7908,7 @@ async function scanFeatures(ctx) {
7707
7908
  if (config.projectType === "single") {
7708
7909
  const featureDirs = await listFeatureDirs(
7709
7910
  ctx,
7710
- path15.join(config.docsDir, "features")
7911
+ path16.join(config.docsDir, "features")
7711
7912
  );
7712
7913
  componentFeatureDirs.set("single", featureDirs);
7713
7914
  allFeatureDirs.push(...featureDirs);
@@ -7719,14 +7920,14 @@ async function scanFeatures(ctx) {
7719
7920
  for (const component of components) {
7720
7921
  const componentDirs = await listFeatureDirs(
7721
7922
  ctx,
7722
- path15.join(config.docsDir, "features", component)
7923
+ path16.join(config.docsDir, "features", component)
7723
7924
  );
7724
7925
  componentFeatureDirs.set(component, componentDirs);
7725
7926
  allFeatureDirs.push(...componentDirs);
7726
7927
  }
7727
7928
  }
7728
7929
  const relativeFeaturePaths = allFeatureDirs.map(
7729
- (dir) => normalizeRelPath(path15.relative(config.docsDir, dir))
7930
+ (dir) => normalizeRelPath(path16.relative(config.docsDir, dir))
7730
7931
  );
7731
7932
  const docsGitMeta = buildDocsFeatureGitMeta(
7732
7933
  ctx,
@@ -7743,7 +7944,7 @@ async function scanFeatures(ctx) {
7743
7944
  const parsed = await Promise.all(
7744
7945
  target.dirs.map(async (dir) => {
7745
7946
  const relativeFeaturePathFromDocs = normalizeRelPath(
7746
- path15.relative(config.docsDir, dir)
7947
+ path16.relative(config.docsDir, dir)
7747
7948
  );
7748
7949
  const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
7749
7950
  return parseFeature(
@@ -7813,13 +8014,13 @@ async function runStatus(options) {
7813
8014
  );
7814
8015
  }
7815
8016
  const { docsDir, projectType, projectName, lang } = ctx.config;
7816
- const featuresDir = path15.join(docsDir, "features");
8017
+ const featuresDir = path16.join(docsDir, "features");
7817
8018
  const scan = await scanFeatures(ctx);
7818
8019
  const features = [];
7819
8020
  const idMap = /* @__PURE__ */ new Map();
7820
8021
  for (const f of scan.features) {
7821
8022
  const id = f.id || "UNKNOWN";
7822
- const relPath = path15.relative(docsDir, f.path);
8023
+ const relPath = path16.relative(docsDir, f.path);
7823
8024
  if (!idMap.has(id)) idMap.set(id, []);
7824
8025
  idMap.get(id).push(relPath);
7825
8026
  if (!f.docs.specExists || !f.docs.tasksExists) continue;
@@ -7910,7 +8111,7 @@ async function runStatus(options) {
7910
8111
  }
7911
8112
  console.log();
7912
8113
  if (options.write) {
7913
- const outputPath = path15.join(featuresDir, "status.md");
8114
+ const outputPath = path16.join(featuresDir, "status.md");
7914
8115
  const date = getLocalDateString();
7915
8116
  const content = [
7916
8117
  "# Feature Status",
@@ -7936,7 +8137,7 @@ function escapeRegExp4(value) {
7936
8137
  }
7937
8138
  async function getFeatureNameFromSpec(fsAdapter, featureDir, fallbackSlug, fallbackFolderName) {
7938
8139
  try {
7939
- const specPath = path15.join(featureDir, "spec.md");
8140
+ const specPath = path16.join(featureDir, "spec.md");
7940
8141
  if (!await fsAdapter.pathExists(specPath)) return fallbackSlug;
7941
8142
  const content = await fsAdapter.readFile(specPath, "utf-8");
7942
8143
  const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
@@ -8024,8 +8225,8 @@ async function runUpdate(options) {
8024
8225
  let updatedCount = 0;
8025
8226
  if (updateAgents) {
8026
8227
  console.log(chalk9.blue(tr(lang, "cli", "update.updatingAgents")));
8027
- const commonAgentsBase = path15.join(templatesDir, lang, "common", "agents");
8028
- const targetAgentsBase = path15.join(docsDir, "agents");
8228
+ const commonAgentsBase = path16.join(templatesDir, lang, "common", "agents");
8229
+ const targetAgentsBase = path16.join(docsDir, "agents");
8029
8230
  const commonAgents = commonAgentsBase;
8030
8231
  const targetAgents = targetAgentsBase;
8031
8232
  const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
@@ -8118,21 +8319,21 @@ async function collectAgentsMdTargets(cwd, config) {
8118
8319
  const targets = /* @__PURE__ */ new Set();
8119
8320
  const docsRepo = config.docsRepo ?? "embedded";
8120
8321
  if (docsRepo === "embedded") {
8121
- const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path15.resolve(config.docsDir, "..");
8122
- targets.add(path15.join(repoRoot, "AGENTS.md"));
8322
+ const repoRoot = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || path16.resolve(config.docsDir, "..");
8323
+ targets.add(path16.join(repoRoot, "AGENTS.md"));
8123
8324
  return [...targets];
8124
8325
  }
8125
- targets.add(path15.join(config.docsDir, "AGENTS.md"));
8326
+ targets.add(path16.join(config.docsDir, "AGENTS.md"));
8126
8327
  const baseDir = getGitTopLevelOrNull2(cwd) || getGitTopLevelOrNull2(config.docsDir) || process.cwd();
8127
8328
  const rawRoots = typeof config.projectRoot === "string" ? [config.projectRoot] : config.projectRoot && typeof config.projectRoot === "object" ? Object.values(config.projectRoot) : [];
8128
8329
  for (const rawRoot of rawRoots) {
8129
8330
  const value = String(rawRoot || "").trim();
8130
8331
  if (!value) continue;
8131
- const resolved = path15.resolve(baseDir, value);
8332
+ const resolved = path16.resolve(baseDir, value);
8132
8333
  if (!await fs.pathExists(resolved)) continue;
8133
8334
  const stat = await fs.stat(resolved);
8134
8335
  if (!stat.isDirectory()) continue;
8135
- targets.add(path15.join(resolved, "AGENTS.md"));
8336
+ targets.add(path16.join(resolved, "AGENTS.md"));
8136
8337
  }
8137
8338
  return [...targets];
8138
8339
  }
@@ -8172,7 +8373,7 @@ function normalizeDecisionEnumList2(raw) {
8172
8373
  return [...deduped];
8173
8374
  }
8174
8375
  async function backfillMissingConfigDefaults(docsDir) {
8175
- const configPath = path15.join(docsDir, ".lee-spec-kit.json");
8376
+ const configPath = path16.join(docsDir, ".lee-spec-kit.json");
8176
8377
  if (!await fs.pathExists(configPath)) {
8177
8378
  return { changed: false, changedPaths: [] };
8178
8379
  }
@@ -8191,6 +8392,8 @@ async function backfillMissingConfigDefaults(docsDir) {
8191
8392
  changedPaths.push("workflow");
8192
8393
  }
8193
8394
  const workflow = raw.workflow;
8395
+ const inferredPreset = workflow.mode === "local" ? "local" : "github";
8396
+ setIfMissing(workflow, "preset", inferredPreset, "workflow.preset");
8194
8397
  setIfMissing(workflow, "mode", "github", "workflow.mode");
8195
8398
  setIfMissing(workflow, "requireWorktree", false, "workflow.requireWorktree");
8196
8399
  setIfMissing(workflow, "codeDirtyScope", "auto", "workflow.codeDirtyScope");
@@ -8299,8 +8502,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
8299
8502
  const files = await fs.readdir(sourceDir);
8300
8503
  let updatedCount = 0;
8301
8504
  for (const file of files) {
8302
- const sourcePath = path15.join(sourceDir, file);
8303
- const targetPath = path15.join(targetDir, file);
8505
+ const sourcePath = path16.join(sourceDir, file);
8506
+ const targetPath = path16.join(targetDir, file);
8304
8507
  const stat = await fs.stat(sourcePath);
8305
8508
  if (stat.isFile()) {
8306
8509
  if (protectedFiles.has(file)) {
@@ -8383,7 +8586,7 @@ function extractPorcelainPaths(line) {
8383
8586
  function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
8384
8587
  const top = getGitTopLevel2(docsDir);
8385
8588
  if (!top) return null;
8386
- const rel = path15.relative(top, docsDir) || ".";
8589
+ const rel = path16.relative(top, docsDir) || ".";
8387
8590
  try {
8388
8591
  const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
8389
8592
  cwd: top,
@@ -8395,7 +8598,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
8395
8598
  }
8396
8599
  const ignoredRelPaths = new Set(
8397
8600
  ignoredAbsPaths.map(
8398
- (absPath) => normalizeGitPath2(path15.relative(top, absPath) || ".")
8601
+ (absPath) => normalizeGitPath2(path16.relative(top, absPath) || ".")
8399
8602
  )
8400
8603
  );
8401
8604
  const filtered = output.split("\n").filter((line) => {
@@ -8453,7 +8656,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
8453
8656
  }
8454
8657
  async function runConfig(options) {
8455
8658
  const cwd = process.cwd();
8456
- const targetCwd = options.dir ? path15.resolve(cwd, options.dir) : cwd;
8659
+ const targetCwd = options.dir ? path16.resolve(cwd, options.dir) : cwd;
8457
8660
  const config = await getConfig(targetCwd);
8458
8661
  if (!config) {
8459
8662
  throw createCliError(
@@ -8461,7 +8664,7 @@ async function runConfig(options) {
8461
8664
  tr(DEFAULT_LANG, "cli", "common.configNotFound")
8462
8665
  );
8463
8666
  }
8464
- const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
8667
+ const configPath = path16.join(config.docsDir, ".lee-spec-kit.json");
8465
8668
  if (!options.projectRoot) {
8466
8669
  console.log();
8467
8670
  console.log(chalk9.blue(tr(config.lang, "cli", "config.currentTitle")));
@@ -8567,47 +8770,47 @@ var BUILTIN_DOC_DEFINITIONS = [
8567
8770
  {
8568
8771
  id: "agents",
8569
8772
  title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
8570
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "agents.md")
8773
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "agents.md")
8571
8774
  },
8572
8775
  {
8573
8776
  id: "git-workflow",
8574
8777
  title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
8575
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "git-workflow.md")
8778
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "git-workflow.md")
8576
8779
  },
8577
8780
  {
8578
8781
  id: "issue-doc",
8579
8782
  title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
8580
- relativePath: (_, lang) => path15.join(lang, "common", "features", "feature-base", "issue.md")
8783
+ relativePath: (_, lang) => path16.join(lang, "common", "features", "feature-base", "issue.md")
8581
8784
  },
8582
8785
  {
8583
8786
  id: "pr-doc",
8584
8787
  title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
8585
- relativePath: (_, lang) => path15.join(lang, "common", "features", "feature-base", "pr.md")
8788
+ relativePath: (_, lang) => path16.join(lang, "common", "features", "feature-base", "pr.md")
8586
8789
  },
8587
8790
  {
8588
8791
  id: "create-feature",
8589
8792
  title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
8590
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-feature.md")
8793
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "skills", "create-feature.md")
8591
8794
  },
8592
8795
  {
8593
8796
  id: "execute-task",
8594
8797
  title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
8595
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "execute-task.md")
8798
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "skills", "execute-task.md")
8596
8799
  },
8597
8800
  {
8598
8801
  id: "create-issue",
8599
8802
  title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
8600
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-issue.md")
8803
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "skills", "create-issue.md")
8601
8804
  },
8602
8805
  {
8603
8806
  id: "create-pr",
8604
8807
  title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
8605
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "create-pr.md")
8808
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "skills", "create-pr.md")
8606
8809
  },
8607
8810
  {
8608
8811
  id: "split-feature",
8609
8812
  title: { ko: "feature \uBD84\uD560 \uAC00\uC774\uB4DC", en: "feature split guide" },
8610
- relativePath: (_, lang) => path15.join(lang, "common", "agents", "skills", "split-feature.md")
8813
+ relativePath: (_, lang) => path16.join(lang, "common", "agents", "skills", "split-feature.md")
8611
8814
  }
8612
8815
  ];
8613
8816
  var DOC_FOLLOWUPS = {
@@ -8705,7 +8908,7 @@ function listBuiltinDocs(projectType, lang) {
8705
8908
  id: doc.id,
8706
8909
  title: doc.title[lang],
8707
8910
  relativePath,
8708
- absolutePath: path15.join(templatesDir, relativePath)
8911
+ absolutePath: path16.join(templatesDir, relativePath)
8709
8912
  };
8710
8913
  });
8711
8914
  }
@@ -8759,7 +8962,7 @@ function toAllowedSet(values, extras) {
8759
8962
  );
8760
8963
  }
8761
8964
  function isDocLikeFile(name) {
8762
- return DOC_LIKE_FILE_EXTENSIONS.has(path15.extname(name).toLowerCase());
8965
+ return DOC_LIKE_FILE_EXTENSIONS.has(path16.extname(name).toLowerCase());
8763
8966
  }
8764
8967
  async function collectUnmanagedDocsEntries(docsDir, allowed) {
8765
8968
  const allowedDirs = toAllowedSet(DEFAULT_MANAGED_DOC_DIRS, allowed?.dirs);
@@ -8775,7 +8978,7 @@ async function collectUnmanagedDocsEntries(docsDir, allowed) {
8775
8978
  unmanaged.push({
8776
8979
  name,
8777
8980
  kind: "dir",
8778
- absPath: path15.join(docsDir, name),
8981
+ absPath: path16.join(docsDir, name),
8779
8982
  relPath: `docs/${name}`
8780
8983
  });
8781
8984
  continue;
@@ -8786,7 +8989,7 @@ async function collectUnmanagedDocsEntries(docsDir, allowed) {
8786
8989
  unmanaged.push({
8787
8990
  name,
8788
8991
  kind: "file",
8789
- absPath: path15.join(docsDir, name),
8992
+ absPath: path16.join(docsDir, name),
8790
8993
  relPath: `docs/${name}`
8791
8994
  });
8792
8995
  }
@@ -9843,7 +10046,7 @@ async function loadApprovalTicketStore(storePath) {
9843
10046
  }
9844
10047
  }
9845
10048
  async function saveApprovalTicketStore(storePath, payload) {
9846
- await fs.ensureDir(path15.dirname(storePath));
10049
+ await fs.ensureDir(path16.dirname(storePath));
9847
10050
  await fs.writeJson(storePath, payload, { spaces: 2 });
9848
10051
  }
9849
10052
  function pruneApprovalTickets(tickets, nowMs) {
@@ -10387,9 +10590,11 @@ async function runContext(featureName, options) {
10387
10590
  const cwd = process.cwd();
10388
10591
  const config = await getConfig(cwd);
10389
10592
  const lang = config?.lang ?? "en";
10390
- const workflowPolicy = resolveWorkflowPolicy(config?.workflow);
10391
- const prePrReviewPolicy = resolvePrePrReviewPolicy(config?.workflow);
10392
- const taskCommitGatePolicy = resolveTaskCommitGatePolicy(config?.workflow);
10593
+ const {
10594
+ workflowPolicy,
10595
+ prePrReviewPolicy,
10596
+ taskCommitGatePolicy
10597
+ } = resolveWorkflowRuntime(config?.workflow);
10393
10598
  if (!config) {
10394
10599
  throw createCliError(
10395
10600
  "CONFIG_NOT_FOUND",
@@ -10907,7 +11112,7 @@ async function runContext(featureName, options) {
10907
11112
  if (f.issueNumber) {
10908
11113
  console.log(` \u2022 Issue: #${f.issueNumber}`);
10909
11114
  }
10910
- console.log(` \u2022 Path: ${path15.relative(cwd, f.path)}`);
11115
+ console.log(` \u2022 Path: ${path16.relative(cwd, f.path)}`);
10911
11116
  if (f.git.projectBranch) {
10912
11117
  console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
10913
11118
  }
@@ -11059,7 +11264,7 @@ function extractTitleAfterId(line, id) {
11059
11264
  return cleaned ? cleaned : void 0;
11060
11265
  }
11061
11266
  async function scanPrdRequirements(fsAdapter, docsDir) {
11062
- const prdDir = path15.join(docsDir, "prd");
11267
+ const prdDir = path16.join(docsDir, "prd");
11063
11268
  const files = await walkFiles(fsAdapter, prdDir, {
11064
11269
  extensions: [".md"],
11065
11270
  ignoreDirs: [".git", "node_modules", "dist", "tmp"]
@@ -11067,7 +11272,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
11067
11272
  const definitions = /* @__PURE__ */ new Map();
11068
11273
  const duplicates = [];
11069
11274
  for (const filePath of files) {
11070
- if (path15.basename(filePath).toLowerCase() === "readme.md") {
11275
+ if (path16.basename(filePath).toLowerCase() === "readme.md") {
11071
11276
  continue;
11072
11277
  }
11073
11278
  let content = "";
@@ -11076,7 +11281,7 @@ async function scanPrdRequirements(fsAdapter, docsDir) {
11076
11281
  } catch {
11077
11282
  continue;
11078
11283
  }
11079
- const relFile = normalizeRelPath2(path15.relative(docsDir, filePath));
11284
+ const relFile = normalizeRelPath2(path16.relative(docsDir, filePath));
11080
11285
  const lines = content.split(/\r?\n/);
11081
11286
  let inCodeBlock = false;
11082
11287
  for (let i = 0; i < lines.length; i += 1) {
@@ -11151,7 +11356,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
11151
11356
  ]);
11152
11357
  function formatPath(cwd, p) {
11153
11358
  if (!p) return "";
11154
- return path15.isAbsolute(p) ? path15.relative(cwd, p) : p;
11359
+ return path16.isAbsolute(p) ? path16.relative(cwd, p) : p;
11155
11360
  }
11156
11361
  function detectPlaceholders(content) {
11157
11362
  const patterns = [
@@ -11310,7 +11515,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
11310
11515
  const placeholderContext = {
11311
11516
  projectName: config.projectName,
11312
11517
  featureName: f.slug,
11313
- featurePath: f.docs.featurePathFromDocs || path15.relative(config.docsDir, f.path),
11518
+ featurePath: f.docs.featurePathFromDocs || path16.relative(config.docsDir, f.path),
11314
11519
  repoType: f.type,
11315
11520
  featureNumber
11316
11521
  };
@@ -11320,7 +11525,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
11320
11525
  "tasks.md"
11321
11526
  ];
11322
11527
  for (const file of files) {
11323
- const fullPath = path15.join(f.path, file);
11528
+ const fullPath = path16.join(f.path, file);
11324
11529
  if (!await fs.pathExists(fullPath)) continue;
11325
11530
  const original = await fs.readFile(fullPath, "utf-8");
11326
11531
  let next = original;
@@ -11373,7 +11578,7 @@ async function checkDocsStructure(config, cwd) {
11373
11578
  const issues = [];
11374
11579
  const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
11375
11580
  for (const dir of requiredDirs) {
11376
- const p = path15.join(config.docsDir, dir);
11581
+ const p = path16.join(config.docsDir, dir);
11377
11582
  if (!await fs.pathExists(p)) {
11378
11583
  issues.push({
11379
11584
  level: "error",
@@ -11385,7 +11590,7 @@ async function checkDocsStructure(config, cwd) {
11385
11590
  });
11386
11591
  }
11387
11592
  }
11388
- const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
11593
+ const configPath = path16.join(config.docsDir, ".lee-spec-kit.json");
11389
11594
  if (!await fs.pathExists(configPath)) {
11390
11595
  issues.push({
11391
11596
  level: "warn",
@@ -11427,7 +11632,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11427
11632
  }
11428
11633
  const idMap = /* @__PURE__ */ new Map();
11429
11634
  for (const f of features) {
11430
- const rel = f.docs.featurePathFromDocs || path15.relative(config.docsDir, f.path);
11635
+ const rel = f.docs.featurePathFromDocs || path16.relative(config.docsDir, f.path);
11431
11636
  const id = f.id || "UNKNOWN";
11432
11637
  if (!idMap.has(id)) idMap.set(id, []);
11433
11638
  idMap.get(id).push(rel);
@@ -11435,7 +11640,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11435
11640
  if (!isInitialTemplateState) {
11436
11641
  const featureDocs = ["spec.md", "plan.md", "tasks.md"];
11437
11642
  for (const file of featureDocs) {
11438
- const p = path15.join(f.path, file);
11643
+ const p = path16.join(f.path, file);
11439
11644
  if (!await fs.pathExists(p)) continue;
11440
11645
  const content = await fs.readFile(p, "utf-8");
11441
11646
  const placeholders = detectPlaceholders(content);
@@ -11450,7 +11655,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11450
11655
  });
11451
11656
  }
11452
11657
  if (decisionsPlaceholderMode !== "off") {
11453
- const decisionsPath = path15.join(f.path, "decisions.md");
11658
+ const decisionsPath = path16.join(f.path, "decisions.md");
11454
11659
  if (await fs.pathExists(decisionsPath)) {
11455
11660
  const content = await fs.readFile(decisionsPath, "utf-8");
11456
11661
  const placeholders = detectPlaceholders(content);
@@ -11479,7 +11684,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11479
11684
  level: "warn",
11480
11685
  code: "spec_status_unset",
11481
11686
  message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
11482
- path: formatPath(cwd, path15.join(f.path, "spec.md"))
11687
+ path: formatPath(cwd, path16.join(f.path, "spec.md"))
11483
11688
  });
11484
11689
  }
11485
11690
  if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
@@ -11487,7 +11692,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11487
11692
  level: "warn",
11488
11693
  code: "plan_status_unset",
11489
11694
  message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
11490
- path: formatPath(cwd, path15.join(f.path, "plan.md"))
11695
+ path: formatPath(cwd, path16.join(f.path, "plan.md"))
11491
11696
  });
11492
11697
  }
11493
11698
  if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
@@ -11495,11 +11700,11 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11495
11700
  level: "warn",
11496
11701
  code: "tasks_empty",
11497
11702
  message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
11498
- path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11703
+ path: formatPath(cwd, path16.join(f.path, "tasks.md"))
11499
11704
  });
11500
11705
  }
11501
11706
  if (f.docs.tasksExists) {
11502
- const tasksPath = path15.join(f.path, "tasks.md");
11707
+ const tasksPath = path16.join(f.path, "tasks.md");
11503
11708
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
11504
11709
  const unknownPrdTags = [...new Set(
11505
11710
  parseTaskLines(tasksContent).flatMap((task) => task.tags).filter((tag) => isPrdRequirementId(tag)).map((tag) => tag.trim().toUpperCase()).filter((tag) => !prdDefinitions.has(tag))
@@ -11521,7 +11726,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11521
11726
  level: "warn",
11522
11727
  code: "tasks_doc_status_missing",
11523
11728
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
11524
- path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11729
+ path: formatPath(cwd, path16.join(f.path, "tasks.md"))
11525
11730
  });
11526
11731
  }
11527
11732
  if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
@@ -11529,7 +11734,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11529
11734
  level: "warn",
11530
11735
  code: "tasks_doc_status_unset",
11531
11736
  message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
11532
- path: formatPath(cwd, path15.join(f.path, "tasks.md"))
11737
+ path: formatPath(cwd, path16.join(f.path, "tasks.md"))
11533
11738
  });
11534
11739
  }
11535
11740
  }
@@ -11553,7 +11758,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
11553
11758
  level: "warn",
11554
11759
  code: "missing_feature_id",
11555
11760
  message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
11556
- path: formatPath(cwd, path15.join(config.docsDir, p))
11761
+ path: formatPath(cwd, path16.join(config.docsDir, p))
11557
11762
  });
11558
11763
  }
11559
11764
  return issues;
@@ -11700,7 +11905,7 @@ function doctorCommand(program2) {
11700
11905
  }
11701
11906
  console.log();
11702
11907
  console.log(chalk9.bold(tr(lang, "cli", "doctor.title")));
11703
- console.log(chalk9.gray(`- Docs: ${path15.relative(cwd, docsDir)}`));
11908
+ console.log(chalk9.gray(`- Docs: ${path16.relative(cwd, docsDir)}`));
11704
11909
  console.log(chalk9.gray(`- Type: ${projectType}`));
11705
11910
  console.log(chalk9.gray(`- Lang: ${lang}`));
11706
11911
  console.log();
@@ -11881,7 +12086,7 @@ async function runView(featureName, options) {
11881
12086
  }
11882
12087
  console.log();
11883
12088
  console.log(chalk9.bold("\u{1F4CA} Workflow View"));
11884
- console.log(chalk9.gray(`- Docs: ${path15.relative(cwd, config.docsDir)}`));
12089
+ console.log(chalk9.gray(`- Docs: ${path16.relative(cwd, config.docsDir)}`));
11885
12090
  console.log(
11886
12091
  chalk9.gray(
11887
12092
  `- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
@@ -11966,13 +12171,13 @@ function normalizeRunId(raw) {
11966
12171
  return value;
11967
12172
  }
11968
12173
  function getFlowRunBaseDir(cwd) {
11969
- return path15.join(getRuntimeStateDir(cwd), "flow-runs");
12174
+ return path16.join(getRuntimeStateDir(cwd), "flow-runs");
11970
12175
  }
11971
12176
  function getFlowRunPath(cwd, runId) {
11972
- return path15.join(getFlowRunBaseDir(cwd), `${runId}.json`);
12177
+ return path16.join(getFlowRunBaseDir(cwd), `${runId}.json`);
11973
12178
  }
11974
12179
  function getFlowRunLockPath(cwd, runId) {
11975
- return path15.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
12180
+ return path16.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
11976
12181
  }
11977
12182
  async function readFlowRunRecordUnsafe(cwd, runId) {
11978
12183
  const normalized = normalizeRunId(runId);
@@ -11998,7 +12203,7 @@ async function readFlowRunRecordUnsafe(cwd, runId) {
11998
12203
  }
11999
12204
  async function writeFlowRunRecord(cwd, record) {
12000
12205
  const filePath = getFlowRunPath(cwd, record.runId);
12001
- await fs.ensureDir(path15.dirname(filePath));
12206
+ await fs.ensureDir(path16.dirname(filePath));
12002
12207
  await fs.writeJson(filePath, record, { spaces: 2 });
12003
12208
  }
12004
12209
  async function createFlowRunRecord(cwd, input) {
@@ -13414,27 +13619,27 @@ function tg(lang, key, vars = {}) {
13414
13619
  function detectGithubCliLangSync(cwd) {
13415
13620
  const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
13416
13621
  const startDirs = [
13417
- explicitDocsDir ? path15.resolve(explicitDocsDir) : "",
13418
- path15.resolve(cwd)
13622
+ explicitDocsDir ? path16.resolve(explicitDocsDir) : "",
13623
+ path16.resolve(cwd)
13419
13624
  ].filter(Boolean);
13420
13625
  const scanOrder = [];
13421
13626
  const seen = /* @__PURE__ */ new Set();
13422
13627
  for (const start of startDirs) {
13423
13628
  let current = start;
13424
13629
  while (true) {
13425
- const abs = path15.resolve(current);
13630
+ const abs = path16.resolve(current);
13426
13631
  if (!seen.has(abs)) {
13427
13632
  scanOrder.push(abs);
13428
13633
  seen.add(abs);
13429
13634
  }
13430
- const parent = path15.dirname(abs);
13635
+ const parent = path16.dirname(abs);
13431
13636
  if (parent === abs) break;
13432
13637
  current = parent;
13433
13638
  }
13434
13639
  }
13435
13640
  for (const base of scanOrder) {
13436
- for (const docsDir of [path15.join(base, "docs"), base]) {
13437
- const configPath = path15.join(docsDir, ".lee-spec-kit.json");
13641
+ for (const docsDir of [path16.join(base, "docs"), base]) {
13642
+ const configPath = path16.join(docsDir, ".lee-spec-kit.json");
13438
13643
  if (fs.existsSync(configPath)) {
13439
13644
  try {
13440
13645
  const parsed = fs.readJsonSync(configPath);
@@ -13443,11 +13648,11 @@ function detectGithubCliLangSync(cwd) {
13443
13648
  } catch {
13444
13649
  }
13445
13650
  }
13446
- const agentsPath = path15.join(docsDir, "agents");
13447
- const featuresPath = path15.join(docsDir, "features");
13651
+ const agentsPath = path16.join(docsDir, "agents");
13652
+ const featuresPath = path16.join(docsDir, "features");
13448
13653
  if (!fs.existsSync(agentsPath) || !fs.existsSync(featuresPath)) continue;
13449
13654
  for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
13450
- const file = path15.join(agentsPath, probe);
13655
+ const file = path16.join(agentsPath, probe);
13451
13656
  if (!fs.existsSync(file)) continue;
13452
13657
  try {
13453
13658
  const content = fs.readFileSync(file, "utf-8");
@@ -13552,7 +13757,7 @@ async function prepareGithubBody(params) {
13552
13757
  };
13553
13758
  }
13554
13759
  }
13555
- await fs.ensureDir(path15.dirname(defaultBodyFile));
13760
+ await fs.ensureDir(path16.dirname(defaultBodyFile));
13556
13761
  await fs.writeFile(defaultBodyFile, generatedBody, "utf-8");
13557
13762
  return {
13558
13763
  body: generatedBody,
@@ -13622,7 +13827,7 @@ function ensureSections(body, sections, kind, lang) {
13622
13827
  }
13623
13828
  function ensureDocsExist(docsDir, relativePaths, lang) {
13624
13829
  const missing = relativePaths.filter(
13625
- (relativePath) => !fs.existsSync(path15.join(docsDir, relativePath))
13830
+ (relativePath) => !fs.existsSync(path16.join(docsDir, relativePath))
13626
13831
  );
13627
13832
  if (missing.length > 0) {
13628
13833
  throw createCliError(
@@ -13632,18 +13837,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
13632
13837
  }
13633
13838
  }
13634
13839
  function buildDefaultBodyFileName(kind, docsDir, component) {
13635
- const key = `${path15.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13840
+ const key = `${path16.resolve(docsDir)}::${component.trim().toLowerCase()}`;
13636
13841
  const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
13637
13842
  return `lee-spec-kit.${digest}.${kind}.md`;
13638
13843
  }
13639
13844
  function toBodyFilePath(raw, kind, docsDir, component, lang) {
13640
- const selected = raw?.trim() || path15.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13845
+ const selected = raw?.trim() || path16.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
13641
13846
  assertValid(
13642
13847
  validatePathWithLang(selected, lang),
13643
13848
  `github.${kind}.bodyFile`,
13644
13849
  lang
13645
13850
  );
13646
- return path15.resolve(selected);
13851
+ return path16.resolve(selected);
13647
13852
  }
13648
13853
  function toProjectRootDocsPath(relativePathFromDocs) {
13649
13854
  const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -14777,7 +14982,7 @@ function ensureCleanWorktree(cwd, lang) {
14777
14982
  function commitAndPushPaths(cwd, absPaths, message, lang, options) {
14778
14983
  const uniqueRelativePaths = [
14779
14984
  ...new Set(
14780
- absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path15.relative(cwd, absPath) || absPath)
14985
+ absPaths.filter((absPath) => !!absPath && fs.existsSync(absPath)).map((absPath) => path16.relative(cwd, absPath) || absPath)
14781
14986
  )
14782
14987
  ];
14783
14988
  if (uniqueRelativePaths.length === 0) return;
@@ -14973,15 +15178,15 @@ function githubCommand(program2) {
14973
15178
  config.lang
14974
15179
  );
14975
15180
  const specContent = await fs.readFile(
14976
- path15.join(config.docsDir, paths.specPath),
15181
+ path16.join(config.docsDir, paths.specPath),
14977
15182
  "utf-8"
14978
15183
  );
14979
15184
  const planContent = await fs.readFile(
14980
- path15.join(config.docsDir, paths.planPath),
15185
+ path16.join(config.docsDir, paths.planPath),
14981
15186
  "utf-8"
14982
15187
  );
14983
15188
  const tasksContent = await fs.readFile(
14984
- path15.join(config.docsDir, paths.tasksPath),
15189
+ path16.join(config.docsDir, paths.tasksPath),
14985
15190
  "utf-8"
14986
15191
  );
14987
15192
  const overview = resolveOverviewFromSpec(
@@ -15024,7 +15229,7 @@ function githubCommand(program2) {
15024
15229
  create: options.create,
15025
15230
  explicitBodyFile,
15026
15231
  defaultBodyFile,
15027
- workflowDraftPath: path15.join(config.docsDir, paths.issuePath),
15232
+ workflowDraftPath: path16.join(config.docsDir, paths.issuePath),
15028
15233
  generatedBody,
15029
15234
  requiredSections: getRequiredIssueSections(config.lang),
15030
15235
  kindLabel: tg(config.lang, "kindIssue"),
@@ -15040,7 +15245,7 @@ function githubCommand(program2) {
15040
15245
  `${feature.type}-issue-sanitized`,
15041
15246
  config.lang
15042
15247
  );
15043
- await fs.ensureDir(path15.dirname(sanitizedBodyFile));
15248
+ await fs.ensureDir(path16.dirname(sanitizedBodyFile));
15044
15249
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
15045
15250
  bodyFile = sanitizedBodyFile;
15046
15251
  }
@@ -15089,12 +15294,12 @@ function githubCommand(program2) {
15089
15294
  const syncedIssueNumber = extractIssueNumberFromUrl(issueUrl);
15090
15295
  if (syncedIssueNumber) {
15091
15296
  const synced = syncTasksIssueMetadata(
15092
- path15.join(config.docsDir, paths.tasksPath),
15297
+ path16.join(config.docsDir, paths.tasksPath),
15093
15298
  syncedIssueNumber,
15094
15299
  config.lang
15095
15300
  );
15096
15301
  const draftSynced = syncIssueDraftMetadata(
15097
- path15.join(config.docsDir, paths.issuePath),
15302
+ path16.join(config.docsDir, paths.issuePath),
15098
15303
  syncedIssueNumber
15099
15304
  );
15100
15305
  syncChanged = synced.changed || draftSynced.changed;
@@ -15205,13 +15410,13 @@ function githubCommand(program2) {
15205
15410
  config.lang
15206
15411
  );
15207
15412
  const specContent = await fs.readFile(
15208
- path15.join(config.docsDir, paths.specPath),
15413
+ path16.join(config.docsDir, paths.specPath),
15209
15414
  "utf-8"
15210
15415
  );
15211
- const planPath = path15.join(config.docsDir, paths.planPath);
15416
+ const planPath = path16.join(config.docsDir, paths.planPath);
15212
15417
  const planContent = await fs.pathExists(planPath) ? await fs.readFile(planPath, "utf-8") : "";
15213
15418
  const tasksContent = await fs.readFile(
15214
- path15.join(config.docsDir, paths.tasksPath),
15419
+ path16.join(config.docsDir, paths.tasksPath),
15215
15420
  "utf-8"
15216
15421
  );
15217
15422
  const overview = resolveOverviewFromSpec(
@@ -15259,7 +15464,7 @@ function githubCommand(program2) {
15259
15464
  create: options.create,
15260
15465
  explicitBodyFile,
15261
15466
  defaultBodyFile,
15262
- workflowDraftPath: path15.join(config.docsDir, paths.prPath),
15467
+ workflowDraftPath: path16.join(config.docsDir, paths.prPath),
15263
15468
  generatedBody,
15264
15469
  requiredSections: getRequiredPrSections(config.lang),
15265
15470
  kindLabel: tg(config.lang, "kindPr"),
@@ -15277,7 +15482,7 @@ function githubCommand(program2) {
15277
15482
  `${feature.type}-pr-sanitized`,
15278
15483
  config.lang
15279
15484
  );
15280
- await fs.ensureDir(path15.dirname(sanitizedBodyFile));
15485
+ await fs.ensureDir(path16.dirname(sanitizedBodyFile));
15281
15486
  await fs.writeFile(sanitizedBodyFile, body, "utf-8");
15282
15487
  bodyFile = sanitizedBodyFile;
15283
15488
  }
@@ -15319,7 +15524,7 @@ function githubCommand(program2) {
15319
15524
  if (preparedBody.source === "generated") {
15320
15525
  await fs.writeFile(bodyFile, body, "utf-8");
15321
15526
  } else {
15322
- await fs.ensureDir(path15.dirname(fallbackBodyFile));
15527
+ await fs.ensureDir(path16.dirname(fallbackBodyFile));
15323
15528
  await fs.writeFile(fallbackBodyFile, body, "utf-8");
15324
15529
  bodyFile = fallbackBodyFile;
15325
15530
  }
@@ -15376,13 +15581,13 @@ function githubCommand(program2) {
15376
15581
  }
15377
15582
  if (prUrl && options.syncTasks !== false) {
15378
15583
  const syncedTasks = syncTasksPrMetadata(
15379
- path15.join(config.docsDir, paths.tasksPath),
15584
+ path16.join(config.docsDir, paths.tasksPath),
15380
15585
  prUrl,
15381
15586
  "Review",
15382
15587
  config.lang
15383
15588
  );
15384
15589
  const syncedDraft = syncPrDraftMetadata(
15385
- path15.join(config.docsDir, paths.prPath),
15590
+ path16.join(config.docsDir, paths.prPath),
15386
15591
  prUrl,
15387
15592
  "Review"
15388
15593
  );
@@ -15423,13 +15628,13 @@ function githubCommand(program2) {
15423
15628
  mergeAlreadyMerged = merged.alreadyMerged;
15424
15629
  if (prUrl && options.syncTasks !== false) {
15425
15630
  const mergedTasksSync = syncTasksPrMetadata(
15426
- path15.join(config.docsDir, paths.tasksPath),
15631
+ path16.join(config.docsDir, paths.tasksPath),
15427
15632
  prUrl,
15428
15633
  "Approved",
15429
15634
  config.lang
15430
15635
  );
15431
15636
  const mergedDraftSync = syncPrDraftMetadata(
15432
- path15.join(config.docsDir, paths.prPath),
15637
+ path16.join(config.docsDir, paths.prPath),
15433
15638
  prUrl,
15434
15639
  "Approved"
15435
15640
  );
@@ -15700,7 +15905,7 @@ function docsCommand(program2) {
15700
15905
  );
15701
15906
  return;
15702
15907
  }
15703
- const relativeFromCwd = path15.relative(process.cwd(), loaded.entry.absolutePath);
15908
+ const relativeFromCwd = path16.relative(process.cwd(), loaded.entry.absolutePath);
15704
15909
  console.log();
15705
15910
  console.log(chalk9.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
15706
15911
  console.log(
@@ -15780,8 +15985,9 @@ function detectCommand(program2) {
15780
15985
  }
15781
15986
  async function runDetect(options) {
15782
15987
  const cwd = process.cwd();
15783
- const targetCwd = options.dir ? path15.resolve(cwd, options.dir) : cwd;
15784
- const config = await getConfig(targetCwd);
15988
+ const targetCwd = options.dir ? path16.resolve(cwd, options.dir) : cwd;
15989
+ const detection = await detectSchemaProject(targetCwd);
15990
+ const config = detection.config;
15785
15991
  const detected = !!config;
15786
15992
  const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
15787
15993
  if (options.json) {
@@ -15807,9 +16013,6 @@ async function runDetect(options) {
15807
16013
  );
15808
16014
  return;
15809
16015
  }
15810
- const configPath2 = path15.join(config.docsDir, ".lee-spec-kit.json");
15811
- const configFilePresent2 = await fs.pathExists(configPath2);
15812
- const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
15813
16016
  console.log(
15814
16017
  JSON.stringify(
15815
16018
  {
@@ -15817,10 +16020,10 @@ async function runDetect(options) {
15817
16020
  reasonCode,
15818
16021
  isLeeSpecKitProject: true,
15819
16022
  targetCwd,
15820
- docsDir: config.docsDir,
15821
- configPath: configFilePresent2 ? configPath2 : null,
15822
- configFilePresent: configFilePresent2,
15823
- detectionSource: detectionSource2,
16023
+ docsDir: detection.docsDir,
16024
+ configPath: detection.configPath,
16025
+ configFilePresent: detection.configFilePresent,
16026
+ detectionSource: detection.detectionSource,
15824
16027
  projectType: config.projectType,
15825
16028
  lang: config.lang,
15826
16029
  projectName: config.projectName ?? null
@@ -15841,14 +16044,11 @@ async function runDetect(options) {
15841
16044
  console.log();
15842
16045
  return;
15843
16046
  }
15844
- const configPath = path15.join(config.docsDir, ".lee-spec-kit.json");
15845
- const configFilePresent = await fs.pathExists(configPath);
15846
- const detectionSource = configFilePresent ? "config" : "heuristic";
15847
16047
  console.log(chalk9.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
15848
- console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
16048
+ console.log(chalk9.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${detection.docsDir}`));
15849
16049
  console.log(
15850
16050
  chalk9.gray(
15851
- `- ${tr(lang, "cli", "detect.labelConfigPath")}: ${configFilePresent ? configPath : "-"}`
16051
+ `- ${tr(lang, "cli", "detect.labelConfigPath")}: ${detection.configPath || "-"}`
15852
16052
  )
15853
16053
  );
15854
16054
  console.log(
@@ -15856,7 +16056,7 @@ async function runDetect(options) {
15856
16056
  `- ${tr(lang, "cli", "detect.labelSource")}: ${tr(
15857
16057
  lang,
15858
16058
  "cli",
15859
- detectionSource === "config" ? "detect.sourceConfig" : "detect.sourceHeuristic"
16059
+ detection.detectionSource === "config" ? "detect.sourceConfig" : "detect.sourceHeuristic"
15860
16060
  )}`
15861
16061
  )
15862
16062
  );
@@ -15918,19 +16118,19 @@ function hasTemplateMarkers(content) {
15918
16118
  return patterns.some((pattern) => pattern.test(content));
15919
16119
  }
15920
16120
  async function countFeatureDirs(ctx, docsDir, projectType) {
15921
- const featuresRoot = path15.join(docsDir, "features");
16121
+ const featuresRoot = path16.join(docsDir, "features");
15922
16122
  if (projectType === "single") {
15923
16123
  const dirs = await listSubdirectories(ctx.fs, featuresRoot);
15924
- return dirs.filter((value) => path15.basename(value) !== "feature-base").length;
16124
+ return dirs.filter((value) => path16.basename(value) !== "feature-base").length;
15925
16125
  }
15926
16126
  const components = await listSubdirectories(ctx.fs, featuresRoot);
15927
16127
  let total = 0;
15928
16128
  for (const componentDir of components) {
15929
- const componentName = path15.basename(componentDir).trim().toLowerCase();
16129
+ const componentName = path16.basename(componentDir).trim().toLowerCase();
15930
16130
  if (!componentName || componentName === "feature-base") continue;
15931
16131
  const dirs = await listSubdirectories(ctx.fs, componentDir);
15932
16132
  total += dirs.filter(
15933
- (value) => path15.basename(value) !== "feature-base"
16133
+ (value) => path16.basename(value) !== "feature-base"
15934
16134
  ).length;
15935
16135
  }
15936
16136
  return total;
@@ -15942,7 +16142,7 @@ async function hasUserPrdFile(ctx, prdDir) {
15942
16142
  ignoreDirs: ["node_modules"]
15943
16143
  });
15944
16144
  return files.some(
15945
- (absolutePath) => path15.basename(absolutePath).toLowerCase() !== "readme.md"
16145
+ (absolutePath) => path16.basename(absolutePath).toLowerCase() !== "readme.md"
15946
16146
  );
15947
16147
  }
15948
16148
  function finalizeChecks(checks) {
@@ -16111,7 +16311,7 @@ async function runOnboardChecks(ctx) {
16111
16311
  });
16112
16312
  }
16113
16313
  }
16114
- const constitutionPath = path15.join(docsDir, "agents", "constitution.md");
16314
+ const constitutionPath = path16.join(docsDir, "agents", "constitution.md");
16115
16315
  if (!await fs.pathExists(constitutionPath)) {
16116
16316
  checks.push({
16117
16317
  id: "constitution_exists",
@@ -16153,7 +16353,7 @@ async function runOnboardChecks(ctx) {
16153
16353
  });
16154
16354
  }
16155
16355
  }
16156
- const customPath = path15.join(docsDir, "agents", "custom.md");
16356
+ const customPath = path16.join(docsDir, "agents", "custom.md");
16157
16357
  if (await fs.pathExists(customPath)) {
16158
16358
  const content = await fs.readFile(customPath, "utf-8");
16159
16359
  if (hasTemplateMarkers(content)) {
@@ -16182,7 +16382,7 @@ async function runOnboardChecks(ctx) {
16182
16382
  });
16183
16383
  }
16184
16384
  }
16185
- const prdDir = path15.join(docsDir, "prd");
16385
+ const prdDir = path16.join(docsDir, "prd");
16186
16386
  const featureCount = await countFeatureDirs(ctx, docsDir, config.projectType);
16187
16387
  const prdReady = await hasUserPrdFile(ctx, prdDir);
16188
16388
  if (!prdReady) {
@@ -16200,7 +16400,7 @@ async function runOnboardChecks(ctx) {
16200
16400
  "PRD is empty. If features already exist, fill PRD as soon as possible."
16201
16401
  ),
16202
16402
  path: prdDir,
16203
- suggestedCommand: `touch ${quotePath(path15.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
16403
+ suggestedCommand: `touch ${quotePath(path16.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
16204
16404
  });
16205
16405
  } else {
16206
16406
  checks.push({
@@ -16638,7 +16838,7 @@ var PrePrReviewValidator = class {
16638
16838
  return result.evidence;
16639
16839
  }
16640
16840
  async validateEvidenceWithScope(evidencePath, projectRoot) {
16641
- const fullPath = path15.resolve(evidencePath);
16841
+ const fullPath = path16.resolve(evidencePath);
16642
16842
  if (!await fs.pathExists(fullPath)) {
16643
16843
  throw createCliError(
16644
16844
  "INVALID_ARGUMENT",
@@ -16736,9 +16936,9 @@ var PrePrReviewValidator = class {
16736
16936
  ]);
16737
16937
  const reviewedFiles = new Set(
16738
16938
  normalizedEvidence.files.map(
16739
- (f) => path15.relative(
16939
+ (f) => path16.relative(
16740
16940
  projectRoot,
16741
- path15.resolve(projectRoot, f.path)
16941
+ path16.resolve(projectRoot, f.path)
16742
16942
  )
16743
16943
  ).map((entry) => normalizeGitPath3(entry)).filter(Boolean)
16744
16944
  );
@@ -17199,7 +17399,7 @@ async function runPrePrReviewRun(featureName, options) {
17199
17399
  );
17200
17400
  const policy = resolvePrePrReviewPolicy(config.workflow);
17201
17401
  const preferred = getPreferredKeys(config.lang);
17202
- const tasksPath = path15.join(feature.path, "tasks.md");
17402
+ const tasksPath = path16.join(feature.path, "tasks.md");
17203
17403
  let tasksUpdated = false;
17204
17404
  if (await fs.pathExists(tasksPath)) {
17205
17405
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -17308,7 +17508,7 @@ async function runPrePrReview(featureName, options) {
17308
17508
  `tasks.md not found for feature: ${feature.folderName}`
17309
17509
  );
17310
17510
  }
17311
- const tasksPath = path15.join(feature.path, "tasks.md");
17511
+ const tasksPath = path16.join(feature.path, "tasks.md");
17312
17512
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
17313
17513
  const policy = resolvePrePrReviewPolicy(config.workflow);
17314
17514
  const preferred = getPreferredKeys(config.lang);
@@ -17406,7 +17606,7 @@ async function runPrePrReview(featureName, options) {
17406
17606
  }
17407
17607
  }
17408
17608
  }
17409
- const decisionsPath = path15.join(feature.path, "decisions.md");
17609
+ const decisionsPath = path16.join(feature.path, "decisions.md");
17410
17610
  const decisionLogEntry = buildReportContent({
17411
17611
  folderName: feature.folderName,
17412
17612
  date,
@@ -17422,9 +17622,9 @@ async function runPrePrReview(featureName, options) {
17422
17622
  await fs.writeFile(decisionsPath, nextDecisions, "utf-8");
17423
17623
  }
17424
17624
  const decisionsPathFromDocs = normalizePathForDoc(
17425
- path15.join(feature.docs.featurePathFromDocs, "decisions.md")
17625
+ path16.join(feature.docs.featurePathFromDocs, "decisions.md")
17426
17626
  );
17427
- const evidencePath = path15.basename(config.docsDir) === "docs" ? normalizePathForDoc(path15.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
17627
+ const evidencePath = path16.basename(config.docsDir) === "docs" ? normalizePathForDoc(path16.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
17428
17628
  let nextTasks = tasksContent;
17429
17629
  nextTasks = upsertSpecLine(
17430
17630
  nextTasks,
@@ -17565,7 +17765,7 @@ async function runCodeReviewRun(featureName, options) {
17565
17765
  );
17566
17766
  }
17567
17767
  const feature = state.matchedFeature;
17568
- const tasksPath = path15.join(feature.path, "tasks.md");
17768
+ const tasksPath = path16.join(feature.path, "tasks.md");
17569
17769
  let tasksUpdated = false;
17570
17770
  if (await fs.pathExists(tasksPath)) {
17571
17771
  const tasksContent = await fs.readFile(tasksPath, "utf-8");
@@ -17598,7 +17798,7 @@ async function runCodeReviewRun(featureName, options) {
17598
17798
  nextMainState: "code_review_running",
17599
17799
  tasksUpdated,
17600
17800
  tasksPath,
17601
- decisionsPath: path15.join(feature.path, "decisions.md"),
17801
+ decisionsPath: path16.join(feature.path, "decisions.md"),
17602
17802
  prompt,
17603
17803
  recordedAt: getLocalDateString()
17604
17804
  };
@@ -17717,7 +17917,7 @@ async function runRequirements(options) {
17717
17917
  }
17718
17918
  for (const feature of scan.features) {
17719
17919
  if (!feature.docs.tasksExists) continue;
17720
- const tasksPath = path15.join(feature.path, "tasks.md");
17920
+ const tasksPath = path16.join(feature.path, "tasks.md");
17721
17921
  let tasksContent = "";
17722
17922
  try {
17723
17923
  tasksContent = await ctx.fs.readFile(tasksPath, "utf-8");
@@ -17851,7 +18051,7 @@ async function runRequirements(options) {
17851
18051
  process.stdout.write(`${lines.join("\n")}
17852
18052
  `);
17853
18053
  if (options.write) {
17854
- const outputPath = path15.join(docsDir, "prd", "status.md");
18054
+ const outputPath = path16.join(docsDir, "prd", "status.md");
17855
18055
  await ctx.fs.writeFile(outputPath, `${lines.join("\n")}
17856
18056
  `, "utf-8");
17857
18057
  console.log(chalk9.green(`\u2705 wrote: ${outputPath}`));
@@ -17875,8 +18075,105 @@ function parseTaskLine(line, index = -1) {
17875
18075
  title: match[4]
17876
18076
  };
17877
18077
  }
18078
+ function countLeadingSpaces(line) {
18079
+ const match = line.match(/^(\s*)/);
18080
+ return match?.[1]?.length ?? 0;
18081
+ }
18082
+ function findTaskBlockEnd(lines, taskLineIndex) {
18083
+ let endIndex = lines.length;
18084
+ for (let index = taskLineIndex + 1; index < lines.length; index++) {
18085
+ if (parseTaskLine(lines[index]) || /^\s*##\s+/.test(lines[index])) {
18086
+ endIndex = index;
18087
+ break;
18088
+ }
18089
+ }
18090
+ return endIndex;
18091
+ }
18092
+ function isPlaceholderTaskItem(text) {
18093
+ const normalized = text.trim();
18094
+ return normalized === "" || normalized === "-" || /^todo$/i.test(normalized);
18095
+ }
18096
+ function parseTaskSectionItems(lines, taskLineIndex, headingPattern) {
18097
+ if (taskLineIndex < 0 || taskLineIndex >= lines.length) return void 0;
18098
+ const endIndex = findTaskBlockEnd(lines, taskLineIndex);
18099
+ let sectionHeaderIndex = -1;
18100
+ let sectionHeaderIndent = 0;
18101
+ for (let index = taskLineIndex + 1; index < endIndex; index++) {
18102
+ if (headingPattern.test(lines[index])) {
18103
+ sectionHeaderIndex = index;
18104
+ sectionHeaderIndent = countLeadingSpaces(lines[index]);
18105
+ break;
18106
+ }
18107
+ }
18108
+ if (sectionHeaderIndex === -1) return void 0;
18109
+ const items = [];
18110
+ let placeholderCount = 0;
18111
+ for (let index = sectionHeaderIndex + 1; index < endIndex; index++) {
18112
+ const line = lines[index];
18113
+ if (!line.trim()) continue;
18114
+ const indent = countLeadingSpaces(line);
18115
+ if (indent <= sectionHeaderIndent && /^\s*-\s+/.test(line)) {
18116
+ break;
18117
+ }
18118
+ const match = line.match(/^\s*-\s+(.+?)\s*$/);
18119
+ if (!match) continue;
18120
+ const text = match[1].trim();
18121
+ items.push(text);
18122
+ if (isPlaceholderTaskItem(text)) placeholderCount++;
18123
+ }
18124
+ if (items.length === 0) return void 0;
18125
+ return { items, placeholderCount };
18126
+ }
18127
+ function parseTaskAcceptance(lines, taskLineIndex) {
18128
+ return parseTaskSectionItems(lines, taskLineIndex, /^\s*-\s+Acceptance:\s*$/);
18129
+ }
18130
+ function parseTaskChecklist(lines, taskLineIndex) {
18131
+ if (taskLineIndex < 0 || taskLineIndex >= lines.length) return void 0;
18132
+ const endIndex = findTaskBlockEnd(lines, taskLineIndex);
18133
+ let checklistHeaderIndex = -1;
18134
+ let checklistHeaderIndent = 0;
18135
+ for (let index = taskLineIndex + 1; index < endIndex; index++) {
18136
+ if (/^\s*-\s+Checklist:\s*$/.test(lines[index])) {
18137
+ checklistHeaderIndex = index;
18138
+ checklistHeaderIndent = countLeadingSpaces(lines[index]);
18139
+ break;
18140
+ }
18141
+ }
18142
+ if (checklistHeaderIndex === -1) return void 0;
18143
+ let total = 0;
18144
+ let checked = 0;
18145
+ let placeholderCount = 0;
18146
+ for (let index = checklistHeaderIndex + 1; index < endIndex; index++) {
18147
+ const line = lines[index];
18148
+ if (!line.trim()) continue;
18149
+ const indent = countLeadingSpaces(line);
18150
+ if (indent <= checklistHeaderIndent && /^\s*-\s+/.test(line)) {
18151
+ break;
18152
+ }
18153
+ const match = line.match(/^\s*-\s*\[([ xX])\]\s+/);
18154
+ if (!match) continue;
18155
+ total++;
18156
+ if (match[1].toLowerCase() === "x") checked++;
18157
+ const text = line.replace(/^\s*-\s*\[[ xX]\]\s+/, "").trim();
18158
+ if (isPlaceholderTaskItem(text)) placeholderCount++;
18159
+ }
18160
+ if (total === 0) return void 0;
18161
+ return { total, checked, unchecked: total - checked, placeholderCount };
18162
+ }
17878
18163
 
17879
18164
  // src/commands/task-run.ts
18165
+ function ensureTaskDetailsReady(lines, task) {
18166
+ const acceptance = parseTaskAcceptance(lines, task.index);
18167
+ const checklist = parseTaskChecklist(lines, task.index);
18168
+ const acceptanceHasPlaceholder = !!acceptance && (acceptance.items.length === 0 || acceptance.placeholderCount > 0);
18169
+ const checklistHasPlaceholder = !!checklist && (checklist.total === 0 || checklist.placeholderCount > 0);
18170
+ if (acceptanceHasPlaceholder || checklistHasPlaceholder) {
18171
+ throw createCliError(
18172
+ "PRECONDITION_FAILED",
18173
+ `Task "${task.taskId}" still contains Acceptance/Checklist placeholder content. Fill concrete Acceptance items and Checklist items before running task-run.`
18174
+ );
18175
+ }
18176
+ }
17880
18177
  function buildTaskRunPrompt(input) {
17881
18178
  const shared = [
17882
18179
  "Read `spec.md`, `plan.md`, and `tasks.md` before editing code.",
@@ -17884,8 +18181,8 @@ function buildTaskRunPrompt(input) {
17884
18181
  "Use additional helper agents only when parallel analysis is clearly worth the extra slot cost.",
17885
18182
  "Keep one writer for overlapping files; do not let multiple sub-agents edit the same files concurrently.",
17886
18183
  "If helper-agent quota is exhausted, continue the task in the main agent instead of blocking progress.",
17887
- "Update the assigned task status and verification notes in `tasks.md` before leaving this task.",
17888
- "Mark the task `DONE` only after code changes and verification are complete."
18184
+ "Update the assigned task status, task-local checklist boxes, and verification notes in `tasks.md` before leaving this task.",
18185
+ "Mark the task `DONE` only after code changes and verification are complete. `task-complete` will reject the transition if checklist items remain unchecked."
17889
18186
  ];
17890
18187
  if (input.lang === "ko") {
17891
18188
  return [
@@ -17941,7 +18238,7 @@ async function resolveTaskRunContext(featureName, options) {
17941
18238
  }
17942
18239
  async function runTaskRun(featureName, options) {
17943
18240
  const { config, feature } = await resolveTaskRunContext(featureName, options);
17944
- const tasksPath = path15.join(feature.path, "tasks.md");
18241
+ const tasksPath = path16.join(feature.path, "tasks.md");
17945
18242
  if (!await fs.pathExists(tasksPath)) {
17946
18243
  throw createCliError(
17947
18244
  "PRECONDITION_FAILED",
@@ -17973,6 +18270,7 @@ async function runTaskRun(featureName, options) {
17973
18270
  `Task "${requestedTaskId}" is already DONE.`
17974
18271
  );
17975
18272
  }
18273
+ ensureTaskDetailsReady(lines, resolvedTask);
17976
18274
  const mode = resolvedTask.status === "TODO" ? "start" : "continue";
17977
18275
  let tasksUpdated = false;
17978
18276
  if (resolvedTask.status === "TODO") {
@@ -18079,7 +18377,7 @@ async function resolveTaskCompleteContext(featureName, options) {
18079
18377
  }
18080
18378
  async function runTaskComplete(featureName, options) {
18081
18379
  const { feature } = await resolveTaskCompleteContext(featureName, options);
18082
- const tasksPath = path15.join(feature.path, "tasks.md");
18380
+ const tasksPath = path16.join(feature.path, "tasks.md");
18083
18381
  if (!await fs.pathExists(tasksPath)) {
18084
18382
  throw createCliError(
18085
18383
  "PRECONDITION_FAILED",
@@ -18117,6 +18415,13 @@ async function runTaskComplete(featureName, options) {
18117
18415
  `Task "${requestedTaskId}" must be DOING/REVIEW before marking it DONE.`
18118
18416
  );
18119
18417
  }
18418
+ const checklist = parseTaskChecklist(lines, resolvedTask.index);
18419
+ if (checklist && checklist.unchecked > 0) {
18420
+ throw createCliError(
18421
+ "PRECONDITION_FAILED",
18422
+ `Task "${requestedTaskId}" still has unchecked checklist items (${checklist.checked}/${checklist.total}).`
18423
+ );
18424
+ }
18120
18425
  lines[resolvedTask.index] = setTaskStatus2(resolvedTask, "DONE");
18121
18426
  await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
18122
18427
  const payload = {
@@ -18175,6 +18480,21 @@ function taskCompleteCommand(program2) {
18175
18480
  }
18176
18481
  );
18177
18482
  }
18483
+ function collectRepeatableOption(value, previous = []) {
18484
+ return [...previous, value];
18485
+ }
18486
+ function normalizeTaskDetailItems(values, flagName) {
18487
+ const normalized = (values || []).map((value) => value.trim()).filter(Boolean);
18488
+ for (const value of normalized) {
18489
+ if (value === "-" || /^todo$/i.test(value)) {
18490
+ throw createCliError(
18491
+ "INVALID_ARGUMENT",
18492
+ `${flagName} must contain concrete text, not placeholder values like "-" or "TODO".`
18493
+ );
18494
+ }
18495
+ }
18496
+ return normalized;
18497
+ }
18178
18498
  function escapeRegExp8(value) {
18179
18499
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18180
18500
  }
@@ -18240,9 +18560,9 @@ function formatTaskBlock(input) {
18240
18560
  `- [TODO][${input.ref}] ${input.taskId} ${input.title}`,
18241
18561
  ` - Date: ${input.recordedAt}`,
18242
18562
  " - Acceptance:",
18243
- " - -",
18563
+ ...input.acceptanceItems.length > 0 ? input.acceptanceItems.map((item) => ` - ${item}`) : [" - -"],
18244
18564
  " - Checklist:",
18245
- " - [ ] -"
18565
+ ...input.checklistItems.length > 0 ? input.checklistItems.map((item) => ` - [ ] ${item}`) : [" - [ ] -"]
18246
18566
  ];
18247
18567
  }
18248
18568
  async function resolveTaskFeature(featureName, component) {
@@ -18273,6 +18593,11 @@ async function runTaskAdd(featureName, options) {
18273
18593
  if (!title) {
18274
18594
  throw createCliError("INVALID_ARGUMENT", "`--title` must not be empty.");
18275
18595
  }
18596
+ const acceptanceItems = normalizeTaskDetailItems(
18597
+ options.acceptance,
18598
+ "--acceptance"
18599
+ );
18600
+ const checklistItems = normalizeTaskDetailItems(options.check, "--check");
18276
18601
  const ref = normalizeTaskRef(options.ref);
18277
18602
  if (isPrdRequirementId(ref)) {
18278
18603
  const { definitions } = await scanPrdRequirements(ctx.fs, ctx.config.docsDir);
@@ -18283,7 +18608,7 @@ async function runTaskAdd(featureName, options) {
18283
18608
  );
18284
18609
  }
18285
18610
  }
18286
- const tasksPath = path15.join(feature.path, "tasks.md");
18611
+ const tasksPath = path16.join(feature.path, "tasks.md");
18287
18612
  if (!await fs.pathExists(tasksPath)) {
18288
18613
  throw createCliError(
18289
18614
  "PRECONDITION_FAILED",
@@ -18312,7 +18637,14 @@ async function runTaskAdd(featureName, options) {
18312
18637
  nextSectionHeadingIndex
18313
18638
  );
18314
18639
  const recordedAt = getLocalDateString();
18315
- const blockLines = formatTaskBlock({ ref, taskId, title, recordedAt });
18640
+ const blockLines = formatTaskBlock({
18641
+ ref,
18642
+ taskId,
18643
+ title,
18644
+ recordedAt,
18645
+ acceptanceItems,
18646
+ checklistItems
18647
+ });
18316
18648
  const shouldPrefixBlank = insertIndex > taskListHeadingIndex + 1 && (lines[insertIndex - 1] || "").trim() !== "";
18317
18649
  const shouldSuffixBlank = insertIndex < lines.length && (lines[insertIndex] || "").trim() !== "";
18318
18650
  const insertLines = [
@@ -18343,7 +18675,17 @@ async function runTaskAdd(featureName, options) {
18343
18675
  }
18344
18676
  function taskCommand(program2) {
18345
18677
  const task = program2.command("task").description("Manage tasks");
18346
- task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or PRD-FR-001").option("--component <component>", "Component name for multi projects").option("--json", "Output JSON").action(async (featureName, options) => {
18678
+ task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or PRD-FR-001").option(
18679
+ "--acceptance <text>",
18680
+ "Acceptance item. Repeat to add more than one.",
18681
+ collectRepeatableOption,
18682
+ []
18683
+ ).option(
18684
+ "--check <text>",
18685
+ "Checklist item. Repeat to add more than one.",
18686
+ collectRepeatableOption,
18687
+ []
18688
+ ).option("--component <component>", "Component name for multi projects").option("--json", "Output JSON").action(async (featureName, options) => {
18347
18689
  try {
18348
18690
  await runTaskAdd(featureName, options);
18349
18691
  } catch (error) {
@@ -18369,10 +18711,9 @@ function taskCommand(program2) {
18369
18711
  }
18370
18712
  });
18371
18713
  }
18372
- function setupCommand(program2) {
18373
- const setup = program2.command("setup").description("Developer environment setup helpers");
18374
- setup.command("codex-bootstrap").description(
18375
- "Install a small Codex global bootstrap that reads ./AGENTS.md or ./docs/AGENTS.md"
18714
+ function registerCodexIntegration(parent) {
18715
+ parent.command("codex").alias("codex-bootstrap").description(
18716
+ "Install or remove the optional Codex bootstrap that re-reads ./AGENTS.md or ./docs/AGENTS.md"
18376
18717
  ).option(
18377
18718
  "--remove",
18378
18719
  "Remove the lee-spec-kit managed Codex bootstrap block"
@@ -18405,6 +18746,10 @@ function setupCommand(program2) {
18405
18746
  }
18406
18747
  });
18407
18748
  }
18749
+ function integrationsCommand(program2) {
18750
+ const integrations = program2.command("integrations").alias("setup").description("Optional developer integration helpers");
18751
+ registerCodexIntegration(integrations);
18752
+ }
18408
18753
  function isBannerDisabled() {
18409
18754
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
18410
18755
  return v === "1";
@@ -18448,11 +18793,11 @@ ${version}
18448
18793
  }
18449
18794
  return `${ascii}${footer}`;
18450
18795
  }
18451
- var CACHE_FILE = path15.join(os.homedir(), ".lee-spec-kit-version-cache.json");
18796
+ var CACHE_FILE = path16.join(os.homedir(), ".lee-spec-kit-version-cache.json");
18452
18797
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
18453
18798
  function getCurrentVersion() {
18454
18799
  try {
18455
- const packageJsonPath = path15.join(__dirname$1, "..", "package.json");
18800
+ const packageJsonPath = path16.join(__dirname$1, "..", "package.json");
18456
18801
  if (fs.existsSync(packageJsonPath)) {
18457
18802
  const pkg = fs.readJsonSync(packageJsonPath);
18458
18803
  return pkg.version;
@@ -18556,7 +18901,7 @@ function shouldCheckForUpdates() {
18556
18901
  if (shouldCheckForUpdates()) checkForUpdates();
18557
18902
  function getCliVersion() {
18558
18903
  try {
18559
- const packageJsonPath = path15.join(__dirname$1, "..", "package.json");
18904
+ const packageJsonPath = path16.join(__dirname$1, "..", "package.json");
18560
18905
  if (fs.existsSync(packageJsonPath)) {
18561
18906
  const pkg = fs.readJsonSync(packageJsonPath);
18562
18907
  if (pkg?.version) return String(pkg.version);
@@ -18576,9 +18921,7 @@ function configureRootCommandSurface() {
18576
18921
  }
18577
18922
  }
18578
18923
  var cliVersion = getCliVersion();
18579
- program.name("lee-spec-kit").description(
18580
- "Agent-guided development harness for spec-driven projects"
18581
- ).version(cliVersion).option("--no-banner", "Hide banner in help output");
18924
+ program.name("lee-spec-kit").description("Orchestration harness CLI for AI agent-driven development").version(cliVersion).option("--no-banner", "Hide banner in help output");
18582
18925
  if (shouldShowBanner()) {
18583
18926
  program.addHelpText("beforeAll", getBanner({ version: cliVersion }));
18584
18927
  }
@@ -18602,7 +18945,7 @@ taskRunCommand(program);
18602
18945
  taskCompleteCommand(program);
18603
18946
  taskCommand(program);
18604
18947
  requirementsCommand(program);
18605
- setupCommand(program);
18948
+ integrationsCommand(program);
18606
18949
  configureRootCommandSurface();
18607
18950
  await program.parseAsync();
18608
18951
  //# sourceMappingURL=index.js.map