sysprom 1.2.1 → 1.2.3
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 +27 -18
- package/dist/src/canonical-json.d.ts +5 -0
- package/dist/src/canonical-json.js +5 -0
- package/dist/src/cli/define-command.d.ts +11 -0
- package/dist/src/cli/define-command.js +11 -0
- package/dist/src/cli/shared.d.ts +15 -0
- package/dist/src/cli/shared.js +15 -0
- package/dist/src/io.d.ts +9 -0
- package/dist/src/io.js +9 -0
- package/dist/src/json-to-md.d.ts +13 -0
- package/dist/src/json-to-md.js +13 -0
- package/dist/src/md-to-json.d.ts +12 -0
- package/dist/src/md-to-json.js +12 -0
- package/dist/src/operations/define-operation.d.ts +11 -0
- package/dist/src/operations/define-operation.js +11 -0
- package/dist/src/schema.d.ts +5 -0
- package/dist/src/schema.js +5 -0
- package/dist/src/speckit/generate.d.ts +24 -0
- package/dist/src/speckit/generate.js +56 -0
- package/dist/src/speckit/parse.d.ts +24 -0
- package/dist/src/speckit/parse.js +52 -0
- package/dist/src/speckit/plan.d.ts +42 -14
- package/dist/src/speckit/plan.js +81 -17
- package/dist/src/speckit/project.d.ts +20 -0
- package/dist/src/speckit/project.js +32 -0
- package/dist/src/text.d.ts +19 -0
- package/dist/src/text.js +19 -0
- package/package.json +1 -1
|
@@ -11,6 +11,10 @@ export interface ParseResult {
|
|
|
11
11
|
* @param content - Markdown file content.
|
|
12
12
|
* @param idPrefix - ID prefix for generated nodes.
|
|
13
13
|
* @returns The result.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const result = parseConstitution(content, "FEAT");
|
|
17
|
+
* ```
|
|
14
18
|
*/
|
|
15
19
|
export declare function parseConstitution(content: string, idPrefix: string): ParseResult;
|
|
16
20
|
/**
|
|
@@ -18,6 +22,10 @@ export declare function parseConstitution(content: string, idPrefix: string): Pa
|
|
|
18
22
|
* @param content - Markdown file content.
|
|
19
23
|
* @param idPrefix - ID prefix for generated nodes.
|
|
20
24
|
* @returns The result.
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const result = parseSpec(content, "FEAT");
|
|
28
|
+
* ```
|
|
21
29
|
*/
|
|
22
30
|
export declare function parseSpec(content: string, idPrefix: string): ParseResult;
|
|
23
31
|
/**
|
|
@@ -25,6 +33,10 @@ export declare function parseSpec(content: string, idPrefix: string): ParseResul
|
|
|
25
33
|
* @param content - Markdown file content.
|
|
26
34
|
* @param idPrefix - ID prefix for generated nodes.
|
|
27
35
|
* @returns The result.
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const result = parsePlan(content, "FEAT");
|
|
39
|
+
* ```
|
|
28
40
|
*/
|
|
29
41
|
export declare function parsePlan(content: string, idPrefix: string): ParseResult;
|
|
30
42
|
/**
|
|
@@ -32,6 +44,10 @@ export declare function parsePlan(content: string, idPrefix: string): ParseResul
|
|
|
32
44
|
* @param content - Markdown file content.
|
|
33
45
|
* @param idPrefix - ID prefix for generated nodes.
|
|
34
46
|
* @returns The result.
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const result = parseTasks(content, "FEAT");
|
|
50
|
+
* ```
|
|
35
51
|
*/
|
|
36
52
|
export declare function parseTasks(content: string, idPrefix: string): ParseResult;
|
|
37
53
|
/**
|
|
@@ -39,6 +55,10 @@ export declare function parseTasks(content: string, idPrefix: string): ParseResu
|
|
|
39
55
|
* @param content - Markdown file content.
|
|
40
56
|
* @param idPrefix - ID prefix for generated nodes.
|
|
41
57
|
* @returns The result.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const result = parseChecklist(content, "FEAT");
|
|
61
|
+
* ```
|
|
42
62
|
*/
|
|
43
63
|
export declare function parseChecklist(content: string, idPrefix: string): ParseResult;
|
|
44
64
|
/**
|
|
@@ -47,5 +67,9 @@ export declare function parseChecklist(content: string, idPrefix: string): Parse
|
|
|
47
67
|
* @param idPrefix - ID prefix for generated nodes.
|
|
48
68
|
* @param constitutionPath - Path to constitution.md, or undefined.
|
|
49
69
|
* @returns The parsed SysProM document.
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const doc = parseSpecKitFeature("./features/auth", "AUTH");
|
|
73
|
+
* ```
|
|
50
74
|
*/
|
|
51
75
|
export declare function parseSpecKitFeature(featureDir: string, idPrefix: string, constitutionPath?: string): SysProMDocument;
|
|
@@ -7,6 +7,10 @@ import { join, basename } from "node:path";
|
|
|
7
7
|
* Parse markdown content into a hierarchical section tree by heading level.
|
|
8
8
|
* @param body - Markdown body text.
|
|
9
9
|
* @returns The result.
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const sections = parseSections(markdownBody);
|
|
13
|
+
* ```
|
|
10
14
|
*/
|
|
11
15
|
function parseSections(body) {
|
|
12
16
|
const lines = body.split("\n");
|
|
@@ -53,6 +57,10 @@ function parseSections(body) {
|
|
|
53
57
|
* Extract bold key-value pairs from markdown like "**Key**: value" or "**Key**: value text".
|
|
54
58
|
* @param content - Markdown file content.
|
|
55
59
|
* @returns The result.
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const meta = parseFrontMatterish(content);
|
|
63
|
+
* ```
|
|
56
64
|
*/
|
|
57
65
|
function parseFrontMatterish(content) {
|
|
58
66
|
const result = {};
|
|
@@ -70,6 +78,10 @@ function parseFrontMatterish(content) {
|
|
|
70
78
|
* Parse checkbox lines like "- [x] ID text" or "- [ ] ID text".
|
|
71
79
|
* @param body - Markdown body text.
|
|
72
80
|
* @returns The result.
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const items = parseCheckboxes(body);
|
|
84
|
+
* ```
|
|
73
85
|
*/
|
|
74
86
|
function parseCheckboxes(body) {
|
|
75
87
|
const items = [];
|
|
@@ -92,6 +104,10 @@ function parseCheckboxes(body) {
|
|
|
92
104
|
* Flatten all sections in the tree into a single array for easier searching.
|
|
93
105
|
* @param sections - Parsed section tree.
|
|
94
106
|
* @returns The result.
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* const flat = flattenSections(sections);
|
|
110
|
+
* ```
|
|
95
111
|
*/
|
|
96
112
|
function flattenSections(sections) {
|
|
97
113
|
const result = [];
|
|
@@ -109,6 +125,10 @@ function flattenSections(sections) {
|
|
|
109
125
|
* @param sections - Parsed section tree.
|
|
110
126
|
* @param predicate - Filter function.
|
|
111
127
|
* @returns The result.
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const s = findSection(sections, (h) => h.startsWith("Phase"));
|
|
131
|
+
* ```
|
|
112
132
|
*/
|
|
113
133
|
function findSection(sections, predicate) {
|
|
114
134
|
return flattenSections(sections).find((s) => predicate(s.heading));
|
|
@@ -117,6 +137,10 @@ function findSection(sections, predicate) {
|
|
|
117
137
|
* Convert status-like strings to NodeStatus. Recognizes common spec-kit patterns.
|
|
118
138
|
* @param value - Raw status string.
|
|
119
139
|
* @returns The result.
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* mapStatusValue("Draft"); // => "proposed"
|
|
143
|
+
* ```
|
|
120
144
|
*/
|
|
121
145
|
function mapStatusValue(value) {
|
|
122
146
|
const lower = value.toLowerCase().trim();
|
|
@@ -146,6 +170,10 @@ function mapStatusValue(value) {
|
|
|
146
170
|
* @param body - Markdown body text.
|
|
147
171
|
* @param key - Front-matter key to extract.
|
|
148
172
|
* @returns The result.
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* extractValue(body, "Created"); // => "2025-01-01"
|
|
176
|
+
* ```
|
|
149
177
|
*/
|
|
150
178
|
function extractValue(body, key) {
|
|
151
179
|
const pattern = new RegExp(`^\\*\\*${key}\\*\\*:\\s*(.+)$`, "m");
|
|
@@ -160,6 +188,10 @@ function extractValue(body, key) {
|
|
|
160
188
|
* @param content - Markdown file content.
|
|
161
189
|
* @param idPrefix - ID prefix for generated nodes.
|
|
162
190
|
* @returns The result.
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* const result = parseConstitution(content, "FEAT");
|
|
194
|
+
* ```
|
|
163
195
|
*/
|
|
164
196
|
export function parseConstitution(content, idPrefix) {
|
|
165
197
|
const sections = parseSections(content);
|
|
@@ -254,6 +286,10 @@ export function parseConstitution(content, idPrefix) {
|
|
|
254
286
|
* @param content - Markdown file content.
|
|
255
287
|
* @param idPrefix - ID prefix for generated nodes.
|
|
256
288
|
* @returns The result.
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* const result = parseSpec(content, "FEAT");
|
|
292
|
+
* ```
|
|
257
293
|
*/
|
|
258
294
|
export function parseSpec(content, idPrefix) {
|
|
259
295
|
const sections = parseSections(content);
|
|
@@ -443,6 +479,10 @@ export function parseSpec(content, idPrefix) {
|
|
|
443
479
|
* @param content - Markdown file content.
|
|
444
480
|
* @param idPrefix - ID prefix for generated nodes.
|
|
445
481
|
* @returns The result.
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* const result = parsePlan(content, "FEAT");
|
|
485
|
+
* ```
|
|
446
486
|
*/
|
|
447
487
|
export function parsePlan(content, idPrefix) {
|
|
448
488
|
const sections = parseSections(content);
|
|
@@ -541,6 +581,10 @@ export function parsePlan(content, idPrefix) {
|
|
|
541
581
|
* @param content - Markdown file content.
|
|
542
582
|
* @param idPrefix - ID prefix for generated nodes.
|
|
543
583
|
* @returns The result.
|
|
584
|
+
* @example
|
|
585
|
+
* ```ts
|
|
586
|
+
* const result = parseTasks(content, "FEAT");
|
|
587
|
+
* ```
|
|
544
588
|
*/
|
|
545
589
|
export function parseTasks(content, idPrefix) {
|
|
546
590
|
const sections = parseSections(content);
|
|
@@ -652,6 +696,10 @@ export function parseTasks(content, idPrefix) {
|
|
|
652
696
|
* @param content - Markdown file content.
|
|
653
697
|
* @param idPrefix - ID prefix for generated nodes.
|
|
654
698
|
* @returns The result.
|
|
699
|
+
* @example
|
|
700
|
+
* ```ts
|
|
701
|
+
* const result = parseChecklist(content, "FEAT");
|
|
702
|
+
* ```
|
|
655
703
|
*/
|
|
656
704
|
export function parseChecklist(content, idPrefix) {
|
|
657
705
|
const sections = parseSections(content);
|
|
@@ -707,6 +755,10 @@ export function parseChecklist(content, idPrefix) {
|
|
|
707
755
|
* @param idPrefix - ID prefix for generated nodes.
|
|
708
756
|
* @param constitutionPath - Path to constitution.md, or undefined.
|
|
709
757
|
* @returns The parsed SysProM document.
|
|
758
|
+
* @example
|
|
759
|
+
* ```ts
|
|
760
|
+
* const doc = parseSpecKitFeature("./features/auth", "AUTH");
|
|
761
|
+
* ```
|
|
710
762
|
*/
|
|
711
763
|
export function parseSpecKitFeature(featureDir, idPrefix, constitutionPath) {
|
|
712
764
|
const nodes = [];
|
|
@@ -77,6 +77,10 @@ export interface TaskCount {
|
|
|
77
77
|
* @param prefix - Plan prefix.
|
|
78
78
|
* @param name - Name for the new item.
|
|
79
79
|
* @returns The result.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const doc = initDocument("PLAN", "My Plan");
|
|
83
|
+
* ```
|
|
80
84
|
*/
|
|
81
85
|
export declare function initDocument(prefix: string, name: string): SysProMDocument;
|
|
82
86
|
/**
|
|
@@ -94,19 +98,27 @@ export declare function initDocument(prefix: string, name: string): SysProMDocum
|
|
|
94
98
|
* @param doc - The SysProM document.
|
|
95
99
|
* @param prefix - Plan prefix.
|
|
96
100
|
* @param name - Name for the new item.
|
|
97
|
-
* @param parentId -
|
|
98
|
-
* @returns The
|
|
101
|
+
* @param parentId - Optional parent change node ID for nesting.
|
|
102
|
+
* @returns The updated document with the new task added.
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const updated = addTask(doc, "PLAN", "Implement auth");
|
|
106
|
+
* ```
|
|
99
107
|
*/
|
|
100
108
|
export declare function addTask(doc: SysProMDocument, prefix: string, name?: string, parentId?: string): SysProMDocument;
|
|
101
109
|
/**
|
|
102
110
|
* Check if a change node's task is complete.
|
|
103
111
|
*
|
|
104
112
|
* If no subsystem or no change children in subsystem:
|
|
105
|
-
* - All items in node.plan must have done === true AND at least one item must exist
|
|
113
|
+
* - All items in node.plan must have done === true AND at least one item must exist.
|
|
106
114
|
* If subsystem has change children:
|
|
107
|
-
* - All children must be recursively done AND own plan items (if any) must be done
|
|
108
|
-
* @param node - The node to
|
|
109
|
-
* @returns
|
|
115
|
+
* - All children must be recursively done AND own plan items (if any) must be done.
|
|
116
|
+
* @param node - The change node to evaluate.
|
|
117
|
+
* @returns Whether all tasks in the node's plan are complete.
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* isTaskDone(changeNode); // => true if all plan items done
|
|
121
|
+
* ```
|
|
110
122
|
*/
|
|
111
123
|
export declare function isTaskDone(node: Node): boolean;
|
|
112
124
|
/**
|
|
@@ -116,37 +128,53 @@ export declare function isTaskDone(node: Node): boolean;
|
|
|
116
128
|
* subsystem (and their subsystems).
|
|
117
129
|
* @param node - The node to check.
|
|
118
130
|
* @returns The result.
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const { total, done } = countTasks(changeNode);
|
|
134
|
+
* ```
|
|
119
135
|
*/
|
|
120
136
|
export declare function countTasks(node: Node): TaskCount;
|
|
121
137
|
/**
|
|
122
138
|
* Inspect a document and return workflow completeness for a given prefix.
|
|
123
139
|
* Never throws — missing nodes are reported as "not defined".
|
|
124
140
|
* @param doc - The SysProM document.
|
|
125
|
-
* @param prefix -
|
|
126
|
-
* @returns
|
|
141
|
+
* @param prefix - ID prefix identifying the plan (e.g. "PLAN").
|
|
142
|
+
* @returns Comprehensive status of all plan components.
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const status = planStatus(doc, "PLAN");
|
|
146
|
+
* ```
|
|
127
147
|
*/
|
|
128
148
|
export declare function planStatus(doc: SysProMDocument, prefix: string): PlanStatus;
|
|
129
149
|
/**
|
|
130
150
|
* Return per-task completion data.
|
|
131
151
|
* Tasks (change nodes) are discovered from PROT-IMPL.subsystem, sorted topologically.
|
|
132
152
|
* @param doc - The SysProM document.
|
|
133
|
-
* @param prefix -
|
|
134
|
-
* @returns
|
|
153
|
+
* @param prefix - ID prefix identifying the plan (e.g. "PLAN").
|
|
154
|
+
* @returns Per-phase progress with task counts and percentages.
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const phases = planProgress(doc, "PLAN");
|
|
158
|
+
* ```
|
|
135
159
|
*/
|
|
136
160
|
export declare function planProgress(doc: SysProMDocument, prefix: string): PhaseProgress[];
|
|
137
161
|
/**
|
|
138
162
|
* Validate readiness to enter the given phase (1-indexed).
|
|
139
163
|
*
|
|
140
164
|
* Always checks:
|
|
141
|
-
* - Each capability ({prefix}-US-*) has a change node that implements it
|
|
142
|
-
* - Each capability has non-placeholder acceptance criteria
|
|
143
|
-
* - Each invariant ({prefix}-FR-*) has a change node that implements it
|
|
165
|
+
* - Each capability ({prefix}-US-*) has a change node that implements it.
|
|
166
|
+
* - Each capability has non-placeholder acceptance criteria.
|
|
167
|
+
* - Each invariant ({prefix}-FR-*) has a change node that implements it.
|
|
144
168
|
*
|
|
145
169
|
* Additionally for phase N > 1:
|
|
146
|
-
* - All tasks in phase N-1 must be done
|
|
170
|
+
* - All tasks in phase N-1 must be done.
|
|
147
171
|
* @param doc - The SysProM document.
|
|
148
172
|
* @param prefix - Plan prefix.
|
|
149
173
|
* @param phase - Phase number (1-indexed).
|
|
150
174
|
* @returns Gate check result with readiness flag and issues.
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const result = checkGate(doc, "PLAN", 2);
|
|
178
|
+
* ```
|
|
151
179
|
*/
|
|
152
180
|
export declare function checkGate(doc: SysProMDocument, prefix: string, phase: number): GateResult;
|
package/dist/src/speckit/plan.js
CHANGED
|
@@ -7,6 +7,10 @@ import { textToString } from "../text.js";
|
|
|
7
7
|
* @param doc - The SysProM document.
|
|
8
8
|
* @param id - Node ID to find.
|
|
9
9
|
* @returns The result.
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const node = findNode(doc, "PLAN-SPEC");
|
|
13
|
+
* ```
|
|
10
14
|
*/
|
|
11
15
|
function findNode(doc, id) {
|
|
12
16
|
return doc.nodes.find((n) => n.id === id) ?? null;
|
|
@@ -16,6 +20,10 @@ function findNode(doc, id) {
|
|
|
16
20
|
* @param subsystem - The subsystem document.
|
|
17
21
|
* @param id - Node ID to find.
|
|
18
22
|
* @returns The result.
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const node = findNodeInSubsystem(subsystem, "CH1");
|
|
26
|
+
* ```
|
|
19
27
|
*/
|
|
20
28
|
function findNodeInSubsystem(subsystem, id) {
|
|
21
29
|
if (!subsystem)
|
|
@@ -27,6 +35,10 @@ function findNodeInSubsystem(subsystem, id) {
|
|
|
27
35
|
* @param doc - The SysProM document.
|
|
28
36
|
* @param type - Node type to filter by.
|
|
29
37
|
* @returns The result.
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const changes = findNodesByType(doc, "change");
|
|
41
|
+
* ```
|
|
30
42
|
*/
|
|
31
43
|
function findNodesByType(doc, type) {
|
|
32
44
|
return doc.nodes.filter((n) => n.type === type);
|
|
@@ -36,6 +48,10 @@ function findNodesByType(doc, type) {
|
|
|
36
48
|
* @param subsystem - The subsystem document.
|
|
37
49
|
* @param type - Node type to filter by.
|
|
38
50
|
* @returns The result.
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const gates = findNodesByTypeInSubsystem(subsystem, "gate");
|
|
54
|
+
* ```
|
|
39
55
|
*/
|
|
40
56
|
function findNodesByTypeInSubsystem(subsystem, type) {
|
|
41
57
|
if (!subsystem)
|
|
@@ -48,6 +64,10 @@ function findNodesByTypeInSubsystem(subsystem, type) {
|
|
|
48
64
|
* @param fromId - Source node ID.
|
|
49
65
|
* @param relationType - Relationship type filter.
|
|
50
66
|
* @returns The result.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const rels = findRelationshipsFrom(subsystem, "CH1");
|
|
70
|
+
* ```
|
|
51
71
|
*/
|
|
52
72
|
function findRelationshipsFrom(subsystem, fromId, relationType) {
|
|
53
73
|
if (!subsystem)
|
|
@@ -66,6 +86,10 @@ function findRelationshipsFrom(subsystem, fromId, relationType) {
|
|
|
66
86
|
* @param toId - Target node ID.
|
|
67
87
|
* @param relationType - Relationship type filter.
|
|
68
88
|
* @returns The result.
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const rels = findRelationshipsTo(subsystem, "CH2");
|
|
92
|
+
* ```
|
|
69
93
|
*/
|
|
70
94
|
function findRelationshipsTo(subsystem, toId, relationType) {
|
|
71
95
|
if (!subsystem)
|
|
@@ -83,6 +107,10 @@ function findRelationshipsTo(subsystem, toId, relationType) {
|
|
|
83
107
|
* Looks for GIVEN/WHEN/THEN patterns (case-insensitive).
|
|
84
108
|
* @param description - Task description text.
|
|
85
109
|
* @returns The result.
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* hasAcceptanceCriteria("Given X When Y Then Z"); // => true
|
|
113
|
+
* ```
|
|
86
114
|
*/
|
|
87
115
|
function hasAcceptanceCriteria(description) {
|
|
88
116
|
if (!description)
|
|
@@ -95,6 +123,10 @@ function hasAcceptanceCriteria(description) {
|
|
|
95
123
|
* @param subsystem - The subsystem document.
|
|
96
124
|
* @param changeNodes - Array of change nodes.
|
|
97
125
|
* @returns The result.
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const sorted = sortChangesByOrder(subsystem, changeNodes);
|
|
129
|
+
* ```
|
|
98
130
|
*/
|
|
99
131
|
function sortChangesByOrder(subsystem, changeNodes) {
|
|
100
132
|
const subsystemToUse = subsystem ?? { nodes: [], relationships: [] };
|
|
@@ -150,6 +182,10 @@ function sortChangesByOrder(subsystem, changeNodes) {
|
|
|
150
182
|
* @param prefix - Plan prefix.
|
|
151
183
|
* @param name - Name for the new item.
|
|
152
184
|
* @returns The result.
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* const doc = initDocument("PLAN", "My Plan");
|
|
188
|
+
* ```
|
|
153
189
|
*/
|
|
154
190
|
export function initDocument(prefix, name) {
|
|
155
191
|
const nodes = [
|
|
@@ -220,8 +256,12 @@ export function initDocument(prefix, name) {
|
|
|
220
256
|
* @param doc - The SysProM document.
|
|
221
257
|
* @param prefix - Plan prefix.
|
|
222
258
|
* @param name - Name for the new item.
|
|
223
|
-
* @param parentId -
|
|
224
|
-
* @returns The
|
|
259
|
+
* @param parentId - Optional parent change node ID for nesting.
|
|
260
|
+
* @returns The updated document with the new task added.
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* const updated = addTask(doc, "PLAN", "Implement auth");
|
|
264
|
+
* ```
|
|
225
265
|
*/
|
|
226
266
|
export function addTask(doc, prefix, name, parentId) {
|
|
227
267
|
const protImpl = findNode(doc, `${prefix}-PROT-IMPL`);
|
|
@@ -283,9 +323,13 @@ export function addTask(doc, prefix, name, parentId) {
|
|
|
283
323
|
* @param doc - The SysProM document.
|
|
284
324
|
* @param protImpl - Implementation protocol node.
|
|
285
325
|
* @param prefix - Plan prefix.
|
|
286
|
-
* @param parentId -
|
|
287
|
-
* @param name -
|
|
288
|
-
* @returns The
|
|
326
|
+
* @param parentId - ID of the parent change node to nest under.
|
|
327
|
+
* @param name - Human-readable task name.
|
|
328
|
+
* @returns The updated document with the nested task added.
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* const updated = addTaskToParent(doc, protImpl, "PLAN", "CH1");
|
|
332
|
+
* ```
|
|
289
333
|
*/
|
|
290
334
|
function addTaskToParent(doc, protImpl, prefix, parentId, name) {
|
|
291
335
|
// Find the parent change node in the subsystem tree
|
|
@@ -412,11 +456,15 @@ function addTaskToParent(doc, protImpl, prefix, parentId, name) {
|
|
|
412
456
|
* Check if a change node's task is complete.
|
|
413
457
|
*
|
|
414
458
|
* If no subsystem or no change children in subsystem:
|
|
415
|
-
* - All items in node.plan must have done === true AND at least one item must exist
|
|
459
|
+
* - All items in node.plan must have done === true AND at least one item must exist.
|
|
416
460
|
* If subsystem has change children:
|
|
417
|
-
* - All children must be recursively done AND own plan items (if any) must be done
|
|
418
|
-
* @param node - The node to
|
|
419
|
-
* @returns
|
|
461
|
+
* - All children must be recursively done AND own plan items (if any) must be done.
|
|
462
|
+
* @param node - The change node to evaluate.
|
|
463
|
+
* @returns Whether all tasks in the node's plan are complete.
|
|
464
|
+
* @example
|
|
465
|
+
* ```ts
|
|
466
|
+
* isTaskDone(changeNode); // => true if all plan items done
|
|
467
|
+
* ```
|
|
420
468
|
*/
|
|
421
469
|
export function isTaskDone(node) {
|
|
422
470
|
// If the node has a subsystem with change children, check those recursively
|
|
@@ -447,6 +495,10 @@ export function isTaskDone(node) {
|
|
|
447
495
|
* subsystem (and their subsystems).
|
|
448
496
|
* @param node - The node to check.
|
|
449
497
|
* @returns The result.
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* const { total, done } = countTasks(changeNode);
|
|
501
|
+
* ```
|
|
450
502
|
*/
|
|
451
503
|
export function countTasks(node) {
|
|
452
504
|
let total = 0;
|
|
@@ -473,8 +525,12 @@ export function countTasks(node) {
|
|
|
473
525
|
* Inspect a document and return workflow completeness for a given prefix.
|
|
474
526
|
* Never throws — missing nodes are reported as "not defined".
|
|
475
527
|
* @param doc - The SysProM document.
|
|
476
|
-
* @param prefix -
|
|
477
|
-
* @returns
|
|
528
|
+
* @param prefix - ID prefix identifying the plan (e.g. "PLAN").
|
|
529
|
+
* @returns Comprehensive status of all plan components.
|
|
530
|
+
* @example
|
|
531
|
+
* ```ts
|
|
532
|
+
* const status = planStatus(doc, "PLAN");
|
|
533
|
+
* ```
|
|
478
534
|
*/
|
|
479
535
|
export function planStatus(doc, prefix) {
|
|
480
536
|
const constitution = findNode(doc, `${prefix}-CONST`);
|
|
@@ -574,8 +630,12 @@ export function planStatus(doc, prefix) {
|
|
|
574
630
|
* Return per-task completion data.
|
|
575
631
|
* Tasks (change nodes) are discovered from PROT-IMPL.subsystem, sorted topologically.
|
|
576
632
|
* @param doc - The SysProM document.
|
|
577
|
-
* @param prefix -
|
|
578
|
-
* @returns
|
|
633
|
+
* @param prefix - ID prefix identifying the plan (e.g. "PLAN").
|
|
634
|
+
* @returns Per-phase progress with task counts and percentages.
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* const phases = planProgress(doc, "PLAN");
|
|
638
|
+
* ```
|
|
579
639
|
*/
|
|
580
640
|
export function planProgress(doc, prefix) {
|
|
581
641
|
const protImpl = findNode(doc, `${prefix}-PROT-IMPL`);
|
|
@@ -611,16 +671,20 @@ export function planProgress(doc, prefix) {
|
|
|
611
671
|
* Validate readiness to enter the given phase (1-indexed).
|
|
612
672
|
*
|
|
613
673
|
* Always checks:
|
|
614
|
-
* - Each capability ({prefix}-US-*) has a change node that implements it
|
|
615
|
-
* - Each capability has non-placeholder acceptance criteria
|
|
616
|
-
* - Each invariant ({prefix}-FR-*) has a change node that implements it
|
|
674
|
+
* - Each capability ({prefix}-US-*) has a change node that implements it.
|
|
675
|
+
* - Each capability has non-placeholder acceptance criteria.
|
|
676
|
+
* - Each invariant ({prefix}-FR-*) has a change node that implements it.
|
|
617
677
|
*
|
|
618
678
|
* Additionally for phase N > 1:
|
|
619
|
-
* - All tasks in phase N-1 must be done
|
|
679
|
+
* - All tasks in phase N-1 must be done.
|
|
620
680
|
* @param doc - The SysProM document.
|
|
621
681
|
* @param prefix - Plan prefix.
|
|
622
682
|
* @param phase - Phase number (1-indexed).
|
|
623
683
|
* @returns Gate check result with readiness flag and issues.
|
|
684
|
+
* @example
|
|
685
|
+
* ```ts
|
|
686
|
+
* const result = checkGate(doc, "PLAN", 2);
|
|
687
|
+
* ```
|
|
624
688
|
*/
|
|
625
689
|
export function checkGate(doc, prefix, phase) {
|
|
626
690
|
if (phase < 1) {
|
|
@@ -30,12 +30,24 @@ export interface SpecKitFeature {
|
|
|
30
30
|
* Looks for .specify/ and specs/ subdirectories.
|
|
31
31
|
* @param dir - Directory to scan.
|
|
32
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
|
+
* ```
|
|
33
41
|
*/
|
|
34
42
|
export declare function detectSpecKitProject(dir: string): SpecKitProject;
|
|
35
43
|
/**
|
|
36
44
|
* List all features in the specs/ directory, sorted by number.
|
|
37
45
|
* @param project - The detected project.
|
|
38
46
|
* @returns Sorted list of features.
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const features = listFeatures(project);
|
|
50
|
+
* ```
|
|
39
51
|
*/
|
|
40
52
|
export declare function listFeatures(project: SpecKitProject): SpecKitFeature[];
|
|
41
53
|
/**
|
|
@@ -44,11 +56,19 @@ export declare function listFeatures(project: SpecKitProject): SpecKitFeature[];
|
|
|
44
56
|
* @param project - The detected project.
|
|
45
57
|
* @param idOrName - Feature ID, number, or name.
|
|
46
58
|
* @returns The matching feature, or null.
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const feature = getFeature(project, "001-auth");
|
|
62
|
+
* ```
|
|
47
63
|
*/
|
|
48
64
|
export declare function getFeature(project: SpecKitProject, idOrName: string): SpecKitFeature | null;
|
|
49
65
|
/**
|
|
50
66
|
* Resolve the constitution.md file, checking .specify/memory/ first, then root.
|
|
51
67
|
* @param project - The detected project.
|
|
52
68
|
* @returns Path to constitution.md, or null.
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* const path = resolveConstitution(project);
|
|
72
|
+
* ```
|
|
53
73
|
*/
|
|
54
74
|
export declare function resolveConstitution(project: SpecKitProject): string | null;
|
|
@@ -5,6 +5,14 @@ import { join } from "node:path";
|
|
|
5
5
|
* Looks for .specify/ and specs/ subdirectories.
|
|
6
6
|
* @param dir - Directory to scan.
|
|
7
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
|
+
* ```
|
|
8
16
|
*/
|
|
9
17
|
export function detectSpecKitProject(dir) {
|
|
10
18
|
const specifyDir = checkDir(join(dir, ".specify"));
|
|
@@ -34,6 +42,10 @@ export function detectSpecKitProject(dir) {
|
|
|
34
42
|
* List all features in the specs/ directory, sorted by number.
|
|
35
43
|
* @param project - The detected project.
|
|
36
44
|
* @returns Sorted list of features.
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const features = listFeatures(project);
|
|
48
|
+
* ```
|
|
37
49
|
*/
|
|
38
50
|
export function listFeatures(project) {
|
|
39
51
|
if (!project.specsDir) {
|
|
@@ -70,6 +82,10 @@ export function listFeatures(project) {
|
|
|
70
82
|
* @param project - The detected project.
|
|
71
83
|
* @param idOrName - Feature ID, number, or name.
|
|
72
84
|
* @returns The matching feature, or null.
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* const feature = getFeature(project, "001-auth");
|
|
88
|
+
* ```
|
|
73
89
|
*/
|
|
74
90
|
export function getFeature(project, idOrName) {
|
|
75
91
|
const features = listFeatures(project);
|
|
@@ -92,6 +108,10 @@ export function getFeature(project, idOrName) {
|
|
|
92
108
|
* Resolve the constitution.md file, checking .specify/memory/ first, then root.
|
|
93
109
|
* @param project - The detected project.
|
|
94
110
|
* @returns Path to constitution.md, or null.
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const path = resolveConstitution(project);
|
|
114
|
+
* ```
|
|
95
115
|
*/
|
|
96
116
|
export function resolveConstitution(project) {
|
|
97
117
|
return project.constitutionPath;
|
|
@@ -100,6 +120,10 @@ export function resolveConstitution(project) {
|
|
|
100
120
|
* Check if a directory exists, return path or null.
|
|
101
121
|
* @param path - Path to check.
|
|
102
122
|
* @returns The path if it exists as a directory, or null.
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const dir = checkDir("/path/to/dir");
|
|
126
|
+
* ```
|
|
103
127
|
*/
|
|
104
128
|
function checkDir(path) {
|
|
105
129
|
try {
|
|
@@ -115,6 +139,10 @@ function checkDir(path) {
|
|
|
115
139
|
* @param dir - Directory to search.
|
|
116
140
|
* @param dirName - Subdirectory name pattern.
|
|
117
141
|
* @returns Array of matching directory paths.
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* const feature = parseFeatureDirectory("./specs", "001-auth");
|
|
145
|
+
* ```
|
|
118
146
|
*/
|
|
119
147
|
function parseFeatureDirectory(dir, dirName) {
|
|
120
148
|
// Parse the directory name format: "NNN-feature-name"
|
|
@@ -145,6 +173,10 @@ function parseFeatureDirectory(dir, dirName) {
|
|
|
145
173
|
* @param dir - Directory to search.
|
|
146
174
|
* @param filename - File name to find.
|
|
147
175
|
* @returns Array of matching file paths.
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const specPath = checkFile("./feature", "spec.md");
|
|
179
|
+
* ```
|
|
148
180
|
*/
|
|
149
181
|
function checkFile(dir, filename) {
|
|
150
182
|
const path = join(dir, filename);
|
package/dist/src/text.d.ts
CHANGED
|
@@ -2,18 +2,32 @@
|
|
|
2
2
|
* Normalise a text field (string | string[]) to a single string, joining with newlines.
|
|
3
3
|
* @param value - The text field to normalise.
|
|
4
4
|
* @returns A single string.
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* textToString(["line 1", "line 2"]) // => "line 1\nline 2"
|
|
8
|
+
* textToString("hello") // => "hello"
|
|
9
|
+
* ```
|
|
5
10
|
*/
|
|
6
11
|
export declare function textToString(value: string | string[]): string;
|
|
7
12
|
/**
|
|
8
13
|
* Normalise a text field to an array of lines.
|
|
9
14
|
* @param value - The text field to normalise.
|
|
10
15
|
* @returns An array of lines.
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* textToLines("hello") // => ["hello"]
|
|
19
|
+
* textToLines(["a", "b"]) // => ["a", "b"]
|
|
20
|
+
* ```
|
|
11
21
|
*/
|
|
12
22
|
export declare function textToLines(value: string | string[]): string[];
|
|
13
23
|
/**
|
|
14
24
|
* Format a text field for Markdown output — each line becomes a paragraph.
|
|
15
25
|
* @param value - The text field to format.
|
|
16
26
|
* @returns Markdown-formatted string.
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* textToMarkdown(["para 1", "para 2"]) // => "para 1\npara 2"
|
|
30
|
+
* ```
|
|
17
31
|
*/
|
|
18
32
|
export declare function textToMarkdown(value: string | string[]): string;
|
|
19
33
|
/**
|
|
@@ -21,5 +35,10 @@ export declare function textToMarkdown(value: string | string[]): string;
|
|
|
21
35
|
* Single-line content stays as a string; multiline becomes an array.
|
|
22
36
|
* @param block - The Markdown text block to parse.
|
|
23
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
|
+
* ```
|
|
24
43
|
*/
|
|
25
44
|
export declare function markdownToText(block: string): string | string[];
|