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.
- package/README.md +8 -0
- package/dist/src/canonical-json.d.ts +5 -1
- package/dist/src/canonical-json.js +5 -1
- package/dist/src/cli/define-command.d.ts +26 -0
- package/dist/src/cli/define-command.js +22 -0
- package/dist/src/cli/shared.d.ts +32 -2
- package/dist/src/cli/shared.js +29 -2
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/io.d.ts +9 -2
- package/dist/src/io.js +9 -2
- package/dist/src/json-to-md.d.ts +13 -3
- package/dist/src/json-to-md.js +13 -3
- package/dist/src/md-to-json.d.ts +12 -3
- package/dist/src/md-to-json.js +12 -3
- package/dist/src/operations/add-node.d.ts +1 -2
- package/dist/src/operations/add-node.js +1 -2
- package/dist/src/operations/add-plan-task.d.ts +1 -2
- package/dist/src/operations/add-plan-task.js +1 -2
- package/dist/src/operations/add-relationship.d.ts +1 -2
- package/dist/src/operations/add-relationship.js +1 -2
- package/dist/src/operations/define-operation.d.ts +16 -8
- package/dist/src/operations/define-operation.js +12 -2
- package/dist/src/operations/mark-task-done.d.ts +1 -2
- package/dist/src/operations/mark-task-done.js +1 -2
- package/dist/src/operations/mark-task-undone.d.ts +1 -2
- package/dist/src/operations/mark-task-undone.js +1 -2
- package/dist/src/operations/next-id.d.ts +1 -2
- package/dist/src/operations/next-id.js +1 -2
- package/dist/src/operations/remove-node.d.ts +1 -2
- package/dist/src/operations/remove-node.js +1 -2
- package/dist/src/operations/remove-relationship.d.ts +1 -2
- package/dist/src/operations/remove-relationship.js +1 -2
- package/dist/src/operations/rename.d.ts +1 -2
- package/dist/src/operations/rename.js +1 -2
- package/dist/src/operations/task-list.d.ts +1 -2
- package/dist/src/operations/task-list.js +1 -2
- package/dist/src/operations/update-node.d.ts +1 -2
- package/dist/src/operations/update-node.js +1 -2
- package/dist/src/operations/update-plan-task.d.ts +1 -2
- package/dist/src/operations/update-plan-task.js +1 -2
- package/dist/src/schema.d.ts +9 -1
- package/dist/src/schema.js +9 -1
- package/dist/src/speckit/generate.d.ts +60 -6
- package/dist/src/speckit/generate.js +114 -6
- package/dist/src/speckit/parse.d.ts +61 -6
- package/dist/src/speckit/parse.js +105 -6
- package/dist/src/speckit/plan.d.ts +50 -0
- package/dist/src/speckit/plan.js +117 -0
- package/dist/src/speckit/project.d.ts +29 -0
- package/dist/src/speckit/project.js +49 -0
- package/dist/src/text.d.ts +25 -4
- package/dist/src/text.js +25 -4
- 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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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;
|
package/dist/src/speckit/plan.js
CHANGED
|
@@ -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);
|
package/dist/src/text.d.ts
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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[];
|