lean-spec 0.2.8 → 0.2.9-dev.20251205030455

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.
@@ -0,0 +1,5 @@
1
+ export { backfillCommand, backfillTimestamps } from './chunk-H5MCUMBK.js';
2
+ import './chunk-RF5PKL6L.js';
3
+ import './chunk-VN5BUHTV.js';
4
+ //# sourceMappingURL=backfill-446GBTBC.js.map
5
+ //# sourceMappingURL=backfill-446GBTBC.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"backfill-QIQLXSUG.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"backfill-446GBTBC.js"}
@@ -1,7 +1,7 @@
1
- import { backfillTimestamps, resolveSpecPath, createSpecDirPattern, getGlobalNextSeq } from './chunk-A6JDTXPV.js';
2
- import { TokenCounter, validateSpecs, sanitizeUserInput, getStatusIndicator, SpecDependencyGraph, countTokens, advancedSearchSpecs, withSpinner, getStatusEmoji, getPriorityEmoji, STATUS_CONFIG, PRIORITY_CONFIG, getSearchSyntaxHelp, analyzeMarkdownStructure, extractLines, countLines, parseFrontmatterFromString, createUpdatedFrontmatter, removeLines } from './chunk-FUIUCGUV.js';
3
- import { loadConfig, getSpec, loadAllSpecs, resolvePrefix, extractGroup, loadSubFiles, saveConfig } from './chunk-LXOJW2FE.js';
4
- import { getSpecFile, updateFrontmatter, normalizeDateFields, parseFrontmatter } from './chunk-LVD7ZAVZ.js';
1
+ import { backfillTimestamps, resolveSpecPath, createSpecDirPattern, getGlobalNextSeq } from './chunk-H5MCUMBK.js';
2
+ import { TokenCounter, validateSpecs, sanitizeUserInput, getStatusIndicator, SpecDependencyGraph, countTokens, advancedSearchSpecs, withSpinner, getStatusEmoji, getPriorityEmoji, STATUS_CONFIG, PRIORITY_CONFIG, getSearchSyntaxHelp, analyzeMarkdownStructure, extractLines, countLines, parseFrontmatterFromString, createUpdatedFrontmatter, removeLines } from './chunk-CJMVV46H.js';
3
+ import { loadConfig, getSpec, loadAllSpecs, resolvePrefix, extractGroup, loadSubFiles, saveConfig } from './chunk-RF5PKL6L.js';
4
+ import { getSpecFile, updateFrontmatter, normalizeDateFields, parseFrontmatter } from './chunk-VN5BUHTV.js';
5
5
  import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
7
  import { readFileSync, existsSync } from 'fs';
@@ -995,9 +995,9 @@ async function buildVariableContext(config, options = {}) {
995
995
  return context;
996
996
  }
997
997
  function linkCommand() {
998
- return new Command("link").description("Add relationships between specs (depends_on, related)").argument("<spec>", "Spec to update").option("--depends-on <specs>", "Add dependencies (comma-separated spec numbers or names)").option("--related <specs>", "Add related specs (comma-separated spec numbers or names)").action(async (specPath, options) => {
999
- if (!options.dependsOn && !options.related) {
1000
- console.error("Error: At least one relationship type required (--depends-on or --related)");
998
+ return new Command("link").description("Add dependencies between specs").argument("<spec>", "Spec to update").option("--depends-on <specs>", "Add dependencies (comma-separated spec numbers or names)").action(async (specPath, options) => {
999
+ if (!options.dependsOn) {
1000
+ console.error("Error: --depends-on is required");
1001
1001
  process.exit(1);
1002
1002
  }
1003
1003
  await linkSpec(specPath, options);
@@ -1019,92 +1019,44 @@ async function linkSpec(specPath, options) {
1019
1019
  const allSpecs = await loadAllSpecs({ includeArchived: true });
1020
1020
  const specMap = new Map(allSpecs.map((s) => [s.path, s]));
1021
1021
  const dependsOnSpecs = options.dependsOn ? options.dependsOn.split(",").map((s) => s.trim()) : [];
1022
- const relatedSpecs = options.related ? options.related.split(",").map((s) => s.trim()) : [];
1023
1022
  const targetSpecName = path12.basename(resolvedPath);
1024
- const allRelationshipSpecs = [...dependsOnSpecs, ...relatedSpecs];
1025
- const resolvedRelationships = /* @__PURE__ */ new Map();
1026
- for (const relSpec of allRelationshipSpecs) {
1027
- if (relSpec === targetSpecName || relSpec === specPath) {
1028
- throw new Error(`Cannot link spec to itself: ${sanitizeUserInput(relSpec)}`);
1023
+ const resolvedDependencies = /* @__PURE__ */ new Map();
1024
+ for (const depSpec of dependsOnSpecs) {
1025
+ if (depSpec === targetSpecName || depSpec === specPath) {
1026
+ throw new Error(`Cannot link spec to itself: ${sanitizeUserInput(depSpec)}`);
1029
1027
  }
1030
- const relResolvedPath = await resolveSpecPath(relSpec, cwd, specsDir);
1031
- if (!relResolvedPath) {
1032
- throw new Error(`Spec not found: ${sanitizeUserInput(relSpec)}`);
1028
+ const depResolvedPath = await resolveSpecPath(depSpec, cwd, specsDir);
1029
+ if (!depResolvedPath) {
1030
+ throw new Error(`Spec not found: ${sanitizeUserInput(depSpec)}`);
1033
1031
  }
1034
- if (relResolvedPath === resolvedPath) {
1035
- throw new Error(`Cannot link spec to itself: ${sanitizeUserInput(relSpec)}`);
1032
+ if (depResolvedPath === resolvedPath) {
1033
+ throw new Error(`Cannot link spec to itself: ${sanitizeUserInput(depSpec)}`);
1036
1034
  }
1037
- const relSpecName = path12.basename(relResolvedPath);
1038
- resolvedRelationships.set(relSpec, relSpecName);
1035
+ const depSpecName = path12.basename(depResolvedPath);
1036
+ resolvedDependencies.set(depSpec, depSpecName);
1039
1037
  }
1040
- const { parseFrontmatter: parseFrontmatter2 } = await import('./frontmatter-R2DANL5X.js');
1038
+ const { parseFrontmatter: parseFrontmatter2 } = await import('./frontmatter-6ZBAGOEU.js');
1041
1039
  const currentFrontmatter = await parseFrontmatter2(specFile);
1042
1040
  const currentDependsOn = currentFrontmatter?.depends_on || [];
1043
- const currentRelated = currentFrontmatter?.related || [];
1044
- const updates = {};
1045
- if (dependsOnSpecs.length > 0) {
1046
- const newDependsOn = [...currentDependsOn];
1047
- let added = 0;
1048
- for (const spec of dependsOnSpecs) {
1049
- const resolvedName = resolvedRelationships.get(spec);
1050
- if (resolvedName && !newDependsOn.includes(resolvedName)) {
1051
- newDependsOn.push(resolvedName);
1052
- added++;
1053
- }
1054
- }
1055
- updates.depends_on = newDependsOn;
1056
- if (added === 0) {
1057
- console.log(chalk15.gray(`\u2139 Dependencies already exist, no changes made`));
1058
- }
1059
- }
1060
- if (relatedSpecs.length > 0) {
1061
- const newRelated = [...currentRelated];
1062
- let added = 0;
1063
- const bidirectionalUpdates = [];
1064
- for (const spec of relatedSpecs) {
1065
- const resolvedName = resolvedRelationships.get(spec);
1066
- if (resolvedName && !newRelated.includes(resolvedName)) {
1067
- newRelated.push(resolvedName);
1068
- added++;
1069
- bidirectionalUpdates.push(resolvedName);
1070
- }
1071
- }
1072
- updates.related = newRelated;
1073
- for (const relSpecName of bidirectionalUpdates) {
1074
- const relSpecPath = await resolveSpecPath(relSpecName, cwd, specsDir);
1075
- if (relSpecPath) {
1076
- const relSpecFile = await getSpecFile(relSpecPath, config.structure.defaultFile);
1077
- if (relSpecFile) {
1078
- const relFrontmatter = await parseFrontmatter2(relSpecFile);
1079
- const relCurrentRelated = relFrontmatter?.related || [];
1080
- if (!relCurrentRelated.includes(targetSpecName)) {
1081
- await updateFrontmatter(relSpecFile, {
1082
- related: [...relCurrentRelated, targetSpecName]
1083
- });
1084
- console.log(chalk15.gray(` Updated: ${sanitizeUserInput(relSpecName)} (bidirectional)`));
1085
- }
1086
- }
1087
- }
1088
- }
1089
- if (added === 0) {
1090
- console.log(chalk15.gray(`\u2139 Related specs already exist, no changes made`));
1091
- }
1092
- }
1093
- if (updates.depends_on && updates.depends_on.length > 0) {
1094
- const cycles = detectCycles(targetSpecName, updates.depends_on, specMap);
1095
- if (cycles.length > 0) {
1096
- console.log(chalk15.yellow(`\u26A0\uFE0F Dependency cycle detected: ${cycles.join(" \u2192 ")}`));
1041
+ const newDependsOn = [...currentDependsOn];
1042
+ let added = 0;
1043
+ for (const spec of dependsOnSpecs) {
1044
+ const resolvedName = resolvedDependencies.get(spec);
1045
+ if (resolvedName && !newDependsOn.includes(resolvedName)) {
1046
+ newDependsOn.push(resolvedName);
1047
+ added++;
1097
1048
  }
1098
1049
  }
1099
- await updateFrontmatter(specFile, updates);
1100
- const updatedFields = [];
1101
- if (dependsOnSpecs.length > 0) {
1102
- updatedFields.push(`depends_on: ${dependsOnSpecs.join(", ")}`);
1050
+ if (added === 0) {
1051
+ console.log(chalk15.gray(`\u2139 Dependencies already exist, no changes made`));
1052
+ return;
1103
1053
  }
1104
- if (relatedSpecs.length > 0) {
1105
- updatedFields.push(`related: ${relatedSpecs.join(", ")}`);
1054
+ const cycles = detectCycles(targetSpecName, newDependsOn, specMap);
1055
+ if (cycles.length > 0) {
1056
+ console.log(chalk15.yellow(`\u26A0\uFE0F Dependency cycle detected: ${cycles.join(" \u2192 ")}`));
1106
1057
  }
1107
- console.log(chalk15.green(`\u2713 Added relationships: ${updatedFields.join(", ")}`));
1058
+ await updateFrontmatter(specFile, { depends_on: newDependsOn });
1059
+ console.log(chalk15.green(`\u2713 Added dependencies: ${dependsOnSpecs.join(", ")}`));
1108
1060
  console.log(chalk15.gray(` Updated: ${sanitizeUserInput(path12.relative(cwd, resolvedPath))}`));
1109
1061
  }
1110
1062
  function detectCycles(startSpec, dependsOn, specMap, visited = /* @__PURE__ */ new Set(), path25 = []) {
@@ -1131,7 +1083,7 @@ function detectCycles(startSpec, dependsOn, specMap, visited = /* @__PURE__ */ n
1131
1083
 
1132
1084
  // src/commands/create.ts
1133
1085
  function createCommand() {
1134
- return new Command("create").description("Create new spec in folder structure").argument("<name>", "Name of the spec").option("--title <title>", "Set custom title").option("--description <desc>", "Set initial description").option("--tags <tags>", "Set tags (comma-separated)").option("--priority <priority>", "Set priority (low, medium, high, critical)").option("--assignee <name>", "Set assignee").option("--template <template>", "Use a specific template").option("--field <name=value...>", "Set custom field (can specify multiple)").option("--no-prefix", "Skip date prefix even if configured").option("--depends-on <specs>", "Add dependencies (comma-separated spec numbers or names)").option("--related <specs>", "Add related specs (comma-separated spec numbers or names)").action(async (name, options) => {
1086
+ return new Command("create").description("Create new spec in folder structure").argument("<name>", "Name of the spec").option("--title <title>", "Set custom title").option("--description <desc>", "Set initial description").option("--tags <tags>", "Set tags (comma-separated)").option("--priority <priority>", "Set priority (low, medium, high, critical)").option("--assignee <name>", "Set assignee").option("--template <template>", "Use a specific template").option("--field <name=value...>", "Set custom field (can specify multiple)").option("--no-prefix", "Skip date prefix even if configured").option("--depends-on <specs>", "Add dependencies (comma-separated spec numbers or names)").action(async (name, options) => {
1135
1087
  const customFields = parseCustomFieldOptions(options.field);
1136
1088
  const createOptions = {
1137
1089
  title: options.title,
@@ -1142,8 +1094,7 @@ function createCommand() {
1142
1094
  template: options.template,
1143
1095
  customFields: Object.keys(customFields).length > 0 ? customFields : void 0,
1144
1096
  noPrefix: options.prefix === false,
1145
- dependsOn: options.dependsOn ? options.dependsOn.split(",").map((s) => s.trim()) : void 0,
1146
- related: options.related ? options.related.split(",").map((s) => s.trim()) : void 0
1097
+ dependsOn: options.dependsOn ? options.dependsOn.split(",").map((s) => s.trim()) : void 0
1147
1098
  };
1148
1099
  await createSpec(name, createOptions);
1149
1100
  });
@@ -1254,7 +1205,7 @@ async function createSpec(name, options = {}) {
1254
1205
  frontmatter: parsed.data
1255
1206
  };
1256
1207
  parsed.content = resolveVariables(parsed.content, contextWithFrontmatter);
1257
- const { enrichWithTimestamps } = await import('./frontmatter-R2DANL5X.js');
1208
+ const { enrichWithTimestamps } = await import('./frontmatter-6ZBAGOEU.js');
1258
1209
  enrichWithTimestamps(parsed.data);
1259
1210
  content = matter.stringify(parsed.content, parsed.data);
1260
1211
  if (options.description) {
@@ -1295,13 +1246,11 @@ ${options.description}`
1295
1246
  console.log(chalk15.green(`\u2713 Created: ${sanitizeUserInput(specDir)}/`));
1296
1247
  console.log(chalk15.gray(` Edit: ${sanitizeUserInput(specFile)}`));
1297
1248
  }
1298
- const hasRelationships = options.dependsOn && options.dependsOn.length > 0 || options.related && options.related.length > 0;
1299
- if (hasRelationships) {
1249
+ if (options.dependsOn && options.dependsOn.length > 0) {
1300
1250
  const newSpecName = path12.basename(specDir);
1301
1251
  try {
1302
1252
  await linkSpec(newSpecName, {
1303
- dependsOn: options.dependsOn?.join(","),
1304
- related: options.related?.join(",")
1253
+ dependsOn: options.dependsOn.join(",")
1305
1254
  });
1306
1255
  } catch (error) {
1307
1256
  console.log(chalk15.yellow(`\u26A0\uFE0F Warning: Failed to add relationships: ${error.message}`));
@@ -1546,9 +1495,9 @@ function renderGroupedList(specs, groupExtractor) {
1546
1495
  }
1547
1496
  }
1548
1497
  function unlinkCommand() {
1549
- return new Command("unlink").description("Remove relationships between specs (depends_on, related)").argument("<spec>", "Spec to update").option("--depends-on [specs]", "Remove dependencies (comma-separated spec numbers or names, or use with --all)").option("--related [specs]", "Remove related specs (comma-separated spec numbers or names, or use with --all)").option("--all", "Remove all relationships of the specified type(s)").action(async (specPath, options) => {
1550
- if (!options.dependsOn && !options.related) {
1551
- console.error("Error: At least one relationship type required (--depends-on or --related)");
1498
+ return new Command("unlink").description("Remove dependencies between specs").argument("<spec>", "Spec to update").option("--depends-on [specs]", "Remove dependencies (comma-separated spec numbers or names, or use with --all)").option("--all", "Remove all dependencies").action(async (specPath, options) => {
1499
+ if (!options.dependsOn) {
1500
+ console.error("Error: --depends-on is required");
1552
1501
  process.exit(1);
1553
1502
  }
1554
1503
  await unlinkSpec(specPath, options);
@@ -1567,97 +1516,35 @@ async function unlinkSpec(specPath, options) {
1567
1516
  if (!specFile) {
1568
1517
  throw new Error(`No spec file found in: ${sanitizeUserInput(specPath)}`);
1569
1518
  }
1570
- const targetSpecName = path12.basename(resolvedPath);
1571
- const { parseFrontmatter: parseFrontmatter2 } = await import('./frontmatter-R2DANL5X.js');
1519
+ const { parseFrontmatter: parseFrontmatter2 } = await import('./frontmatter-6ZBAGOEU.js');
1572
1520
  const currentFrontmatter = await parseFrontmatter2(specFile);
1573
1521
  const currentDependsOn = currentFrontmatter?.depends_on || [];
1574
- const currentRelated = currentFrontmatter?.related || [];
1575
- const updates = {};
1522
+ let newDependsOn;
1576
1523
  let removedCount = 0;
1577
- if (options.dependsOn !== void 0) {
1578
- if (options.all || options.dependsOn === true) {
1579
- updates.depends_on = [];
1580
- removedCount += currentDependsOn.length;
1581
- } else {
1582
- const toRemove = options.dependsOn.split(",").map((s) => s.trim());
1583
- const resolvedToRemove = /* @__PURE__ */ new Set();
1584
- for (const spec of toRemove) {
1585
- const resolvedSpecPath = await resolveSpecPath(spec, cwd, specsDir);
1586
- if (resolvedSpecPath) {
1587
- resolvedToRemove.add(path12.basename(resolvedSpecPath));
1588
- } else {
1589
- resolvedToRemove.add(spec);
1590
- }
1591
- }
1592
- const newDependsOn = currentDependsOn.filter((dep) => !resolvedToRemove.has(dep));
1593
- removedCount += currentDependsOn.length - newDependsOn.length;
1594
- updates.depends_on = newDependsOn;
1595
- }
1596
- }
1597
- if (options.related !== void 0) {
1598
- if (options.all || options.related === true) {
1599
- for (const relSpec of currentRelated) {
1600
- const relSpecPath = await resolveSpecPath(relSpec, cwd, specsDir);
1601
- if (relSpecPath) {
1602
- const relSpecFile = await getSpecFile(relSpecPath, config.structure.defaultFile);
1603
- if (relSpecFile) {
1604
- const relFrontmatter = await parseFrontmatter2(relSpecFile);
1605
- const relCurrentRelated = relFrontmatter?.related || [];
1606
- const relNewRelated = relCurrentRelated.filter((r) => r !== targetSpecName);
1607
- if (relNewRelated.length !== relCurrentRelated.length) {
1608
- await updateFrontmatter(relSpecFile, {
1609
- related: relNewRelated
1610
- });
1611
- console.log(chalk15.gray(` Updated: ${sanitizeUserInput(relSpec)} (bidirectional)`));
1612
- }
1613
- }
1614
- }
1615
- }
1616
- removedCount += currentRelated.length;
1617
- updates.related = [];
1618
- } else {
1619
- const toRemove = options.related.split(",").map((s) => s.trim());
1620
- const resolvedToRemove = /* @__PURE__ */ new Set();
1621
- for (const spec of toRemove) {
1622
- const resolvedSpecPath = await resolveSpecPath(spec, cwd, specsDir);
1623
- if (resolvedSpecPath) {
1624
- const specName = path12.basename(resolvedSpecPath);
1625
- resolvedToRemove.add(specName);
1626
- const relSpecFile = await getSpecFile(resolvedSpecPath, config.structure.defaultFile);
1627
- if (relSpecFile) {
1628
- const relFrontmatter = await parseFrontmatter2(relSpecFile);
1629
- const relCurrentRelated = relFrontmatter?.related || [];
1630
- const relNewRelated = relCurrentRelated.filter((r) => r !== targetSpecName);
1631
- if (relNewRelated.length !== relCurrentRelated.length) {
1632
- await updateFrontmatter(relSpecFile, {
1633
- related: relNewRelated
1634
- });
1635
- console.log(chalk15.gray(` Updated: ${sanitizeUserInput(specName)} (bidirectional)`));
1636
- }
1637
- }
1638
- } else {
1639
- resolvedToRemove.add(spec);
1640
- }
1524
+ if (options.all || options.dependsOn === true) {
1525
+ newDependsOn = [];
1526
+ removedCount = currentDependsOn.length;
1527
+ } else {
1528
+ const toRemove = options.dependsOn.split(",").map((s) => s.trim());
1529
+ const resolvedToRemove = /* @__PURE__ */ new Set();
1530
+ for (const spec of toRemove) {
1531
+ const resolvedSpecPath = await resolveSpecPath(spec, cwd, specsDir);
1532
+ if (resolvedSpecPath) {
1533
+ resolvedToRemove.add(path12.basename(resolvedSpecPath));
1534
+ } else {
1535
+ resolvedToRemove.add(spec);
1641
1536
  }
1642
- const newRelated = currentRelated.filter((rel) => !resolvedToRemove.has(rel));
1643
- removedCount += currentRelated.length - newRelated.length;
1644
- updates.related = newRelated;
1645
1537
  }
1538
+ newDependsOn = currentDependsOn.filter((dep) => !resolvedToRemove.has(dep));
1539
+ removedCount = currentDependsOn.length - newDependsOn.length;
1646
1540
  }
1647
- await updateFrontmatter(specFile, updates);
1648
1541
  if (removedCount === 0) {
1649
- console.log(chalk15.gray(`\u2139 No matching relationships found to remove`));
1650
- } else {
1651
- const updatedFields = [];
1652
- if (options.dependsOn !== void 0) {
1653
- updatedFields.push(`depends_on`);
1654
- }
1655
- if (options.related !== void 0) {
1656
- updatedFields.push(`related`);
1657
- }
1658
- console.log(chalk15.green(`\u2713 Removed relationships: ${updatedFields.join(", ")} (${removedCount} total)`));
1659
- console.log(chalk15.gray(` Updated: ${sanitizeUserInput(path12.relative(cwd, resolvedPath))}`));
1542
+ console.log(chalk15.gray(`\u2139 No matching dependencies found to remove`));
1543
+ return;
1660
1544
  }
1545
+ await updateFrontmatter(specFile, { depends_on: newDependsOn });
1546
+ console.log(chalk15.green(`\u2713 Removed ${removedCount} dependencies`));
1547
+ console.log(chalk15.gray(` Updated: ${sanitizeUserInput(path12.relative(cwd, resolvedPath))}`));
1661
1548
  }
1662
1549
  function templatesCommand() {
1663
1550
  const cmd = new Command("templates").description("Manage spec templates");
@@ -3490,7 +3377,7 @@ async function migrateAuto(inputPath, documents, format, config, options) {
3490
3377
  if ((options.backfill || options.auto) && !options.dryRun) {
3491
3378
  console.log("\x1B[36mRunning backfill...\x1B[0m");
3492
3379
  try {
3493
- const { backfillCommand: backfillCommand2 } = await import('./backfill-QIQLXSUG.js');
3380
+ const { backfillCommand: backfillCommand2 } = await import('./backfill-446GBTBC.js');
3494
3381
  const cmd = backfillCommand2();
3495
3382
  await cmd.parseAsync(["node", "lean-spec", "--all", "--assignee"], { from: "user" });
3496
3383
  console.log("\x1B[32m\u2713\x1B[0m Backfill complete\n");
@@ -3501,7 +3388,7 @@ async function migrateAuto(inputPath, documents, format, config, options) {
3501
3388
  if (!options.skipValidation && !options.dryRun) {
3502
3389
  console.log("\x1B[36mValidating...\x1B[0m");
3503
3390
  try {
3504
- const { validateSpecs: validateSpecs2 } = await import('./validate-PT6GAS57.js');
3391
+ const { validateSpecs: validateSpecs2 } = await import('./validate-DIWYTDEF.js');
3505
3392
  await validateSpecs2({});
3506
3393
  console.log("\x1B[32m\u2713\x1B[0m Validation complete\n");
3507
3394
  } catch {
@@ -3594,7 +3481,7 @@ async function outputManualInstructions(inputPath, documents, config, format) {
3594
3481
  console.log("\x1B[1mImportant Rules:\x1B[0m");
3595
3482
  console.log("- Preserve decision rationale and context");
3596
3483
  console.log("- Map status appropriately to LeanSpec states");
3597
- console.log("- Link related specs using `related` field (manual frontmatter edit)");
3484
+ console.log("- Link dependencies using `lean-spec link --depends-on`");
3598
3485
  console.log("- Follow LeanSpec first principles: clarity over completeness");
3599
3486
  console.log("- Keep specs under 400 lines (split if needed)");
3600
3487
  console.log();
@@ -4719,7 +4606,7 @@ function depsCommand(specPath, options = {}) {
4719
4606
  if (typeof specPath === "string") {
4720
4607
  return showDeps(specPath, options);
4721
4608
  }
4722
- return new Command("deps").description("Show dependency graph for a spec. Related specs (\u27F7) are shown bidirectionally, depends_on (\u2192) are directional.").argument("<spec>", "Spec to show dependencies for").option("--depth <n>", "Show N levels deep (default: 3)", parseInt).option("--graph", "ASCII graph visualization").option("--json", "Output as JSON").option("--complete", "Show complete graph (default: all relationships)").option("--upstream", "Show only upstream dependencies").option("--downstream", "Show only downstream dependents").option("--impact", "Show impact radius (all affected specs)").action(async (target, opts) => {
4609
+ return new Command("deps").description("Show dependency graph for a spec. Shows upstream dependencies (\u2192) and downstream dependents (\u2190).").argument("<spec>", "Spec to show dependencies for").option("--depth <n>", "Show N levels deep (default: 3)", parseInt).option("--graph", "ASCII graph visualization").option("--json", "Output as JSON").option("--complete", "Show complete graph (default: all relationships)").option("--upstream", "Show only upstream dependencies").option("--downstream", "Show only downstream dependents").option("--impact", "Show impact radius (all affected specs)").action(async (target, opts) => {
4723
4610
  await showDeps(target, opts);
4724
4611
  });
4725
4612
  }
@@ -4754,12 +4641,10 @@ async function showDeps(specPath, options = {}) {
4754
4641
  }
4755
4642
  let dependsOn = [];
4756
4643
  let requiredBy = [];
4757
- let related = [];
4758
4644
  if (mode === "complete") {
4759
4645
  const completeGraph = graph.getCompleteGraph(spec.path);
4760
4646
  dependsOn = completeGraph.dependsOn;
4761
4647
  requiredBy = completeGraph.requiredBy;
4762
- related = completeGraph.related;
4763
4648
  } else if (mode === "upstream") {
4764
4649
  dependsOn = graph.getUpstream(spec.path, options.depth || 3);
4765
4650
  } else if (mode === "downstream") {
@@ -4768,7 +4653,6 @@ async function showDeps(specPath, options = {}) {
4768
4653
  const impact = graph.getImpactRadius(spec.path, options.depth || 3);
4769
4654
  dependsOn = impact.upstream;
4770
4655
  requiredBy = impact.downstream;
4771
- related = impact.related;
4772
4656
  }
4773
4657
  if (options.json) {
4774
4658
  const data = {
@@ -4781,9 +4665,6 @@ async function showDeps(specPath, options = {}) {
4781
4665
  if (mode === "complete" || mode === "downstream" || mode === "impact") {
4782
4666
  data.requiredBy = requiredBy.map((s) => ({ path: s.path, status: s.frontmatter.status }));
4783
4667
  }
4784
- if (mode === "complete" || mode === "impact") {
4785
- data.related = related.map((s) => ({ path: s.path, status: s.frontmatter.status }));
4786
- }
4787
4668
  if (mode === "complete" && (options.graph || dependsOn.length > 0)) {
4788
4669
  data.chain = buildDependencyChain(spec, specMap, options.depth || 3);
4789
4670
  }
@@ -4793,9 +4674,9 @@ async function showDeps(specPath, options = {}) {
4793
4674
  console.log("");
4794
4675
  console.log(chalk15.green(`\u{1F4E6} Dependencies for ${chalk15.cyan(sanitizeUserInput(spec.path))}`));
4795
4676
  console.log("");
4796
- const hasAnyRelationships = dependsOn.length > 0 || requiredBy.length > 0 || related.length > 0;
4797
- if (!hasAnyRelationships) {
4798
- console.log(chalk15.gray(" No dependencies or relationships"));
4677
+ const hasAnyDependencies = dependsOn.length > 0 || requiredBy.length > 0;
4678
+ if (!hasAnyDependencies) {
4679
+ console.log(chalk15.gray(" No dependencies"));
4799
4680
  console.log("");
4800
4681
  return;
4801
4682
  }
@@ -4817,14 +4698,6 @@ async function showDeps(specPath, options = {}) {
4817
4698
  }
4818
4699
  console.log("");
4819
4700
  }
4820
- if ((mode === "complete" || mode === "impact") && related.length > 0) {
4821
- console.log(chalk15.bold("Related Specs:"));
4822
- for (const rel of related) {
4823
- const status = getStatusIndicator(rel.frontmatter.status);
4824
- console.log(` \u27F7 ${sanitizeUserInput(rel.path)} ${status}`);
4825
- }
4826
- console.log("");
4827
- }
4828
4701
  if (mode === "complete" && (options.graph || dependsOn.length > 0)) {
4829
4702
  console.log(chalk15.bold("Dependency Chain:"));
4830
4703
  const chain = buildDependencyChain(spec, specMap, options.depth || 3);
@@ -4832,10 +4705,10 @@ async function showDeps(specPath, options = {}) {
4832
4705
  console.log("");
4833
4706
  }
4834
4707
  if (mode === "impact") {
4835
- const total = dependsOn.length + requiredBy.length + related.length;
4708
+ const total = dependsOn.length + requiredBy.length;
4836
4709
  console.log(chalk15.bold(`Impact Summary:`));
4837
4710
  console.log(` Changing this spec affects ${chalk15.yellow(total)} specs total`);
4838
- console.log(` Upstream: ${dependsOn.length} | Downstream: ${requiredBy.length} | Related: ${related.length}`);
4711
+ console.log(` Upstream: ${dependsOn.length} | Downstream: ${requiredBy.length}`);
4839
4712
  console.log("");
4840
4713
  }
4841
4714
  }
@@ -6663,8 +6536,7 @@ function createTool() {
6663
6536
  priority: z.enum(["low", "medium", "high", "critical"]).optional().describe('Priority level for the spec. Defaults to "medium" if not specified.'),
6664
6537
  assignee: z.string().optional().describe("Person responsible for this spec."),
6665
6538
  template: z.string().optional().describe('Template name to use (e.g., "minimal", "enterprise"). Uses default template if omitted.'),
6666
- dependsOn: z.array(z.string()).optional().describe('Specs this depends on (e.g., ["045-api-design", "046-database"]). Creates upstream dependencies.'),
6667
- related: z.array(z.string()).optional().describe('Related specs (e.g., ["047-frontend"]). Creates bidirectional relationships.')
6539
+ dependsOn: z.array(z.string()).optional().describe('Specs this depends on (e.g., ["045-api-design", "046-database"]). Creates upstream dependencies.')
6668
6540
  },
6669
6541
  outputSchema: {
6670
6542
  success: z.boolean(),
@@ -6687,8 +6559,7 @@ function createTool() {
6687
6559
  priority: input.priority,
6688
6560
  assignee: input.assignee,
6689
6561
  template: input.template,
6690
- dependsOn: input.dependsOn,
6691
- related: input.related
6562
+ dependsOn: input.dependsOn
6692
6563
  });
6693
6564
  const output = {
6694
6565
  success: true,
@@ -6726,7 +6597,7 @@ async function getDepsData(specPath, mode = "complete") {
6726
6597
  }
6727
6598
  const allSpecs = await loadAllSpecs({ includeArchived: true });
6728
6599
  const graph = new SpecDependencyGraph(allSpecs);
6729
- const spec = allSpecs.find((s) => s.path === resolvedPath);
6600
+ const spec = allSpecs.find((s) => s.fullPath === resolvedPath || s.path === resolvedPath);
6730
6601
  if (!spec) {
6731
6602
  throw new Error(`Spec not found: ${specPath}`);
6732
6603
  }
@@ -6747,11 +6618,6 @@ async function getDepsData(specPath, mode = "complete") {
6747
6618
  path: s.path,
6748
6619
  status: s.frontmatter.status,
6749
6620
  priority: s.frontmatter.priority
6750
- })),
6751
- related: completeGraph.related.map((s) => ({
6752
- path: s.path,
6753
- status: s.frontmatter.status,
6754
- priority: s.frontmatter.priority
6755
6621
  }))
6756
6622
  };
6757
6623
  } else if (mode === "upstream") {
@@ -6800,12 +6666,7 @@ async function getDepsData(specPath, mode = "complete") {
6800
6666
  status: s.frontmatter.status,
6801
6667
  priority: s.frontmatter.priority
6802
6668
  })),
6803
- related: impact.related.map((s) => ({
6804
- path: s.path,
6805
- status: s.frontmatter.status,
6806
- priority: s.frontmatter.priority
6807
- })),
6808
- total: impact.upstream.length + impact.downstream.length + impact.related.length
6669
+ total: impact.upstream.length + impact.downstream.length
6809
6670
  };
6810
6671
  }
6811
6672
  return getDepsData(specPath, "complete");
@@ -6815,7 +6676,7 @@ function depsTool() {
6815
6676
  "deps",
6816
6677
  {
6817
6678
  title: "Get Dependencies",
6818
- description: "Analyze complete spec dependency graph (upstream, downstream, bidirectional). Shows which specs this depends on (dependsOn), which depend on this spec (requiredBy), and related work (related). Use this to understand impact of changes and work order.",
6679
+ description: "Analyze spec dependency graph (upstream and downstream). Shows which specs this depends on (dependsOn) and which depend on this spec (requiredBy). Use this to understand impact of changes and work order.",
6819
6680
  inputSchema: {
6820
6681
  specPath: z.string().describe('The spec to analyze. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), or full folder name (e.g., "045-unified-dashboard").'),
6821
6682
  mode: z.enum(["complete", "upstream", "downstream", "impact"]).optional().describe("View mode: complete (all relationships), upstream (dependencies only), downstream (dependents only), impact (full impact radius with summary). Defaults to complete.")
@@ -6897,11 +6758,10 @@ function linkTool() {
6897
6758
  "link",
6898
6759
  {
6899
6760
  title: "Link Specs",
6900
- description: "Add relationships between specs (depends_on for dependencies, related for bidirectional links). Use this after creating a spec to connect it to related specs, or to add relationships to existing specs.",
6761
+ description: "Add dependency relationships between specs. Use this after creating a spec to connect it to specs it depends on, or to add dependencies to existing specs.",
6901
6762
  inputSchema: {
6902
- specPath: z.string().describe('The spec to add relationships to. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), or full folder name (e.g., "045-unified-dashboard").'),
6903
- dependsOn: z.string().optional().describe('Comma-separated specs this depends on (e.g., "045,046" or "api-design,database"). Creates upstream dependencies.'),
6904
- related: z.string().optional().describe('Comma-separated related specs (e.g., "047,frontend"). Creates bidirectional relationships - both specs will reference each other.')
6763
+ specPath: z.string().describe('The spec to add dependencies to. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), or full folder name (e.g., "045-unified-dashboard").'),
6764
+ dependsOn: z.string().describe('Comma-separated specs this depends on (e.g., "045,046" or "api-design,database"). Creates upstream dependencies.')
6905
6765
  },
6906
6766
  outputSchema: {
6907
6767
  success: z.boolean(),
@@ -6912,10 +6772,10 @@ function linkTool() {
6912
6772
  async (input, _extra) => {
6913
6773
  const originalLog = console.log;
6914
6774
  try {
6915
- if (!input.dependsOn && !input.related) {
6775
+ if (!input.dependsOn) {
6916
6776
  const output2 = {
6917
6777
  success: false,
6918
- message: "At least one relationship type required (dependsOn or related)"
6778
+ message: "dependsOn is required"
6919
6779
  };
6920
6780
  return {
6921
6781
  content: [{ type: "text", text: JSON.stringify(output2, null, 2) }],
@@ -6933,8 +6793,7 @@ function linkTool() {
6933
6793
  }
6934
6794
  };
6935
6795
  await linkSpec(input.specPath, {
6936
- dependsOn: input.dependsOn,
6937
- related: input.related
6796
+ dependsOn: input.dependsOn
6938
6797
  });
6939
6798
  const output = {
6940
6799
  success: true,
@@ -7337,12 +7196,11 @@ function unlinkTool() {
7337
7196
  "unlink",
7338
7197
  {
7339
7198
  title: "Unlink Specs",
7340
- description: "Remove relationships between specs. Can remove specific relationships or all relationships of a type. Related links are removed bidirectionally (from both specs).",
7199
+ description: "Remove dependency relationships between specs. Can remove specific dependencies or all dependencies.",
7341
7200
  inputSchema: {
7342
- specPath: z.string().describe('The spec to remove relationships from. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), or full folder name (e.g., "045-unified-dashboard").'),
7201
+ specPath: z.string().describe('The spec to remove dependencies from. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), or full folder name (e.g., "045-unified-dashboard").'),
7343
7202
  dependsOn: z.string().optional().describe('Comma-separated dependencies to remove (e.g., "045,046"). Leave empty with removeAll=true to remove all dependencies.'),
7344
- related: z.string().optional().describe('Comma-separated related specs to remove (e.g., "047,frontend"). Removes bidirectionally. Leave empty with removeAll=true to remove all.'),
7345
- removeAll: z.boolean().optional().describe("When true, removes ALL relationships of the specified type(s). Use with dependsOn or related to clear all of that type.")
7203
+ removeAll: z.boolean().optional().describe("When true, removes ALL dependencies. Use with dependsOn to clear all.")
7346
7204
  },
7347
7205
  outputSchema: {
7348
7206
  success: z.boolean(),
@@ -7354,10 +7212,10 @@ function unlinkTool() {
7354
7212
  async (input, _extra) => {
7355
7213
  const originalLog = console.log;
7356
7214
  try {
7357
- if (!input.dependsOn && !input.related && !input.removeAll) {
7215
+ if (!input.dependsOn && !input.removeAll) {
7358
7216
  const output2 = {
7359
7217
  success: false,
7360
- message: "At least one relationship type required (dependsOn or related)"
7218
+ message: "Either dependsOn or removeAll is required"
7361
7219
  };
7362
7220
  return {
7363
7221
  content: [{ type: "text", text: JSON.stringify(output2, null, 2) }],
@@ -7385,9 +7243,7 @@ function unlinkTool() {
7385
7243
  if (input.dependsOn !== void 0) {
7386
7244
  options.dependsOn = input.dependsOn || true;
7387
7245
  } else if (input.removeAll) {
7388
- }
7389
- if (input.related !== void 0) {
7390
- options.related = input.related || true;
7246
+ options.dependsOn = true;
7391
7247
  }
7392
7248
  await unlinkSpec(input.specPath, options);
7393
7249
  const output = {
@@ -7493,7 +7349,7 @@ function validateTool() {
7493
7349
  inputSchema: {
7494
7350
  specs: z.array(z.string()).optional().describe("Specific specs to validate. If omitted, validates all specs in the project."),
7495
7351
  maxLines: z.number().optional().describe("Custom line limit for complexity checks (default: 400 lines)."),
7496
- checkDeps: z.boolean().optional().describe("Check for content/frontmatter dependency alignment. Detects when spec content references other specs but those references are not in frontmatter depends_on/related fields.")
7352
+ checkDeps: z.boolean().optional().describe("Check for content/frontmatter dependency alignment. Detects when spec content references other specs but those references are not in frontmatter depends_on fields.")
7497
7353
  },
7498
7354
  outputSchema: {
7499
7355
  passed: z.boolean(),
@@ -7815,7 +7671,7 @@ function planProjectRoadmapPrompt() {
7815
7671
  3. **Identify Tasks**: List key tasks and work items for each phase
7816
7672
  4. **Map Dependencies**: Establish dependencies between tasks (what must be done first)
7817
7673
  5. **Create Specs**: Create specification documents for major work items using the \`create\` tool
7818
- 6. **Set Relationships**: Use \`link\` tool to establish \`depends_on\` and \`related\` relationships
7674
+ 6. **Set Dependencies**: Use \`link\` tool to establish \`depends_on\` relationships between specs
7819
7675
  7. **Timeline Estimation**: Provide realistic timeline based on task complexity and project velocity
7820
7676
  8. **Risk Analysis**: Identify risks, unknowns, and mitigation strategies
7821
7677
 
@@ -7910,7 +7766,7 @@ For the work you're currently doing:
7910
7766
  For any specs you've worked on:
7911
7767
  1. Update status if changed (\`planned\` \u2192 \`in-progress\` \u2192 \`complete\`)
7912
7768
  2. Document key decisions or changes in the spec content
7913
- 3. Link related specs if you discovered connections
7769
+ 3. Link dependencies if you discover blocking relationships
7914
7770
 
7915
7771
  ### Step 4: Validate (Before Completing)
7916
7772
  Before marking any spec as complete, run validation:
@@ -7981,12 +7837,12 @@ create {
7981
7837
  \`\`\`
7982
7838
 
7983
7839
  ### Step 3: Link Dependencies (CRITICAL)
7984
- After creating, **immediately** link any referenced specs:
7840
+ After creating, **immediately** link any specs this depends on:
7985
7841
 
7986
- For each spec mentioned in content:
7987
- - "Depends on spec 045" \u2192 \`link { "spec": "your-spec", "dependsOn": ["045"] }\`
7988
- - "Related to spec 072" \u2192 \`link { "spec": "your-spec", "related": ["072"] }\`
7989
- - "See spec 110" \u2192 \`link { "spec": "your-spec", "related": ["110"] }\`
7842
+ For each dependency mentioned in content:
7843
+ - "Depends on spec 045" \u2192 \`link { "spec": "your-spec", "dependsOn": "045" }\`
7844
+ - "Blocked by spec 072" \u2192 \`link { "spec": "your-spec", "dependsOn": "072" }\`
7845
+ - "Requires spec 110" \u2192 \`link { "spec": "your-spec", "dependsOn": "110" }\`
7990
7846
 
7991
7847
  ### Step 4: Verify
7992
7848
  Use \`deps\` to verify all links are in place:
@@ -8007,9 +7863,8 @@ validate { "specs": ["your-spec"], "checkDeps": true }
8007
7863
  | Content Pattern | Link Type |
8008
7864
  |----------------|-----------|
8009
7865
  | "depends on", "blocked by", "requires" | dependsOn |
8010
- | "related to", "see also", "similar to" | related |
8011
- | "builds on" | dependsOn (if blocking) or related |
8012
- | "## Related Specs" section | related (link each one) |
7866
+ | "builds on", "extends" | dependsOn |
7867
+ | "after spec X is complete" | dependsOn |
8013
7868
 
8014
7869
  **Remember:** Content and frontmatter must stay aligned!`
8015
7870
  }
@@ -8086,5 +7941,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
8086
7941
  }
8087
7942
 
8088
7943
  export { agentCommand, analyzeCommand, archiveCommand, boardCommand, checkCommand, compactCommand, createCommand, createMcpServer, depsCommand, examplesCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, viewCommand };
8089
- //# sourceMappingURL=chunk-EBSQ7FUR.js.map
8090
- //# sourceMappingURL=chunk-EBSQ7FUR.js.map
7944
+ //# sourceMappingURL=chunk-BJHJ6IUO.js.map
7945
+ //# sourceMappingURL=chunk-BJHJ6IUO.js.map