sysprom 1.16.1 → 1.18.0
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 +147 -75
- package/dist/schema.json +2 -1
- package/dist/src/cli/commands/add.js +52 -36
- package/dist/src/cli/commands/graph.d.ts +15 -0
- package/dist/src/cli/commands/graph.js +51 -2
- package/dist/src/cli/commands/infer.js +44 -25
- package/dist/src/cli/commands/init.d.ts +1 -1
- package/dist/src/cli/commands/json2md.d.ts +30 -1
- package/dist/src/cli/commands/json2md.js +42 -1
- package/dist/src/cli/commands/md2json.d.ts +1 -1
- package/dist/src/cli/commands/query.js +35 -37
- package/dist/src/cli/commands/speckit.js +81 -77
- package/dist/src/cli/commands/stats.js +4 -4
- package/dist/src/cli/commands/sync.d.ts +1 -1
- package/dist/src/cli/commands/update.js +33 -20
- package/dist/src/cli/define-command.d.ts +1 -1
- package/dist/src/cli/define-command.js +176 -156
- package/dist/src/endpoint-types.js +13 -6
- package/dist/src/io.js +59 -8
- package/dist/src/json-to-md.d.ts +32 -2
- package/dist/src/json-to-md.js +145 -5
- package/dist/src/mcp/server.js +269 -112
- package/dist/src/md-to-json.js +7 -0
- package/dist/src/operations/add-node.d.ts +12 -9
- package/dist/src/operations/add-plan-task.d.ts +8 -6
- package/dist/src/operations/add-relationship.d.ts +11 -8
- package/dist/src/operations/check.d.ts +4 -3
- package/dist/src/operations/define-operation.d.ts +1 -1
- package/dist/src/operations/graph-decision.d.ts +329 -0
- package/dist/src/operations/graph-decision.js +96 -0
- package/dist/src/operations/graph-dependency.d.ts +329 -0
- package/dist/src/operations/graph-dependency.js +121 -0
- package/dist/src/operations/graph-refinement.d.ts +329 -0
- package/dist/src/operations/graph-refinement.js +97 -0
- package/dist/src/operations/graph-shared.d.ts +116 -0
- package/dist/src/operations/graph-shared.js +257 -0
- package/dist/src/operations/graph.d.ts +20 -4
- package/dist/src/operations/graph.js +129 -36
- package/dist/src/operations/index.d.ts +3 -0
- package/dist/src/operations/index.js +3 -0
- package/dist/src/operations/infer-completeness.d.ts +4 -3
- package/dist/src/operations/infer-derived.d.ts +4 -3
- package/dist/src/operations/infer-impact.d.ts +28 -21
- package/dist/src/operations/infer-lifecycle.d.ts +4 -3
- package/dist/src/operations/init-document.d.ts +4 -3
- package/dist/src/operations/json-to-markdown.d.ts +28 -3
- package/dist/src/operations/json-to-markdown.js +11 -1
- package/dist/src/operations/mark-task-done.d.ts +8 -6
- package/dist/src/operations/mark-task-undone.d.ts +8 -6
- package/dist/src/operations/markdown-to-json.d.ts +4 -3
- package/dist/src/operations/next-id.d.ts +4 -3
- package/dist/src/operations/node-history.d.ts +4 -3
- package/dist/src/operations/plan-add-task.d.ts +8 -6
- package/dist/src/operations/plan-gate.d.ts +4 -3
- package/dist/src/operations/plan-init.d.ts +4 -3
- package/dist/src/operations/plan-progress.d.ts +4 -3
- package/dist/src/operations/plan-status.d.ts +4 -3
- package/dist/src/operations/query-node.d.ts +24 -17
- package/dist/src/operations/query-nodes.d.ts +8 -6
- package/dist/src/operations/query-relationships.d.ts +7 -5
- package/dist/src/operations/remove-node.d.ts +12 -9
- package/dist/src/operations/remove-relationship.d.ts +10 -7
- package/dist/src/operations/rename.d.ts +8 -6
- package/dist/src/operations/search.d.ts +8 -6
- package/dist/src/operations/speckit-diff.d.ts +4 -3
- package/dist/src/operations/speckit-export.d.ts +4 -3
- package/dist/src/operations/speckit-import.d.ts +4 -3
- package/dist/src/operations/speckit-sync.d.ts +12 -9
- package/dist/src/operations/state-at.d.ts +4 -3
- package/dist/src/operations/stats.d.ts +4 -3
- package/dist/src/operations/sync.d.ts +12 -9
- package/dist/src/operations/task-list.d.ts +4 -3
- package/dist/src/operations/timeline.d.ts +4 -3
- package/dist/src/operations/trace-from-node.d.ts +12 -9
- package/dist/src/operations/update-metadata.d.ts +8 -6
- package/dist/src/operations/update-node.d.ts +11 -8
- package/dist/src/operations/update-plan-task.d.ts +8 -6
- package/dist/src/operations/validate.d.ts +4 -3
- package/dist/src/schema.d.ts +15 -10
- package/dist/src/schema.js +3 -11
- package/dist/src/utils/define-schema.d.ts +17 -0
- package/dist/src/utils/define-schema.js +21 -0
- package/package.json +98 -93
- package/schema.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/sɪs.prɒm/
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/sysprom)[](https://github.com/ExaDev/SysProM)
|
|
6
|
+
|
|
5
7
|
A recursive, decision-driven model for recording where every part of a system came from, what decisions shaped it, and how it reached its current form.
|
|
6
8
|
|
|
7
9
|
## Install
|
|
@@ -21,16 +23,60 @@ Both `sysprom` and `spm` are available as commands — use `sysprom` for new pro
|
|
|
21
23
|
|
|
22
24
|
## CLI
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
````sh
|
|
25
27
|
# Convert between formats
|
|
26
28
|
sysprom json2md --input .SysProM.json --output ./.SysProM
|
|
27
29
|
sysprom md2json --input ./.SysProM --output output.SysProM.json
|
|
28
30
|
|
|
31
|
+
# Diagram and label options
|
|
32
|
+
|
|
33
|
+
The `graph` and `json2md` commands support options to control diagram layout and node label style used when generating Mermaid/DOT output.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# Generate a Mermaid graph with friendly labels (default)
|
|
39
|
+
sysprom graph --path .SysProM.json --format mermaid
|
|
40
|
+
|
|
41
|
+
# Compact node labels (IDs only) instead of `ID: Name`
|
|
42
|
+
sysprom graph --path .SysProM.json --format mermaid --label-mode compact
|
|
43
|
+
|
|
44
|
+
# Embed diagrams when converting JSON → Markdown, using compact labels
|
|
45
|
+
sysprom json2md --input .SysProM.json --output ./.SysProM --embed-diagrams --label-mode compact
|
|
46
|
+
|
|
47
|
+
# Control layout direction for graph (LR, TD, RL, BT)
|
|
48
|
+
sysprom graph --path .SysProM.json --format mermaid --layout LR
|
|
49
|
+
````
|
|
50
|
+
|
|
51
|
+
Notes:
|
|
52
|
+
|
|
53
|
+
- `--label-mode friendly|compact` controls whether nodes show `ID: Name` (`friendly`) or just `ID` (`compact`).
|
|
54
|
+
- `--layout LR|TD|RL|BT` controls graph direction. `json2md` uses sensible per-diagram defaults (relationship/refinement/decision diagrams default to `TD`, dependency diagrams default to `LR`) but you can override when calling `graph` directly.
|
|
55
|
+
- `--label-mode friendly|compact` controls whether nodes show `ID: Name` (`friendly`) or just `ID` (`compact`).
|
|
56
|
+
- `--layout LR|TD|RL|BT` controls graph direction. `json2md` uses sensible per-diagram defaults (relationship/refinement/decision diagrams default to `TD`, dependency diagrams default to `LR`) but you can override when calling `graph` directly.
|
|
57
|
+
- `json2md` supports per-diagram layout overrides to control each embedded diagram independently:
|
|
58
|
+
- `--relationship-layout LR|TD|RL|BT` — override the layout used for the Relationship Graph.
|
|
59
|
+
- `--refinement-layout LR|TD|RL|BT` — override the layout used for the Refinement Chain.
|
|
60
|
+
- `--decision-layout LR|TD|RL|BT` — override the layout used for the Decision Map.
|
|
61
|
+
- `--dependency-layout LR|TD|RL|BT` — override the layout used for the Dependency Graph.
|
|
62
|
+
|
|
63
|
+
Examples:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
# Embed diagrams in multi-doc output but make dependency graph top-to-bottom (TD)
|
|
67
|
+
sysprom json2md --input .SysProM.json --output ./.SysProM --embed-diagrams --dependency-layout TD
|
|
68
|
+
|
|
69
|
+
# Override relationship and refinement layouts independently
|
|
70
|
+
sysprom json2md --input .SysProM.json --output ./.SysProM --embed-diagrams --relationship-layout LR --refinement-layout TD
|
|
71
|
+
```
|
|
72
|
+
|
|
29
73
|
# Validate and summarise (auto-detects .SysProM.json in current directory)
|
|
74
|
+
|
|
30
75
|
sysprom validate
|
|
31
76
|
sysprom stats
|
|
32
77
|
|
|
33
78
|
# Query nodes and relationships
|
|
79
|
+
|
|
34
80
|
sysprom query nodes --type decision
|
|
35
81
|
sysprom query node D1
|
|
36
82
|
sysprom query rels --from D1
|
|
@@ -39,27 +85,32 @@ sysprom query timeline
|
|
|
39
85
|
sysprom query state-at 2026-03-22
|
|
40
86
|
|
|
41
87
|
# Add nodes (ID auto-generated from type prefix if --id omitted)
|
|
88
|
+
|
|
42
89
|
sysprom add invariant --name "New Rule" --description "Must hold"
|
|
43
90
|
sysprom add decision --name "Choose X" \
|
|
44
|
-
|
|
45
|
-
|
|
91
|
+
--option "OPT-A:Use framework X" --option "OPT-B:Use framework Y" \
|
|
92
|
+
--selected OPT-A --rationale "Lower migration effort"
|
|
46
93
|
|
|
47
94
|
# Remove nodes
|
|
95
|
+
|
|
48
96
|
sysprom remove INV23
|
|
49
97
|
|
|
50
98
|
# Update nodes, relationships, and metadata
|
|
99
|
+
|
|
51
100
|
sysprom update node D1 --status deprecated
|
|
52
101
|
sysprom update add-rel D1 affects EL5
|
|
53
102
|
sysprom update remove-rel D1 affects EL5
|
|
54
103
|
sysprom update meta --fields version=2
|
|
55
104
|
|
|
56
105
|
# Inference operations (deterministic graph analysis)
|
|
57
|
-
|
|
58
|
-
sysprom infer
|
|
59
|
-
sysprom infer
|
|
60
|
-
sysprom infer
|
|
61
|
-
sysprom infer
|
|
62
|
-
|
|
106
|
+
|
|
107
|
+
sysprom infer completeness # Score node completeness (0-1)
|
|
108
|
+
sysprom infer lifecycle # Infer lifecycle phases
|
|
109
|
+
sysprom infer impact I1 # Trace impact from node
|
|
110
|
+
sysprom infer derived # Compute transitive closure
|
|
111
|
+
sysprom infer all # Run all analyses
|
|
112
|
+
|
|
113
|
+
````
|
|
63
114
|
|
|
64
115
|
All commands auto-detect the document — they search the current directory for `.SysProM.json`, `.SysProM.md`, or `.SysProM/` (in that priority order), then fall back to `.spm.json`, `.spm.md`, or `.spm/`. Use `--path` to specify an explicit path. Note: `spm` is an alias for `sysprom` for backwards compatibility.
|
|
65
116
|
|
|
@@ -80,7 +131,7 @@ Add the following to your MCP client's configuration (e.g. `.cursor/mcp.json`, `
|
|
|
80
131
|
}
|
|
81
132
|
}
|
|
82
133
|
}
|
|
83
|
-
|
|
134
|
+
````
|
|
84
135
|
|
|
85
136
|
Or via the CLI subcommand (equivalent):
|
|
86
137
|
|
|
@@ -90,23 +141,23 @@ sysprom mcp # starts the MCP server on stdio
|
|
|
90
141
|
|
|
91
142
|
### Available Tools
|
|
92
143
|
|
|
93
|
-
| Tool
|
|
94
|
-
|
|
95
|
-
| `validate`
|
|
96
|
-
| `stats`
|
|
97
|
-
| `query-nodes`
|
|
98
|
-
| `query-node`
|
|
99
|
-
| `query-relationships` | Query relationships by source, target, or type
|
|
100
|
-
| `trace`
|
|
101
|
-
| `add-node`
|
|
102
|
-
| `remove-node`
|
|
103
|
-
| `update-node`
|
|
104
|
-
| `add-relationship`
|
|
105
|
-
| `remove-relationship` | Remove a relationship
|
|
106
|
-
| `infer-completeness`
|
|
107
|
-
| `infer-lifecycle`
|
|
108
|
-
| `infer-impact`
|
|
109
|
-
| `infer-derived`
|
|
144
|
+
| Tool | Description |
|
|
145
|
+
| --------------------- | --------------------------------------------------------------- |
|
|
146
|
+
| `validate` | Validate a SysProM document and return issues |
|
|
147
|
+
| `stats` | Return document statistics |
|
|
148
|
+
| `query-nodes` | Query nodes by type, status, or text |
|
|
149
|
+
| `query-node` | Retrieve a single node by ID |
|
|
150
|
+
| `query-relationships` | Query relationships by source, target, or type |
|
|
151
|
+
| `trace` | Trace refinement chains from a node |
|
|
152
|
+
| `add-node` | Add a new node to the document |
|
|
153
|
+
| `remove-node` | Remove a node by ID |
|
|
154
|
+
| `update-node` | Update fields on an existing node |
|
|
155
|
+
| `add-relationship` | Add a relationship between nodes |
|
|
156
|
+
| `remove-relationship` | Remove a relationship |
|
|
157
|
+
| `infer-completeness` | Score node completeness (0-1) based on refinement relationships |
|
|
158
|
+
| `infer-lifecycle` | Infer lifecycle phase from status and lifecycle fields |
|
|
159
|
+
| `infer-impact` | Trace impact propagation from a starting node |
|
|
160
|
+
| `infer-derived` | Compute transitive closure and inverse relationships |
|
|
110
161
|
|
|
111
162
|
All tools accept a `path` parameter to specify the SysProM document location.
|
|
112
163
|
|
|
@@ -114,50 +165,50 @@ All tools accept a `path` parameter to specify the SysProM document location.
|
|
|
114
165
|
|
|
115
166
|
```ts
|
|
116
167
|
import {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
// Schema and types
|
|
169
|
+
sysproMDocument,
|
|
170
|
+
node,
|
|
171
|
+
nodeType,
|
|
172
|
+
relationshipType,
|
|
173
|
+
type SysProMDocument,
|
|
174
|
+
type Node,
|
|
175
|
+
type Relationship,
|
|
176
|
+
|
|
177
|
+
// Conversion
|
|
178
|
+
jsonToMarkdown,
|
|
179
|
+
jsonToMarkdownSingle,
|
|
180
|
+
jsonToMarkdownMultiDoc,
|
|
181
|
+
markdownToJson,
|
|
182
|
+
|
|
183
|
+
// Validation and query
|
|
184
|
+
validate,
|
|
185
|
+
stats,
|
|
186
|
+
queryNodes,
|
|
187
|
+
queryNode,
|
|
188
|
+
queryRelationships,
|
|
189
|
+
traceFromNode,
|
|
190
|
+
|
|
191
|
+
// Mutation
|
|
192
|
+
addNode,
|
|
193
|
+
removeNode,
|
|
194
|
+
updateNode,
|
|
195
|
+
addRelationship,
|
|
196
|
+
removeRelationship,
|
|
197
|
+
updateMetadata,
|
|
198
|
+
|
|
199
|
+
// Inference
|
|
200
|
+
inferCompletenessOp,
|
|
201
|
+
inferLifecycleOp,
|
|
202
|
+
inferImpactOp,
|
|
203
|
+
inferDerivedOp,
|
|
204
|
+
|
|
205
|
+
// File I/O
|
|
206
|
+
loadDocument,
|
|
207
|
+
saveDocument,
|
|
208
|
+
|
|
209
|
+
// Utilities
|
|
210
|
+
canonicalise,
|
|
211
|
+
toJSONSchema,
|
|
161
212
|
} from "sysprom";
|
|
162
213
|
|
|
163
214
|
// Validate
|
|
@@ -170,12 +221,24 @@ const decisions = queryNodes(doc, { type: "decision" });
|
|
|
170
221
|
const trace = traceFromNode(doc, "I1");
|
|
171
222
|
|
|
172
223
|
// Mutate
|
|
173
|
-
const updated = addNode(doc, {
|
|
174
|
-
|
|
224
|
+
const updated = addNode(doc, {
|
|
225
|
+
id: "INV23",
|
|
226
|
+
type: "invariant",
|
|
227
|
+
name: "New Rule",
|
|
228
|
+
});
|
|
229
|
+
const withRel = addRelationship(updated, {
|
|
230
|
+
from: "D1",
|
|
231
|
+
to: "INV23",
|
|
232
|
+
type: "must_preserve",
|
|
233
|
+
});
|
|
175
234
|
|
|
176
235
|
// Type guards
|
|
177
|
-
if (sysproMDocument.is(data)) {
|
|
178
|
-
|
|
236
|
+
if (sysproMDocument.is(data)) {
|
|
237
|
+
/* data is SysProMDocument */
|
|
238
|
+
}
|
|
239
|
+
if (node.is(thing)) {
|
|
240
|
+
/* thing is Node */
|
|
241
|
+
}
|
|
179
242
|
```
|
|
180
243
|
|
|
181
244
|
## What is SysProM?
|
|
@@ -321,21 +384,25 @@ When working on the SysProM repo itself, skills in `.claude/skills/` are auto-di
|
|
|
321
384
|
### Skills by Category
|
|
322
385
|
|
|
323
386
|
**Node Creation (4 skills)**
|
|
387
|
+
|
|
324
388
|
- `add-decision` — Create decision nodes with context, options, rationale, and invariant links
|
|
325
389
|
- `add-change` — Create change nodes with scope, operations, and task tracking
|
|
326
390
|
- `add-invariant` — Create invariant nodes representing system rules and constraints
|
|
327
391
|
- `add-node` — Generic node creation for any SysProM type
|
|
328
392
|
|
|
329
393
|
**Node Modification (3 skills)**
|
|
394
|
+
|
|
330
395
|
- `update-node` — Modify node fields, status, lifecycle, context, or rationale
|
|
331
396
|
- `remove-node` — Delete nodes with safety flags (hard delete, recursive, repair)
|
|
332
397
|
- `rename-node` — Rename node IDs across all references
|
|
333
398
|
|
|
334
399
|
**Relationships (2 skills)**
|
|
400
|
+
|
|
335
401
|
- `add-relationship` — Create relationships between nodes with specific types
|
|
336
402
|
- `remove-relationship` — Delete relationships
|
|
337
403
|
|
|
338
404
|
**Query & Analysis (5 skills)**
|
|
405
|
+
|
|
339
406
|
- `query-nodes` — Search nodes by type, status, text, or ID
|
|
340
407
|
- `query-relationships` — Query relationships by source, target, or type
|
|
341
408
|
- `trace-node` — Trace refinement chains through abstraction layers
|
|
@@ -343,26 +410,31 @@ When working on the SysProM repo itself, skills in `.claude/skills/` are auto-di
|
|
|
343
410
|
- `stats` — Show document statistics and composition metrics
|
|
344
411
|
|
|
345
412
|
**Visualisation (1 skill)**
|
|
413
|
+
|
|
346
414
|
- `graph` — Generate Mermaid or DOT graphs with filtering
|
|
347
415
|
|
|
348
416
|
**Format Conversion (4 skills)**
|
|
417
|
+
|
|
349
418
|
- `init-document` — Create new SysProM documents with metadata
|
|
350
419
|
- `json-to-markdown` — Convert JSON to Markdown format
|
|
351
420
|
- `markdown-to-json` — Convert Markdown to JSON format
|
|
352
421
|
- `sync-formats` — Bidirectional sync between JSON and Markdown
|
|
353
422
|
|
|
354
423
|
**Spec-Kit Integration (4 skills)**
|
|
424
|
+
|
|
355
425
|
- `speckit-import` — Import Spec-Kit features as SysProM nodes
|
|
356
426
|
- `speckit-export` — Export SysProM nodes to Spec-Kit format
|
|
357
427
|
- `speckit-sync` — Bidirectional sync with Spec-Kit specifications
|
|
358
428
|
- `speckit-diff` — Show differences between SysProM and Spec-Kit
|
|
359
429
|
|
|
360
430
|
**Task Management (3 skills)**
|
|
431
|
+
|
|
361
432
|
- `task-list` — List tasks in a change node with progress
|
|
362
433
|
- `task-add` — Add tasks to a change
|
|
363
434
|
- `task-mark-done` — Mark tasks as complete
|
|
364
435
|
|
|
365
436
|
**Plan Management (2 skills)**
|
|
437
|
+
|
|
366
438
|
- `plan-init` — Initialise plans with phases and gates
|
|
367
439
|
- `plan-status` — Show plan progress and phase gates
|
|
368
440
|
|
package/dist/schema.json
CHANGED
|
@@ -31,6 +31,49 @@ const optsSchema = mutationOpts.extend({
|
|
|
31
31
|
.optional()
|
|
32
32
|
.describe("Option in format 'ID:description' or just 'description' (repeatable)"),
|
|
33
33
|
});
|
|
34
|
+
/**
|
|
35
|
+
* Populate optional node fields from CLI options.
|
|
36
|
+
* @param node - The node to populate
|
|
37
|
+
* @param description - Optional description
|
|
38
|
+
* @param status - Optional (pre-validated) status
|
|
39
|
+
* @param context - Optional decision context
|
|
40
|
+
* @param rationale - Optional decision rationale
|
|
41
|
+
* @param scope - Optional change scope
|
|
42
|
+
* @param selected - Optional selected option ID
|
|
43
|
+
* @param options - Optional array of option descriptions
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const node: Node = { id: "D1", type: "decision", name: "My Decision" };
|
|
47
|
+
* populateNodeFromOpts(node, desc, status, ctx, rat, scope, sel, opts);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function populateNodeFromOpts(node, description, status, context, rationale, scope, selected, options) {
|
|
51
|
+
if (description)
|
|
52
|
+
node.description = description;
|
|
53
|
+
if (status)
|
|
54
|
+
node.status = status;
|
|
55
|
+
if (context)
|
|
56
|
+
node.context = context;
|
|
57
|
+
if (rationale)
|
|
58
|
+
node.rationale = rationale;
|
|
59
|
+
if (scope?.length)
|
|
60
|
+
node.scope = scope;
|
|
61
|
+
if (selected)
|
|
62
|
+
node.selected = selected;
|
|
63
|
+
if (options?.length) {
|
|
64
|
+
node.options = options.map((arg, i) => {
|
|
65
|
+
const colonIdx = arg.indexOf(":");
|
|
66
|
+
if (colonIdx >= 0) {
|
|
67
|
+
return {
|
|
68
|
+
id: arg.slice(0, colonIdx),
|
|
69
|
+
description: arg.slice(colonIdx + 1),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const letter = String.fromCharCode(65 + i);
|
|
73
|
+
return { id: `${node.id}-OPT-${letter}`, description: arg };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
34
77
|
export const addCommand = {
|
|
35
78
|
name: "add",
|
|
36
79
|
description: addNodeOp.def.description,
|
|
@@ -49,45 +92,18 @@ export const addCommand = {
|
|
|
49
92
|
console.error(`Unknown node type: "${type}". Valid types: ${NodeType.options.join(", ")}`);
|
|
50
93
|
process.exit(1);
|
|
51
94
|
}
|
|
95
|
+
if (opts.status && !NodeStatus.is(opts.status)) {
|
|
96
|
+
console.error(`Unknown status: "${opts.status}". Valid statuses: ${NodeStatus.options.join(", ")}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
52
99
|
const id = opts.id ?? nextIdOp({ doc, type });
|
|
53
|
-
// Build the node from CLI options
|
|
54
100
|
const node = { id, type, name: opts.name };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (!NodeStatus.is(opts.status)) {
|
|
60
|
-
console.error(`Unknown status: "${opts.status}". Valid statuses: ${NodeStatus.options.join(", ")}`);
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
node.status = opts.status;
|
|
64
|
-
}
|
|
65
|
-
if (opts.context) {
|
|
66
|
-
node.context = opts.context;
|
|
67
|
-
}
|
|
68
|
-
if (opts.rationale) {
|
|
69
|
-
node.rationale = opts.rationale;
|
|
70
|
-
}
|
|
71
|
-
if (opts.scope && opts.scope.length > 0) {
|
|
72
|
-
node.scope = opts.scope;
|
|
73
|
-
}
|
|
74
|
-
if (opts.selected) {
|
|
75
|
-
node.selected = opts.selected;
|
|
76
|
-
}
|
|
77
|
-
if (opts.option && opts.option.length > 0) {
|
|
78
|
-
node.options = opts.option.map((arg, i) => {
|
|
79
|
-
const colonIdx = arg.indexOf(":");
|
|
80
|
-
if (colonIdx >= 0) {
|
|
81
|
-
return {
|
|
82
|
-
id: arg.slice(0, colonIdx),
|
|
83
|
-
description: arg.slice(colonIdx + 1),
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
// Auto-generate ID: D26-OPT-A, D26-OPT-B, etc.
|
|
87
|
-
const letter = String.fromCharCode(65 + i); // A, B, C, ...
|
|
88
|
-
return { id: `${id}-OPT-${letter}`, description: arg };
|
|
89
|
-
});
|
|
101
|
+
// Use type guard to narrow status type after validation
|
|
102
|
+
let status;
|
|
103
|
+
if (opts.status && NodeStatus.is(opts.status)) {
|
|
104
|
+
status = opts.status;
|
|
90
105
|
}
|
|
106
|
+
populateNodeFromOpts(node, opts.description, status, opts.context, opts.rationale, opts.scope, opts.selected, opts.option);
|
|
91
107
|
try {
|
|
92
108
|
const newDoc = addNodeOp({
|
|
93
109
|
doc,
|
|
@@ -9,6 +9,21 @@ declare const optsSchema: z.ZodObject<{
|
|
|
9
9
|
mermaid: "mermaid";
|
|
10
10
|
}>>;
|
|
11
11
|
type: z.ZodOptional<z.ZodString>;
|
|
12
|
+
nodeTypes: z.ZodOptional<z.ZodString>;
|
|
13
|
+
nodeIds: z.ZodOptional<z.ZodString>;
|
|
14
|
+
relTypes: z.ZodOptional<z.ZodString>;
|
|
15
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
TD: "TD";
|
|
17
|
+
BT: "BT";
|
|
18
|
+
RL: "RL";
|
|
19
|
+
LR: "LR";
|
|
20
|
+
}>>;
|
|
21
|
+
labelMode: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
friendly: "friendly";
|
|
23
|
+
compact: "compact";
|
|
24
|
+
}>>;
|
|
25
|
+
cluster: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
connectedOnly: z.ZodOptional<z.ZodBoolean>;
|
|
12
27
|
}, z.core.$strip>;
|
|
13
28
|
export declare const graphCommand: CommandDef<typeof noArgs, typeof optsSchema>;
|
|
14
29
|
export {};
|
|
@@ -1,9 +1,41 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import * as z from "zod";
|
|
2
3
|
import { graphOp } from "../../operations/index.js";
|
|
3
4
|
import { readOpts, loadDoc } from "../shared.js";
|
|
4
5
|
const optsSchema = readOpts.extend({
|
|
5
6
|
format: z.enum(["mermaid", "dot"]).optional().describe("Output format"),
|
|
6
|
-
type: z
|
|
7
|
+
type: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Filter by relationship type (backward compatible)"),
|
|
11
|
+
nodeTypes: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Filter by node types (comma-separated)"),
|
|
15
|
+
nodeIds: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Filter by node IDs (comma-separated)"),
|
|
19
|
+
relTypes: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Filter by relationship types (comma-separated)"),
|
|
23
|
+
layout: z
|
|
24
|
+
.enum(["LR", "TD", "RL", "BT"])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Graph layout direction"),
|
|
27
|
+
labelMode: z
|
|
28
|
+
.enum(["friendly", "compact"])
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Node label mode: "friendly" shows `id: name`, "compact" shows `id`'),
|
|
31
|
+
cluster: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Group nodes by category in subgraphs"),
|
|
35
|
+
connectedOnly: z
|
|
36
|
+
.boolean()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Only show nodes that have relationships"),
|
|
7
39
|
});
|
|
8
40
|
export const graphCommand = {
|
|
9
41
|
name: "graph",
|
|
@@ -13,15 +45,32 @@ export const graphCommand = {
|
|
|
13
45
|
action(_args, opts) {
|
|
14
46
|
try {
|
|
15
47
|
const { doc } = loadDoc(opts.path);
|
|
48
|
+
const nodeTypes = opts.nodeTypes
|
|
49
|
+
? opts.nodeTypes.split(",").map((s) => s.trim())
|
|
50
|
+
: undefined;
|
|
51
|
+
const nodeIds = opts.nodeIds
|
|
52
|
+
? opts.nodeIds.split(",").map((s) => s.trim())
|
|
53
|
+
: undefined;
|
|
54
|
+
const relTypes = opts.relTypes
|
|
55
|
+
? opts.relTypes.split(",").map((s) => s.trim())
|
|
56
|
+
: undefined;
|
|
16
57
|
const output = graphOp({
|
|
17
58
|
doc,
|
|
18
59
|
format: opts.format ?? "mermaid",
|
|
19
60
|
typeFilter: opts.type,
|
|
61
|
+
nodeTypes,
|
|
62
|
+
nodeIds,
|
|
63
|
+
relTypes,
|
|
64
|
+
layout: opts.layout ?? "TD",
|
|
65
|
+
cluster: opts.cluster ?? true,
|
|
66
|
+
labelMode: opts.labelMode ?? "friendly",
|
|
67
|
+
connectedOnly: opts.connectedOnly ?? false,
|
|
20
68
|
});
|
|
21
69
|
console.log(output);
|
|
22
70
|
}
|
|
23
71
|
catch (err) {
|
|
24
|
-
|
|
72
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73
|
+
console.error(message);
|
|
25
74
|
process.exit(1);
|
|
26
75
|
}
|
|
27
76
|
},
|