sysprom 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +8 -0
  2. package/dist/src/canonical-json.d.ts +5 -1
  3. package/dist/src/canonical-json.js +5 -1
  4. package/dist/src/cli/define-command.d.ts +26 -0
  5. package/dist/src/cli/define-command.js +22 -0
  6. package/dist/src/cli/shared.d.ts +32 -2
  7. package/dist/src/cli/shared.js +29 -2
  8. package/dist/src/index.d.ts +0 -1
  9. package/dist/src/index.js +0 -1
  10. package/dist/src/io.d.ts +9 -2
  11. package/dist/src/io.js +9 -2
  12. package/dist/src/json-to-md.d.ts +13 -3
  13. package/dist/src/json-to-md.js +13 -3
  14. package/dist/src/md-to-json.d.ts +12 -3
  15. package/dist/src/md-to-json.js +12 -3
  16. package/dist/src/operations/add-node.d.ts +1 -2
  17. package/dist/src/operations/add-node.js +1 -2
  18. package/dist/src/operations/add-plan-task.d.ts +1 -2
  19. package/dist/src/operations/add-plan-task.js +1 -2
  20. package/dist/src/operations/add-relationship.d.ts +1 -2
  21. package/dist/src/operations/add-relationship.js +1 -2
  22. package/dist/src/operations/define-operation.d.ts +16 -8
  23. package/dist/src/operations/define-operation.js +12 -2
  24. package/dist/src/operations/mark-task-done.d.ts +1 -2
  25. package/dist/src/operations/mark-task-done.js +1 -2
  26. package/dist/src/operations/mark-task-undone.d.ts +1 -2
  27. package/dist/src/operations/mark-task-undone.js +1 -2
  28. package/dist/src/operations/next-id.d.ts +1 -2
  29. package/dist/src/operations/next-id.js +1 -2
  30. package/dist/src/operations/remove-node.d.ts +1 -2
  31. package/dist/src/operations/remove-node.js +1 -2
  32. package/dist/src/operations/remove-relationship.d.ts +1 -2
  33. package/dist/src/operations/remove-relationship.js +1 -2
  34. package/dist/src/operations/rename.d.ts +1 -2
  35. package/dist/src/operations/rename.js +1 -2
  36. package/dist/src/operations/task-list.d.ts +1 -2
  37. package/dist/src/operations/task-list.js +1 -2
  38. package/dist/src/operations/update-node.d.ts +1 -2
  39. package/dist/src/operations/update-node.js +1 -2
  40. package/dist/src/operations/update-plan-task.d.ts +1 -2
  41. package/dist/src/operations/update-plan-task.js +1 -2
  42. package/dist/src/schema.d.ts +9 -1
  43. package/dist/src/schema.js +9 -1
  44. package/dist/src/speckit/generate.d.ts +60 -6
  45. package/dist/src/speckit/generate.js +114 -6
  46. package/dist/src/speckit/parse.d.ts +61 -6
  47. package/dist/src/speckit/parse.js +105 -6
  48. package/dist/src/speckit/plan.d.ts +50 -0
  49. package/dist/src/speckit/plan.js +117 -0
  50. package/dist/src/speckit/project.d.ts +29 -0
  51. package/dist/src/speckit/project.js +49 -0
  52. package/dist/src/text.d.ts +25 -4
  53. package/dist/src/text.js +25 -4
  54. package/package.json +2 -1
@@ -5,6 +5,12 @@ import { join, basename } from "node:path";
5
5
  // ---------------------------------------------------------------------------
6
6
  /**
7
7
  * Parse markdown content into a hierarchical section tree by heading level.
8
+ * @param body - Markdown body text.
9
+ * @returns The result.
10
+ * @example
11
+ * ```ts
12
+ * const sections = parseSections(markdownBody);
13
+ * ```
8
14
  */
9
15
  function parseSections(body) {
10
16
  const lines = body.split("\n");
@@ -49,6 +55,12 @@ function parseSections(body) {
49
55
  }
50
56
  /**
51
57
  * Extract bold key-value pairs from markdown like "**Key**: value" or "**Key**: value text".
58
+ * @param content - Markdown file content.
59
+ * @returns The result.
60
+ * @example
61
+ * ```ts
62
+ * const meta = parseFrontMatterish(content);
63
+ * ```
52
64
  */
53
65
  function parseFrontMatterish(content) {
54
66
  const result = {};
@@ -64,6 +76,12 @@ function parseFrontMatterish(content) {
64
76
  }
65
77
  /**
66
78
  * Parse checkbox lines like "- [x] ID text" or "- [ ] ID text".
79
+ * @param body - Markdown body text.
80
+ * @returns The result.
81
+ * @example
82
+ * ```ts
83
+ * const items = parseCheckboxes(body);
84
+ * ```
67
85
  */
68
86
  function parseCheckboxes(body) {
69
87
  const items = [];
@@ -84,6 +102,12 @@ function parseCheckboxes(body) {
84
102
  }
85
103
  /**
86
104
  * Flatten all sections in the tree into a single array for easier searching.
105
+ * @param sections - Parsed section tree.
106
+ * @returns The result.
107
+ * @example
108
+ * ```ts
109
+ * const flat = flattenSections(sections);
110
+ * ```
87
111
  */
88
112
  function flattenSections(sections) {
89
113
  const result = [];
@@ -98,12 +122,25 @@ function flattenSections(sections) {
98
122
  }
99
123
  /**
100
124
  * Find the first section whose heading matches a predicate (searches entire tree).
125
+ * @param sections - Parsed section tree.
126
+ * @param predicate - Filter function.
127
+ * @returns The result.
128
+ * @example
129
+ * ```ts
130
+ * const s = findSection(sections, (h) => h.startsWith("Phase"));
131
+ * ```
101
132
  */
102
133
  function findSection(sections, predicate) {
103
134
  return flattenSections(sections).find((s) => predicate(s.heading));
104
135
  }
105
136
  /**
106
137
  * Convert status-like strings to NodeStatus. Recognizes common spec-kit patterns.
138
+ * @param value - Raw status string.
139
+ * @returns The result.
140
+ * @example
141
+ * ```ts
142
+ * mapStatusValue("Draft"); // => "proposed"
143
+ * ```
107
144
  */
108
145
  function mapStatusValue(value) {
109
146
  const lower = value.toLowerCase().trim();
@@ -130,6 +167,13 @@ function mapStatusValue(value) {
130
167
  }
131
168
  /**
132
169
  * Extract a single line value from body text (e.g., "**Created**: 2025-01-01").
170
+ * @param body - Markdown body text.
171
+ * @param key - Front-matter key to extract.
172
+ * @returns The result.
173
+ * @example
174
+ * ```ts
175
+ * extractValue(body, "Created"); // => "2025-01-01"
176
+ * ```
133
177
  */
134
178
  function extractValue(body, key) {
135
179
  const pattern = new RegExp(`^\\*\\*${key}\\*\\*:\\s*(.+)$`, "m");
@@ -139,7 +183,16 @@ function extractValue(body, key) {
139
183
  // ---------------------------------------------------------------------------
140
184
  // constitution.md parser
141
185
  // ---------------------------------------------------------------------------
142
- /** Parse a Spec-Kit constitution file into SysProM nodes (principles and invariants) and relationships. */
186
+ /**
187
+ * Parse a Spec-Kit constitution file into SysProM nodes (principles and invariants) and relationships.
188
+ * @param content - Markdown file content.
189
+ * @param idPrefix - ID prefix for generated nodes.
190
+ * @returns The result.
191
+ * @example
192
+ * ```ts
193
+ * const result = parseConstitution(content, "FEAT");
194
+ * ```
195
+ */
143
196
  export function parseConstitution(content, idPrefix) {
144
197
  const sections = parseSections(content);
145
198
  const nodes = [];
@@ -228,7 +281,16 @@ export function parseConstitution(content, idPrefix) {
228
281
  // ---------------------------------------------------------------------------
229
282
  // spec.md parser
230
283
  // ---------------------------------------------------------------------------
231
- /** Parse a Spec-Kit specification file into SysProM nodes (user stories, functional requirements, acceptance criteria). */
284
+ /**
285
+ * Parse a Spec-Kit specification file into SysProM nodes (user stories, functional requirements, acceptance criteria).
286
+ * @param content - Markdown file content.
287
+ * @param idPrefix - ID prefix for generated nodes.
288
+ * @returns The result.
289
+ * @example
290
+ * ```ts
291
+ * const result = parseSpec(content, "FEAT");
292
+ * ```
293
+ */
232
294
  export function parseSpec(content, idPrefix) {
233
295
  const sections = parseSections(content);
234
296
  const allSections = flattenSections(sections);
@@ -412,7 +474,16 @@ export function parseSpec(content, idPrefix) {
412
474
  // ---------------------------------------------------------------------------
413
475
  // plan.md parser
414
476
  // ---------------------------------------------------------------------------
415
- /** Parse a Spec-Kit plan file into SysProM nodes (phases, milestones) and relationships. */
477
+ /**
478
+ * Parse a Spec-Kit plan file into SysProM nodes (phases, milestones) and relationships.
479
+ * @param content - Markdown file content.
480
+ * @param idPrefix - ID prefix for generated nodes.
481
+ * @returns The result.
482
+ * @example
483
+ * ```ts
484
+ * const result = parsePlan(content, "FEAT");
485
+ * ```
486
+ */
416
487
  export function parsePlan(content, idPrefix) {
417
488
  const sections = parseSections(content);
418
489
  const allSections = flattenSections(sections);
@@ -505,7 +576,16 @@ export function parsePlan(content, idPrefix) {
505
576
  // ---------------------------------------------------------------------------
506
577
  // tasks.md parser
507
578
  // ---------------------------------------------------------------------------
508
- /** Parse a Spec-Kit tasks file into SysProM change nodes with task plans. */
579
+ /**
580
+ * Parse a Spec-Kit tasks file into SysProM change nodes with task plans.
581
+ * @param content - Markdown file content.
582
+ * @param idPrefix - ID prefix for generated nodes.
583
+ * @returns The result.
584
+ * @example
585
+ * ```ts
586
+ * const result = parseTasks(content, "FEAT");
587
+ * ```
588
+ */
509
589
  export function parseTasks(content, idPrefix) {
510
590
  const sections = parseSections(content);
511
591
  const allSections = flattenSections(sections);
@@ -611,7 +691,16 @@ export function parseTasks(content, idPrefix) {
611
691
  // ---------------------------------------------------------------------------
612
692
  // checklist.md parser
613
693
  // ---------------------------------------------------------------------------
614
- /** Parse a Spec-Kit checklist file into SysProM gate nodes with task plans. */
694
+ /**
695
+ * Parse a Spec-Kit checklist file into SysProM gate nodes with task plans.
696
+ * @param content - Markdown file content.
697
+ * @param idPrefix - ID prefix for generated nodes.
698
+ * @returns The result.
699
+ * @example
700
+ * ```ts
701
+ * const result = parseChecklist(content, "FEAT");
702
+ * ```
703
+ */
615
704
  export function parseChecklist(content, idPrefix) {
616
705
  const sections = parseSections(content);
617
706
  const allSections = flattenSections(sections);
@@ -660,7 +749,17 @@ export function parseChecklist(content, idPrefix) {
660
749
  // ---------------------------------------------------------------------------
661
750
  // Full feature directory parser
662
751
  // ---------------------------------------------------------------------------
663
- /** Parse an entire Spec-Kit feature directory into a SysProM document, combining constitution, spec, plan, tasks, and checklist. */
752
+ /**
753
+ * Parse an entire Spec-Kit feature directory into a SysProM document, combining constitution, spec, plan, tasks, and checklist.
754
+ * @param featureDir - Path to Spec-Kit feature directory.
755
+ * @param idPrefix - ID prefix for generated nodes.
756
+ * @param constitutionPath - Path to constitution.md, or undefined.
757
+ * @returns The parsed SysProM document.
758
+ * @example
759
+ * ```ts
760
+ * const doc = parseSpecKitFeature("./features/auth", "AUTH");
761
+ * ```
762
+ */
664
763
  export function parseSpecKitFeature(featureDir, idPrefix, constitutionPath) {
665
764
  const nodes = [];
666
765
  const relationships = [];
@@ -74,6 +74,13 @@ export interface TaskCount {
74
74
  * - {prefix}-CHK governed_by {prefix}-PROT-IMPL
75
75
  *
76
76
  * Tasks are not pre-scaffolded; use addTask to add them.
77
+ * @param prefix - Plan prefix.
78
+ * @param name - Name for the new item.
79
+ * @returns The result.
80
+ * @example
81
+ * ```ts
82
+ * const doc = initDocument("PLAN", "My Plan");
83
+ * ```
77
84
  */
78
85
  export declare function initDocument(prefix: string, name: string): SysProMDocument;
79
86
  /**
@@ -88,6 +95,15 @@ export declare function initDocument(prefix: string, name: string): SysProMDocum
88
95
  *
89
96
  * Wires must_follow to previous sibling change node at the same level.
90
97
  * Default name: "Task N".
98
+ * @param doc - The SysProM document.
99
+ * @param prefix - Plan prefix.
100
+ * @param name - Name for the new item.
101
+ * @param parentId - Parent task ID.
102
+ * @returns The result.
103
+ * @example
104
+ * ```ts
105
+ * const updated = addTask(doc, "PLAN", "Implement auth");
106
+ * ```
91
107
  */
92
108
  export declare function addTask(doc: SysProMDocument, prefix: string, name?: string, parentId?: string): SysProMDocument;
93
109
  /**
@@ -97,6 +113,12 @@ export declare function addTask(doc: SysProMDocument, prefix: string, name?: str
97
113
  * - All items in node.plan must have done === true AND at least one item must exist
98
114
  * If subsystem has change children:
99
115
  * - All children must be recursively done AND own plan items (if any) must be done
116
+ * @param node - The node to check.
117
+ * @returns The result.
118
+ * @example
119
+ * ```ts
120
+ * isTaskDone(changeNode); // => true if all plan items done
121
+ * ```
100
122
  */
101
123
  export declare function isTaskDone(node: Node): boolean;
102
124
  /**
@@ -104,16 +126,36 @@ export declare function isTaskDone(node: Node): boolean;
104
126
  *
105
127
  * Sums plan[] items from this node and recursively from all change nodes in
106
128
  * subsystem (and their subsystems).
129
+ * @param node - The node to check.
130
+ * @returns The result.
131
+ * @example
132
+ * ```ts
133
+ * const { total, done } = countTasks(changeNode);
134
+ * ```
107
135
  */
108
136
  export declare function countTasks(node: Node): TaskCount;
109
137
  /**
110
138
  * Inspect a document and return workflow completeness for a given prefix.
111
139
  * Never throws — missing nodes are reported as "not defined".
140
+ * @param doc - The SysProM document.
141
+ * @param prefix - Plan prefix.
142
+ * @returns The result.
143
+ * @example
144
+ * ```ts
145
+ * const status = planStatus(doc, "PLAN");
146
+ * ```
112
147
  */
113
148
  export declare function planStatus(doc: SysProMDocument, prefix: string): PlanStatus;
114
149
  /**
115
150
  * Return per-task completion data.
116
151
  * Tasks (change nodes) are discovered from PROT-IMPL.subsystem, sorted topologically.
152
+ * @param doc - The SysProM document.
153
+ * @param prefix - Plan prefix.
154
+ * @returns The result.
155
+ * @example
156
+ * ```ts
157
+ * const phases = planProgress(doc, "PLAN");
158
+ * ```
117
159
  */
118
160
  export declare function planProgress(doc: SysProMDocument, prefix: string): PhaseProgress[];
119
161
  /**
@@ -126,5 +168,13 @@ export declare function planProgress(doc: SysProMDocument, prefix: string): Phas
126
168
  *
127
169
  * Additionally for phase N > 1:
128
170
  * - All tasks in phase N-1 must be done
171
+ * @param doc - The SysProM document.
172
+ * @param prefix - Plan prefix.
173
+ * @param phase - Phase number (1-indexed).
174
+ * @returns Gate check result with readiness flag and issues.
175
+ * @example
176
+ * ```ts
177
+ * const result = checkGate(doc, "PLAN", 2);
178
+ * ```
129
179
  */
130
180
  export declare function checkGate(doc: SysProMDocument, prefix: string, phase: number): GateResult;
@@ -4,12 +4,26 @@ import { textToString } from "../text.js";
4
4
  // ============================================================================
5
5
  /**
6
6
  * Find a single node by ID, or null if not found.
7
+ * @param doc - The SysProM document.
8
+ * @param id - Node ID to find.
9
+ * @returns The result.
10
+ * @example
11
+ * ```ts
12
+ * const node = findNode(doc, "PLAN-SPEC");
13
+ * ```
7
14
  */
8
15
  function findNode(doc, id) {
9
16
  return doc.nodes.find((n) => n.id === id) ?? null;
10
17
  }
11
18
  /**
12
19
  * Find a single node by ID in a subsystem, or null if not found.
20
+ * @param subsystem - The subsystem document.
21
+ * @param id - Node ID to find.
22
+ * @returns The result.
23
+ * @example
24
+ * ```ts
25
+ * const node = findNodeInSubsystem(subsystem, "CH1");
26
+ * ```
13
27
  */
14
28
  function findNodeInSubsystem(subsystem, id) {
15
29
  if (!subsystem)
@@ -18,12 +32,26 @@ function findNodeInSubsystem(subsystem, id) {
18
32
  }
19
33
  /**
20
34
  * Find all nodes of a specific type.
35
+ * @param doc - The SysProM document.
36
+ * @param type - Node type to filter by.
37
+ * @returns The result.
38
+ * @example
39
+ * ```ts
40
+ * const changes = findNodesByType(doc, "change");
41
+ * ```
21
42
  */
22
43
  function findNodesByType(doc, type) {
23
44
  return doc.nodes.filter((n) => n.type === type);
24
45
  }
25
46
  /**
26
47
  * Find all nodes of a specific type in a subsystem.
48
+ * @param subsystem - The subsystem document.
49
+ * @param type - Node type to filter by.
50
+ * @returns The result.
51
+ * @example
52
+ * ```ts
53
+ * const gates = findNodesByTypeInSubsystem(subsystem, "gate");
54
+ * ```
27
55
  */
28
56
  function findNodesByTypeInSubsystem(subsystem, type) {
29
57
  if (!subsystem)
@@ -32,6 +60,14 @@ function findNodesByTypeInSubsystem(subsystem, type) {
32
60
  }
33
61
  /**
34
62
  * Find relationships from a source node to nodes of a target type (within a subsystem).
63
+ * @param subsystem - The subsystem document.
64
+ * @param fromId - Source node ID.
65
+ * @param relationType - Relationship type filter.
66
+ * @returns The result.
67
+ * @example
68
+ * ```ts
69
+ * const rels = findRelationshipsFrom(subsystem, "CH1");
70
+ * ```
35
71
  */
36
72
  function findRelationshipsFrom(subsystem, fromId, relationType) {
37
73
  if (!subsystem)
@@ -46,6 +82,14 @@ function findRelationshipsFrom(subsystem, fromId, relationType) {
46
82
  }
47
83
  /**
48
84
  * Find relationships to a target node (within a subsystem).
85
+ * @param subsystem - The subsystem document.
86
+ * @param toId - Target node ID.
87
+ * @param relationType - Relationship type filter.
88
+ * @returns The result.
89
+ * @example
90
+ * ```ts
91
+ * const rels = findRelationshipsTo(subsystem, "CH2");
92
+ * ```
49
93
  */
50
94
  function findRelationshipsTo(subsystem, toId, relationType) {
51
95
  if (!subsystem)
@@ -61,6 +105,12 @@ function findRelationshipsTo(subsystem, toId, relationType) {
61
105
  /**
62
106
  * Detect if a text contains non-placeholder acceptance criteria.
63
107
  * Looks for GIVEN/WHEN/THEN patterns (case-insensitive).
108
+ * @param description - Task description text.
109
+ * @returns The result.
110
+ * @example
111
+ * ```ts
112
+ * hasAcceptanceCriteria("Given X When Y Then Z"); // => true
113
+ * ```
64
114
  */
65
115
  function hasAcceptanceCriteria(description) {
66
116
  if (!description)
@@ -70,6 +120,13 @@ function hasAcceptanceCriteria(description) {
70
120
  }
71
121
  /**
72
122
  * Sort change nodes topologically using must_follow relationships.
123
+ * @param subsystem - The subsystem document.
124
+ * @param changeNodes - Array of change nodes.
125
+ * @returns The result.
126
+ * @example
127
+ * ```ts
128
+ * const sorted = sortChangesByOrder(subsystem, changeNodes);
129
+ * ```
73
130
  */
74
131
  function sortChangesByOrder(subsystem, changeNodes) {
75
132
  const subsystemToUse = subsystem ?? { nodes: [], relationships: [] };
@@ -122,6 +179,13 @@ function sortChangesByOrder(subsystem, changeNodes) {
122
179
  * - {prefix}-CHK governed_by {prefix}-PROT-IMPL
123
180
  *
124
181
  * Tasks are not pre-scaffolded; use addTask to add them.
182
+ * @param prefix - Plan prefix.
183
+ * @param name - Name for the new item.
184
+ * @returns The result.
185
+ * @example
186
+ * ```ts
187
+ * const doc = initDocument("PLAN", "My Plan");
188
+ * ```
125
189
  */
126
190
  export function initDocument(prefix, name) {
127
191
  const nodes = [
@@ -189,6 +253,15 @@ export function initDocument(prefix, name) {
189
253
  *
190
254
  * Wires must_follow to previous sibling change node at the same level.
191
255
  * Default name: "Task N".
256
+ * @param doc - The SysProM document.
257
+ * @param prefix - Plan prefix.
258
+ * @param name - Name for the new item.
259
+ * @param parentId - Parent task ID.
260
+ * @returns The result.
261
+ * @example
262
+ * ```ts
263
+ * const updated = addTask(doc, "PLAN", "Implement auth");
264
+ * ```
192
265
  */
193
266
  export function addTask(doc, prefix, name, parentId) {
194
267
  const protImpl = findNode(doc, `${prefix}-PROT-IMPL`);
@@ -247,6 +320,16 @@ export function addTask(doc, prefix, name, parentId) {
247
320
  }
248
321
  /**
249
322
  * Helper function to recursively add a task to a parent change node's subsystem.
323
+ * @param doc - The SysProM document.
324
+ * @param protImpl - Implementation protocol node.
325
+ * @param prefix - Plan prefix.
326
+ * @param parentId - Parent task ID.
327
+ * @param name - Name for the new item.
328
+ * @returns The result.
329
+ * @example
330
+ * ```ts
331
+ * const updated = addTaskToParent(doc, protImpl, "PLAN", "CH1");
332
+ * ```
250
333
  */
251
334
  function addTaskToParent(doc, protImpl, prefix, parentId, name) {
252
335
  // Find the parent change node in the subsystem tree
@@ -376,6 +459,12 @@ function addTaskToParent(doc, protImpl, prefix, parentId, name) {
376
459
  * - All items in node.plan must have done === true AND at least one item must exist
377
460
  * If subsystem has change children:
378
461
  * - All children must be recursively done AND own plan items (if any) must be done
462
+ * @param node - The node to check.
463
+ * @returns The result.
464
+ * @example
465
+ * ```ts
466
+ * isTaskDone(changeNode); // => true if all plan items done
467
+ * ```
379
468
  */
380
469
  export function isTaskDone(node) {
381
470
  // If the node has a subsystem with change children, check those recursively
@@ -404,6 +493,12 @@ export function isTaskDone(node) {
404
493
  *
405
494
  * Sums plan[] items from this node and recursively from all change nodes in
406
495
  * subsystem (and their subsystems).
496
+ * @param node - The node to check.
497
+ * @returns The result.
498
+ * @example
499
+ * ```ts
500
+ * const { total, done } = countTasks(changeNode);
501
+ * ```
407
502
  */
408
503
  export function countTasks(node) {
409
504
  let total = 0;
@@ -429,6 +524,13 @@ export function countTasks(node) {
429
524
  /**
430
525
  * Inspect a document and return workflow completeness for a given prefix.
431
526
  * Never throws — missing nodes are reported as "not defined".
527
+ * @param doc - The SysProM document.
528
+ * @param prefix - Plan prefix.
529
+ * @returns The result.
530
+ * @example
531
+ * ```ts
532
+ * const status = planStatus(doc, "PLAN");
533
+ * ```
432
534
  */
433
535
  export function planStatus(doc, prefix) {
434
536
  const constitution = findNode(doc, `${prefix}-CONST`);
@@ -527,6 +629,13 @@ export function planStatus(doc, prefix) {
527
629
  /**
528
630
  * Return per-task completion data.
529
631
  * Tasks (change nodes) are discovered from PROT-IMPL.subsystem, sorted topologically.
632
+ * @param doc - The SysProM document.
633
+ * @param prefix - Plan prefix.
634
+ * @returns The result.
635
+ * @example
636
+ * ```ts
637
+ * const phases = planProgress(doc, "PLAN");
638
+ * ```
530
639
  */
531
640
  export function planProgress(doc, prefix) {
532
641
  const protImpl = findNode(doc, `${prefix}-PROT-IMPL`);
@@ -568,6 +677,14 @@ export function planProgress(doc, prefix) {
568
677
  *
569
678
  * Additionally for phase N > 1:
570
679
  * - All tasks in phase N-1 must be done
680
+ * @param doc - The SysProM document.
681
+ * @param prefix - Plan prefix.
682
+ * @param phase - Phase number (1-indexed).
683
+ * @returns Gate check result with readiness flag and issues.
684
+ * @example
685
+ * ```ts
686
+ * const result = checkGate(doc, "PLAN", 2);
687
+ * ```
571
688
  */
572
689
  export function checkGate(doc, prefix, phase) {
573
690
  if (phase < 1) {
@@ -28,18 +28,47 @@ export interface SpecKitFeature {
28
28
  /**
29
29
  * Detect Spec-Kit project structure from a directory.
30
30
  * Looks for .specify/ and specs/ subdirectories.
31
+ * @param dir - Directory to scan.
32
+ * @returns Detected project structure.
33
+ * @example
34
+ * ```ts
35
+ * const project = detectSpecKitProject("./my-project");
36
+ * ```
37
+ * @example
38
+ * ```ts
39
+ * const project = detectSpecKitProject("./my-project");
40
+ * ```
31
41
  */
32
42
  export declare function detectSpecKitProject(dir: string): SpecKitProject;
33
43
  /**
34
44
  * List all features in the specs/ directory, sorted by number.
45
+ * @param project - The detected project.
46
+ * @returns Sorted list of features.
47
+ * @example
48
+ * ```ts
49
+ * const features = listFeatures(project);
50
+ * ```
35
51
  */
36
52
  export declare function listFeatures(project: SpecKitProject): SpecKitFeature[];
37
53
  /**
38
54
  * Get a specific feature by number or name.
39
55
  * Matches "001", "001-feature-name", or "feature-name".
56
+ * @param project - The detected project.
57
+ * @param idOrName - Feature ID, number, or name.
58
+ * @returns The matching feature, or null.
59
+ * @example
60
+ * ```ts
61
+ * const feature = getFeature(project, "001-auth");
62
+ * ```
40
63
  */
41
64
  export declare function getFeature(project: SpecKitProject, idOrName: string): SpecKitFeature | null;
42
65
  /**
43
66
  * Resolve the constitution.md file, checking .specify/memory/ first, then root.
67
+ * @param project - The detected project.
68
+ * @returns Path to constitution.md, or null.
69
+ * @example
70
+ * ```ts
71
+ * const path = resolveConstitution(project);
72
+ * ```
44
73
  */
45
74
  export declare function resolveConstitution(project: SpecKitProject): string | null;
@@ -3,6 +3,16 @@ import { join } from "node:path";
3
3
  /**
4
4
  * Detect Spec-Kit project structure from a directory.
5
5
  * Looks for .specify/ and specs/ subdirectories.
6
+ * @param dir - Directory to scan.
7
+ * @returns Detected project structure.
8
+ * @example
9
+ * ```ts
10
+ * const project = detectSpecKitProject("./my-project");
11
+ * ```
12
+ * @example
13
+ * ```ts
14
+ * const project = detectSpecKitProject("./my-project");
15
+ * ```
6
16
  */
7
17
  export function detectSpecKitProject(dir) {
8
18
  const specifyDir = checkDir(join(dir, ".specify"));
@@ -30,6 +40,12 @@ export function detectSpecKitProject(dir) {
30
40
  }
31
41
  /**
32
42
  * List all features in the specs/ directory, sorted by number.
43
+ * @param project - The detected project.
44
+ * @returns Sorted list of features.
45
+ * @example
46
+ * ```ts
47
+ * const features = listFeatures(project);
48
+ * ```
33
49
  */
34
50
  export function listFeatures(project) {
35
51
  if (!project.specsDir) {
@@ -63,6 +79,13 @@ export function listFeatures(project) {
63
79
  /**
64
80
  * Get a specific feature by number or name.
65
81
  * Matches "001", "001-feature-name", or "feature-name".
82
+ * @param project - The detected project.
83
+ * @param idOrName - Feature ID, number, or name.
84
+ * @returns The matching feature, or null.
85
+ * @example
86
+ * ```ts
87
+ * const feature = getFeature(project, "001-auth");
88
+ * ```
66
89
  */
67
90
  export function getFeature(project, idOrName) {
68
91
  const features = listFeatures(project);
@@ -83,12 +106,24 @@ export function getFeature(project, idOrName) {
83
106
  }
84
107
  /**
85
108
  * Resolve the constitution.md file, checking .specify/memory/ first, then root.
109
+ * @param project - The detected project.
110
+ * @returns Path to constitution.md, or null.
111
+ * @example
112
+ * ```ts
113
+ * const path = resolveConstitution(project);
114
+ * ```
86
115
  */
87
116
  export function resolveConstitution(project) {
88
117
  return project.constitutionPath;
89
118
  }
90
119
  /**
91
120
  * Check if a directory exists, return path or null.
121
+ * @param path - Path to check.
122
+ * @returns The path if it exists as a directory, or null.
123
+ * @example
124
+ * ```ts
125
+ * const dir = checkDir("/path/to/dir");
126
+ * ```
92
127
  */
93
128
  function checkDir(path) {
94
129
  try {
@@ -101,6 +136,13 @@ function checkDir(path) {
101
136
  }
102
137
  /**
103
138
  * Parse a feature directory and extract metadata and file paths.
139
+ * @param dir - Directory to search.
140
+ * @param dirName - Subdirectory name pattern.
141
+ * @returns Array of matching directory paths.
142
+ * @example
143
+ * ```ts
144
+ * const feature = parseFeatureDirectory("./specs", "001-auth");
145
+ * ```
104
146
  */
105
147
  function parseFeatureDirectory(dir, dirName) {
106
148
  // Parse the directory name format: "NNN-feature-name"
@@ -128,6 +170,13 @@ function parseFeatureDirectory(dir, dirName) {
128
170
  }
129
171
  /**
130
172
  * Check if a file exists in a directory, return path or null.
173
+ * @param dir - Directory to search.
174
+ * @param filename - File name to find.
175
+ * @returns Array of matching file paths.
176
+ * @example
177
+ * ```ts
178
+ * const specPath = checkFile("./feature", "spec.md");
179
+ * ```
131
180
  */
132
181
  function checkFile(dir, filename) {
133
182
  const path = join(dir, filename);
@@ -1,23 +1,44 @@
1
- /** Normalise a text field (string | string[]) to a single string, joining with newlines.
1
+ /**
2
+ * Normalise a text field (string | string[]) to a single string, joining with newlines.
2
3
  * @param value - The text field to normalise.
3
4
  * @returns A single string.
5
+ * @example
6
+ * ```ts
7
+ * textToString(["line 1", "line 2"]) // => "line 1\nline 2"
8
+ * textToString("hello") // => "hello"
9
+ * ```
4
10
  */
5
11
  export declare function textToString(value: string | string[]): string;
6
- /** Normalise a text field to an array of lines.
12
+ /**
13
+ * Normalise a text field to an array of lines.
7
14
  * @param value - The text field to normalise.
8
15
  * @returns An array of lines.
16
+ * @example
17
+ * ```ts
18
+ * textToLines("hello") // => ["hello"]
19
+ * textToLines(["a", "b"]) // => ["a", "b"]
20
+ * ```
9
21
  */
10
22
  export declare function textToLines(value: string | string[]): string[];
11
- /** Format a text field for Markdown output — each line becomes a paragraph.
23
+ /**
24
+ * Format a text field for Markdown output — each line becomes a paragraph.
12
25
  * @param value - The text field to format.
13
26
  * @returns Markdown-formatted string.
27
+ * @example
28
+ * ```ts
29
+ * textToMarkdown(["para 1", "para 2"]) // => "para 1\npara 2"
30
+ * ```
14
31
  */
15
32
  export declare function textToMarkdown(value: string | string[]): string;
16
33
  /**
17
34
  * Parse a Markdown text block back into the canonical text representation.
18
35
  * Single-line content stays as a string; multiline becomes an array.
19
- *
20
36
  * @param block - The Markdown text block to parse.
21
37
  * @returns A string for single-line content, or an array of lines for multiline.
38
+ * @example
39
+ * ```ts
40
+ * markdownToText("single line") // => "single line"
41
+ * markdownToText("line 1\nline 2") // => ["line 1", "line 2"]
42
+ * ```
22
43
  */
23
44
  export declare function markdownToText(block: string): string | string[];