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.
Files changed (84) hide show
  1. package/README.md +147 -75
  2. package/dist/schema.json +2 -1
  3. package/dist/src/cli/commands/add.js +52 -36
  4. package/dist/src/cli/commands/graph.d.ts +15 -0
  5. package/dist/src/cli/commands/graph.js +51 -2
  6. package/dist/src/cli/commands/infer.js +44 -25
  7. package/dist/src/cli/commands/init.d.ts +1 -1
  8. package/dist/src/cli/commands/json2md.d.ts +30 -1
  9. package/dist/src/cli/commands/json2md.js +42 -1
  10. package/dist/src/cli/commands/md2json.d.ts +1 -1
  11. package/dist/src/cli/commands/query.js +35 -37
  12. package/dist/src/cli/commands/speckit.js +81 -77
  13. package/dist/src/cli/commands/stats.js +4 -4
  14. package/dist/src/cli/commands/sync.d.ts +1 -1
  15. package/dist/src/cli/commands/update.js +33 -20
  16. package/dist/src/cli/define-command.d.ts +1 -1
  17. package/dist/src/cli/define-command.js +176 -156
  18. package/dist/src/endpoint-types.js +13 -6
  19. package/dist/src/io.js +59 -8
  20. package/dist/src/json-to-md.d.ts +32 -2
  21. package/dist/src/json-to-md.js +145 -5
  22. package/dist/src/mcp/server.js +269 -112
  23. package/dist/src/md-to-json.js +7 -0
  24. package/dist/src/operations/add-node.d.ts +12 -9
  25. package/dist/src/operations/add-plan-task.d.ts +8 -6
  26. package/dist/src/operations/add-relationship.d.ts +11 -8
  27. package/dist/src/operations/check.d.ts +4 -3
  28. package/dist/src/operations/define-operation.d.ts +1 -1
  29. package/dist/src/operations/graph-decision.d.ts +329 -0
  30. package/dist/src/operations/graph-decision.js +96 -0
  31. package/dist/src/operations/graph-dependency.d.ts +329 -0
  32. package/dist/src/operations/graph-dependency.js +121 -0
  33. package/dist/src/operations/graph-refinement.d.ts +329 -0
  34. package/dist/src/operations/graph-refinement.js +97 -0
  35. package/dist/src/operations/graph-shared.d.ts +116 -0
  36. package/dist/src/operations/graph-shared.js +257 -0
  37. package/dist/src/operations/graph.d.ts +20 -4
  38. package/dist/src/operations/graph.js +129 -36
  39. package/dist/src/operations/index.d.ts +3 -0
  40. package/dist/src/operations/index.js +3 -0
  41. package/dist/src/operations/infer-completeness.d.ts +4 -3
  42. package/dist/src/operations/infer-derived.d.ts +4 -3
  43. package/dist/src/operations/infer-impact.d.ts +28 -21
  44. package/dist/src/operations/infer-lifecycle.d.ts +4 -3
  45. package/dist/src/operations/init-document.d.ts +4 -3
  46. package/dist/src/operations/json-to-markdown.d.ts +28 -3
  47. package/dist/src/operations/json-to-markdown.js +11 -1
  48. package/dist/src/operations/mark-task-done.d.ts +8 -6
  49. package/dist/src/operations/mark-task-undone.d.ts +8 -6
  50. package/dist/src/operations/markdown-to-json.d.ts +4 -3
  51. package/dist/src/operations/next-id.d.ts +4 -3
  52. package/dist/src/operations/node-history.d.ts +4 -3
  53. package/dist/src/operations/plan-add-task.d.ts +8 -6
  54. package/dist/src/operations/plan-gate.d.ts +4 -3
  55. package/dist/src/operations/plan-init.d.ts +4 -3
  56. package/dist/src/operations/plan-progress.d.ts +4 -3
  57. package/dist/src/operations/plan-status.d.ts +4 -3
  58. package/dist/src/operations/query-node.d.ts +24 -17
  59. package/dist/src/operations/query-nodes.d.ts +8 -6
  60. package/dist/src/operations/query-relationships.d.ts +7 -5
  61. package/dist/src/operations/remove-node.d.ts +12 -9
  62. package/dist/src/operations/remove-relationship.d.ts +10 -7
  63. package/dist/src/operations/rename.d.ts +8 -6
  64. package/dist/src/operations/search.d.ts +8 -6
  65. package/dist/src/operations/speckit-diff.d.ts +4 -3
  66. package/dist/src/operations/speckit-export.d.ts +4 -3
  67. package/dist/src/operations/speckit-import.d.ts +4 -3
  68. package/dist/src/operations/speckit-sync.d.ts +12 -9
  69. package/dist/src/operations/state-at.d.ts +4 -3
  70. package/dist/src/operations/stats.d.ts +4 -3
  71. package/dist/src/operations/sync.d.ts +12 -9
  72. package/dist/src/operations/task-list.d.ts +4 -3
  73. package/dist/src/operations/timeline.d.ts +4 -3
  74. package/dist/src/operations/trace-from-node.d.ts +12 -9
  75. package/dist/src/operations/update-metadata.d.ts +8 -6
  76. package/dist/src/operations/update-node.d.ts +11 -8
  77. package/dist/src/operations/update-plan-task.d.ts +8 -6
  78. package/dist/src/operations/validate.d.ts +4 -3
  79. package/dist/src/schema.d.ts +15 -10
  80. package/dist/src/schema.js +3 -11
  81. package/dist/src/utils/define-schema.d.ts +17 -0
  82. package/dist/src/utils/define-schema.js +21 -0
  83. package/package.json +98 -93
  84. package/schema.json +2 -1
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  /sɪs.prɒm/
4
4
 
5
+ [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm)](https://www.npmjs.com/package/sysprom)[![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github)](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
- ```sh
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
- --option "OPT-A:Use framework X" --option "OPT-B:Use framework Y" \
45
- --selected OPT-A --rationale "Lower migration effort"
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
- sysprom infer completeness # Score node completeness (0-1)
58
- sysprom infer lifecycle # Infer lifecycle phases
59
- sysprom infer impact I1 # Trace impact from node
60
- sysprom infer derived # Compute transitive closure
61
- sysprom infer all # Run all analyses
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 | Description |
94
- |------|-------------|
95
- | `validate` | Validate a SysProM document and return issues |
96
- | `stats` | Return document statistics |
97
- | `query-nodes` | Query nodes by type, status, or text |
98
- | `query-node` | Retrieve a single node by ID |
99
- | `query-relationships` | Query relationships by source, target, or type |
100
- | `trace` | Trace refinement chains from a node |
101
- | `add-node` | Add a new node to the document |
102
- | `remove-node` | Remove a node by ID |
103
- | `update-node` | Update fields on an existing node |
104
- | `add-relationship` | Add a relationship between nodes |
105
- | `remove-relationship` | Remove a relationship |
106
- | `infer-completeness` | Score node completeness (0-1) based on refinement relationships |
107
- | `infer-lifecycle` | Infer lifecycle phase from status and lifecycle fields |
108
- | `infer-impact` | Trace impact propagation from a starting node |
109
- | `infer-derived` | Compute transitive closure and inverse relationships |
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
- // Schema and types
118
- sysproMDocument,
119
- node,
120
- nodeType,
121
- relationshipType,
122
- type SysProMDocument,
123
- type Node,
124
- type Relationship,
125
-
126
- // Conversion
127
- jsonToMarkdown,
128
- jsonToMarkdownSingle,
129
- jsonToMarkdownMultiDoc,
130
- markdownToJson,
131
-
132
- // Validation and query
133
- validate,
134
- stats,
135
- queryNodes,
136
- queryNode,
137
- queryRelationships,
138
- traceFromNode,
139
-
140
- // Mutation
141
- addNode,
142
- removeNode,
143
- updateNode,
144
- addRelationship,
145
- removeRelationship,
146
- updateMetadata,
147
-
148
- // Inference
149
- inferCompletenessOp,
150
- inferLifecycleOp,
151
- inferImpactOp,
152
- inferDerivedOp,
153
-
154
- // File I/O
155
- loadDocument,
156
- saveDocument,
157
-
158
- // Utilities
159
- canonicalise,
160
- toJSONSchema,
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, { id: "INV23", type: "invariant", name: "New Rule" });
174
- const withRel = addRelationship(updated, { from: "D1", to: "INV23", type: "must_preserve" });
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)) { /* data is SysProMDocument */ }
178
- if (node.is(thing)) { /* thing is Node */ }
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
@@ -504,7 +504,8 @@
504
504
  "selects",
505
505
  "requires",
506
506
  "disables",
507
- "influence"
507
+ "influence",
508
+ "justifies"
508
509
  ],
509
510
  "type": "string"
510
511
  }
@@ -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
- if (opts.description) {
56
- node.description = opts.description;
57
- }
58
- if (opts.status) {
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.string().optional().describe("Filter by relationship type"),
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
- console.error(err instanceof Error ? err.message : String(err));
72
+ const message = err instanceof Error ? err.message : String(err);
73
+ console.error(message);
25
74
  process.exit(1);
26
75
  }
27
76
  },