sysprom 1.14.0 → 1.16.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 (61) hide show
  1. package/README.md +19 -2
  2. package/dist/schema.json +18 -1
  3. package/dist/src/cli/commands/infer.d.ts +2 -0
  4. package/dist/src/cli/commands/infer.js +235 -0
  5. package/dist/src/cli/program.js +2 -0
  6. package/dist/src/endpoint-types.js +23 -0
  7. package/dist/src/index.d.ts +2 -2
  8. package/dist/src/index.js +2 -2
  9. package/dist/src/mcp/server.js +112 -1
  10. package/dist/src/operations/add-node.d.ts +51 -9
  11. package/dist/src/operations/add-plan-task.d.ts +34 -6
  12. package/dist/src/operations/add-relationship.d.ts +48 -8
  13. package/dist/src/operations/check.d.ts +17 -3
  14. package/dist/src/operations/graph.d.ts +17 -3
  15. package/dist/src/operations/index.d.ts +4 -0
  16. package/dist/src/operations/index.js +5 -0
  17. package/dist/src/operations/infer-completeness.d.ts +428 -0
  18. package/dist/src/operations/infer-completeness.js +131 -0
  19. package/dist/src/operations/infer-derived.d.ts +389 -0
  20. package/dist/src/operations/infer-derived.js +158 -0
  21. package/dist/src/operations/infer-impact.d.ts +2299 -0
  22. package/dist/src/operations/infer-impact.js +262 -0
  23. package/dist/src/operations/infer-lifecycle.d.ts +435 -0
  24. package/dist/src/operations/infer-lifecycle.js +119 -0
  25. package/dist/src/operations/init-document.d.ts +17 -3
  26. package/dist/src/operations/json-to-markdown.d.ts +17 -3
  27. package/dist/src/operations/mark-task-done.d.ts +34 -6
  28. package/dist/src/operations/mark-task-undone.d.ts +34 -6
  29. package/dist/src/operations/markdown-to-json.d.ts +17 -3
  30. package/dist/src/operations/next-id.d.ts +17 -3
  31. package/dist/src/operations/node-history.d.ts +17 -3
  32. package/dist/src/operations/plan-add-task.d.ts +34 -6
  33. package/dist/src/operations/plan-gate.d.ts +17 -3
  34. package/dist/src/operations/plan-init.d.ts +17 -3
  35. package/dist/src/operations/plan-progress.d.ts +17 -3
  36. package/dist/src/operations/plan-status.d.ts +17 -3
  37. package/dist/src/operations/query-node.d.ts +107 -17
  38. package/dist/src/operations/query-nodes.d.ts +34 -6
  39. package/dist/src/operations/query-relationships.d.ts +31 -5
  40. package/dist/src/operations/remove-node.d.ts +51 -9
  41. package/dist/src/operations/remove-relationship.d.ts +36 -7
  42. package/dist/src/operations/rename.d.ts +34 -6
  43. package/dist/src/operations/search.d.ts +34 -6
  44. package/dist/src/operations/speckit-diff.d.ts +17 -3
  45. package/dist/src/operations/speckit-export.d.ts +17 -3
  46. package/dist/src/operations/speckit-import.d.ts +17 -3
  47. package/dist/src/operations/speckit-sync.d.ts +51 -9
  48. package/dist/src/operations/state-at.d.ts +17 -3
  49. package/dist/src/operations/stats.d.ts +17 -3
  50. package/dist/src/operations/sync.d.ts +51 -9
  51. package/dist/src/operations/task-list.d.ts +17 -3
  52. package/dist/src/operations/timeline.d.ts +17 -3
  53. package/dist/src/operations/trace-from-node.d.ts +51 -9
  54. package/dist/src/operations/update-metadata.d.ts +34 -6
  55. package/dist/src/operations/update-node.d.ts +48 -8
  56. package/dist/src/operations/update-plan-task.d.ts +34 -6
  57. package/dist/src/operations/validate.d.ts +17 -3
  58. package/dist/src/schema.d.ts +70 -10
  59. package/dist/src/schema.js +21 -0
  60. package/package.json +1 -1
  61. package/schema.json +18 -1
package/README.md CHANGED
@@ -52,13 +52,20 @@ sysprom update node D1 --status deprecated
52
52
  sysprom update add-rel D1 affects EL5
53
53
  sysprom update remove-rel D1 affects EL5
54
54
  sysprom update meta --fields version=2
55
+
56
+ # 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
55
62
  ```
56
63
 
57
64
  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.
58
65
 
59
66
  ## MCP Server
60
67
 
61
- SysProM includes an MCP (Model Context Protocol) server exposing 11 tools over stdio transport. Any MCP-compatible agent — Cursor, Windsurf, VS Code Copilot, Cline, or custom clients — can use it.
68
+ SysProM includes an MCP (Model Context Protocol) server exposing 15 tools over stdio transport. Any MCP-compatible agent — Cursor, Windsurf, VS Code Copilot, Cline, or custom clients — can use it.
62
69
 
63
70
  ### Configuration
64
71
 
@@ -96,6 +103,10 @@ sysprom mcp # starts the MCP server on stdio
96
103
  | `update-node` | Update fields on an existing node |
97
104
  | `add-relationship` | Add a relationship between nodes |
98
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 |
99
110
 
100
111
  All tools accept a `path` parameter to specify the SysProM document location.
101
112
 
@@ -134,6 +145,12 @@ import {
134
145
  removeRelationship,
135
146
  updateMetadata,
136
147
 
148
+ // Inference
149
+ inferCompletenessOp,
150
+ inferLifecycleOp,
151
+ inferImpactOp,
152
+ inferDerivedOp,
153
+
137
154
  // File I/O
138
155
  loadDocument,
139
156
  saveDocument,
@@ -212,7 +229,7 @@ SysProM models systems as directed graphs across abstraction layers — intent,
212
229
  <tr><td><a href="https://github.com/Priivacy-ai/spec-kitty">Spec Kitty</a></td><td>✅</td><td>🔶</td><td>✅</td><td>🔶</td><td></td><td>🔶</td><td>🔶</td><td>🔶</td><td></td><td></td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td></tr>
213
230
  <tr><td><a href="https://github.com/shotgun-sh/shotgun">Shotgun</a></td><td>✅</td><td>🔶</td><td>🔶</td><td></td><td></td><td>🔶</td><td></td><td>🔶</td><td></td><td></td><td>🔶</td><td>🔶</td><td>✅</td><td>🔶</td></tr>
214
231
  <tr><td><a href="https://github.com/obra/superpowers">Superpowers</a></td><td>✅</td><td>🔶</td><td>🔶</td><td>🔶</td><td></td><td>🔶</td><td>✅</td><td>🔶</td><td></td><td>✅</td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td></tr>
215
- <tr><td><strong>SysProM</strong></td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td></td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td></tr>
232
+ <tr><td><strong>SysProM</strong></td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>🔶</td><td>✅</td><td>✅</td><td>✅</td></tr>
216
233
  </tbody>
217
234
  </table>
218
235
 
package/dist/schema.json CHANGED
@@ -458,6 +458,22 @@
458
458
  "description": "Source node ID.",
459
459
  "type": "string"
460
460
  },
461
+ "polarity": {
462
+ "description": "Impact polarity — positive, negative, neutral, or uncertain.",
463
+ "enum": [
464
+ "positive",
465
+ "negative",
466
+ "neutral",
467
+ "uncertain"
468
+ ],
469
+ "type": "string"
470
+ },
471
+ "strength": {
472
+ "description": "Relationship strength as a number between 0 and 1.",
473
+ "maximum": 1,
474
+ "minimum": 0,
475
+ "type": "number"
476
+ },
461
477
  "to": {
462
478
  "description": "Target node ID.",
463
479
  "type": "string"
@@ -487,7 +503,8 @@
487
503
  "transforms_into",
488
504
  "selects",
489
505
  "requires",
490
- "disables"
506
+ "disables",
507
+ "influence"
491
508
  ],
492
509
  "type": "string"
493
510
  }
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const inferCommand: CommandDef;
@@ -0,0 +1,235 @@
1
+ import pc from "picocolors";
2
+ import * as z from "zod";
3
+ import { readOpts, loadDoc } from "../shared.js";
4
+ import { inferCompletenessOp, inferLifecycleOp, inferImpactOp, inferDerivedOp, } from "../../operations/index.js";
5
+ // ---------------------------------------------------------------------------
6
+ // Presentation helpers
7
+ // ---------------------------------------------------------------------------
8
+ function printCompletenessNode(r) {
9
+ const scoreColour = r.score === 1 ? pc.green : r.score >= 0.5 ? pc.yellow : pc.red;
10
+ console.log(`${pc.cyan(r.id.padEnd(12))} ${pc.dim(r.type.padEnd(16))} ${pc.bold(r.name)} ${scoreColour(`[${(r.score * 100).toFixed(0)}%]`)}`);
11
+ for (const issue of r.issues) {
12
+ console.log(` ${pc.dim("•")} ${pc.red(issue)}`);
13
+ }
14
+ }
15
+ function printLifecycleNode(r) {
16
+ const phaseColours = {
17
+ early: pc.blue,
18
+ middle: pc.yellow,
19
+ late: pc.green,
20
+ terminal: pc.red,
21
+ unknown: pc.dim,
22
+ };
23
+ const colour = phaseColours[r.inferredPhase] ?? pc.dim;
24
+ console.log(`${pc.cyan(r.id.padEnd(12))} ${pc.dim(r.type.padEnd(16))} ${pc.bold(r.name)} ${colour(`[${r.inferredPhase}]`)} ${pc.dim(r.inferredState)}`);
25
+ }
26
+ function printImpactNode(r) {
27
+ const typeColours = {
28
+ direct: pc.red,
29
+ transitive: pc.yellow,
30
+ potential: pc.blue,
31
+ };
32
+ const colour = typeColours[r.impactType] ?? pc.dim;
33
+ const nodeName = r.node ? r.node.name : "(unknown)";
34
+ const indent = " ".repeat(r.distance);
35
+ console.log(`${indent}${pc.cyan(r.id)} ${pc.dim(`(${String(r.distance)})`)} ${colour(`[${r.impactType}]`)} ${pc.bold(nodeName)}`);
36
+ }
37
+ function printDerivedRelationship(r) {
38
+ const typeColours = {
39
+ transitive: pc.yellow,
40
+ composite: pc.blue,
41
+ inverse: pc.green,
42
+ };
43
+ const colour = typeColours[r.derivationType] ?? pc.dim;
44
+ console.log(`${pc.cyan(r.from.padEnd(12))} ${colour(r.type.padEnd(20))} ${pc.cyan(r.to)} ${pc.dim(`[${r.derivationType}]`)}`);
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // Arg/opt schemas
48
+ // ---------------------------------------------------------------------------
49
+ const impactArgs = z.object({
50
+ id: z.string().describe("node ID to start impact analysis from"),
51
+ direction: z
52
+ .enum(["outgoing", "incoming", "bidirectional"])
53
+ .optional()
54
+ .describe("traversal direction: outgoing (default) | incoming | bidirectional"),
55
+ maxDepth: z
56
+ .number()
57
+ .int()
58
+ .positive()
59
+ .optional()
60
+ .describe("maximum traversal depth"),
61
+ filter: z
62
+ .string()
63
+ .optional()
64
+ .describe("comma-separated list of relationship types to follow"),
65
+ });
66
+ // ---------------------------------------------------------------------------
67
+ // Subcommands
68
+ // ---------------------------------------------------------------------------
69
+ const completenessSubcommand = {
70
+ name: "completeness",
71
+ description: inferCompletenessOp.def.description,
72
+ apiLink: inferCompletenessOp.def.name,
73
+ opts: readOpts,
74
+ action(_rawArgs, rawOpts) {
75
+ const opts = readOpts.parse(rawOpts);
76
+ const { doc } = loadDoc(opts.path);
77
+ const result = inferCompletenessOp({ doc });
78
+ if (opts.json) {
79
+ console.log(JSON.stringify(result, null, 2));
80
+ }
81
+ else {
82
+ console.log(pc.bold("\nCompleteness Analysis\n") +
83
+ pc.dim(`Average score: ${(result.averageScore * 100).toFixed(1)}% | `) +
84
+ pc.green(`${String(result.completeNodes)} complete`) +
85
+ pc.dim(" | ") +
86
+ pc.red(`${String(result.incompleteNodes)} incomplete`) +
87
+ "\n");
88
+ // Show incomplete nodes first
89
+ const incomplete = result.nodes.filter((n) => n.score < 1);
90
+ const complete = result.nodes.filter((n) => n.score === 1);
91
+ if (incomplete.length > 0) {
92
+ console.log(pc.dim("Incomplete nodes:"));
93
+ for (const n of incomplete)
94
+ printCompletenessNode(n);
95
+ console.log();
96
+ }
97
+ console.log(pc.dim(`${String(complete.length)} fully complete nodes`));
98
+ }
99
+ },
100
+ };
101
+ const lifecycleSubcommand = {
102
+ name: "lifecycle",
103
+ description: inferLifecycleOp.def.description,
104
+ apiLink: inferLifecycleOp.def.name,
105
+ opts: readOpts,
106
+ action(_rawArgs, rawOpts) {
107
+ const opts = readOpts.parse(rawOpts);
108
+ const { doc } = loadDoc(opts.path);
109
+ const result = inferLifecycleOp({ doc });
110
+ if (opts.json) {
111
+ console.log(JSON.stringify(result, null, 2));
112
+ }
113
+ else {
114
+ console.log(pc.bold("\nLifecycle Analysis\n") +
115
+ pc.dim(`Early: ${String(result.summary.early)} | Middle: ${String(result.summary.middle)} | Late: ${String(result.summary.late)} | Terminal: ${String(result.summary.terminal)} | Unknown: ${String(result.summary.unknown)}`) +
116
+ "\n");
117
+ for (const n of result.nodes)
118
+ printLifecycleNode(n);
119
+ }
120
+ },
121
+ };
122
+ const impactSubcommand = {
123
+ name: "impact",
124
+ description: inferImpactOp.def.description,
125
+ apiLink: inferImpactOp.def.name,
126
+ args: impactArgs,
127
+ opts: readOpts,
128
+ action(rawArgs, rawOpts) {
129
+ const args = impactArgs.parse(rawArgs);
130
+ const opts = readOpts.parse(rawOpts);
131
+ const { doc } = loadDoc(opts.path);
132
+ // Parse relationship filter if provided
133
+ const relationshipFilter = args.filter
134
+ ? args.filter.split(",").map((s) => s.trim())
135
+ : undefined;
136
+ const result = inferImpactOp({
137
+ doc,
138
+ startId: args.id,
139
+ direction: args.direction,
140
+ maxDepth: args.maxDepth,
141
+ relationshipFilter,
142
+ });
143
+ if (opts.json) {
144
+ console.log(JSON.stringify(result, null, 2));
145
+ }
146
+ else {
147
+ const directionLabel = args.direction ? ` (${args.direction})` : "";
148
+ const depthLabel = args.maxDepth
149
+ ? ` [depth: ${String(args.maxDepth)}]`
150
+ : "";
151
+ const filterLabel = args.filter ? ` [filter: ${args.filter}]` : "";
152
+ console.log(pc.bold(`\nImpact Analysis from ${args.id}${directionLabel}${depthLabel}${filterLabel}\n`) +
153
+ pc.dim(`Direct: ${String(result.summary.direct)} | Transitive: ${String(result.summary.transitive)} | Potential: ${String(result.summary.potential)} | Total: ${String(result.summary.total)}`) +
154
+ "\n");
155
+ if (result.impactedNodes.length === 0) {
156
+ console.log(pc.dim("No impacted nodes found"));
157
+ }
158
+ else {
159
+ for (const n of result.impactedNodes)
160
+ printImpactNode(n);
161
+ }
162
+ }
163
+ },
164
+ };
165
+ const derivedSubcommand = {
166
+ name: "derived",
167
+ description: inferDerivedOp.def.description,
168
+ apiLink: inferDerivedOp.def.name,
169
+ opts: readOpts,
170
+ action(_rawArgs, rawOpts) {
171
+ const opts = readOpts.parse(rawOpts);
172
+ const { doc } = loadDoc(opts.path);
173
+ const result = inferDerivedOp({ doc });
174
+ if (opts.json) {
175
+ console.log(JSON.stringify(result, null, 2));
176
+ }
177
+ else {
178
+ console.log(pc.bold("\nDerived Relationships\n") +
179
+ pc.dim(`Transitive: ${String(result.summary.transitive)} | Composite: ${String(result.summary.composite)} | Inverse: ${String(result.summary.inverse)} | Total: ${String(result.summary.total)}`) +
180
+ "\n");
181
+ if (result.derivedRelationships.length === 0) {
182
+ console.log(pc.dim("No derived relationships found"));
183
+ }
184
+ else {
185
+ for (const r of result.derivedRelationships)
186
+ printDerivedRelationship(r);
187
+ }
188
+ }
189
+ },
190
+ };
191
+ const allSubcommand = {
192
+ name: "all",
193
+ description: "Run all inference analyses",
194
+ opts: readOpts,
195
+ action(_rawArgs, rawOpts) {
196
+ const opts = readOpts.parse(rawOpts);
197
+ const { doc } = loadDoc(opts.path);
198
+ // Completeness
199
+ console.log(pc.bold("\n=== Completeness ===\n"));
200
+ const completeness = inferCompletenessOp({ doc });
201
+ console.log(pc.dim(`Average score: ${(completeness.averageScore * 100).toFixed(1)}% | `) +
202
+ pc.green(`${String(completeness.completeNodes)} complete`) +
203
+ pc.dim(" | ") +
204
+ pc.red(`${String(completeness.incompleteNodes)} incomplete`));
205
+ // Lifecycle
206
+ console.log(pc.bold("\n=== Lifecycle ===\n"));
207
+ const lifecycle = inferLifecycleOp({ doc });
208
+ console.log(pc.dim(`Early: ${String(lifecycle.summary.early)} | Middle: ${String(lifecycle.summary.middle)} | Late: ${String(lifecycle.summary.late)} | Terminal: ${String(lifecycle.summary.terminal)} | Unknown: ${String(lifecycle.summary.unknown)}`));
209
+ // Derived
210
+ console.log(pc.bold("\n=== Derived Relationships ===\n"));
211
+ const derived = inferDerivedOp({ doc });
212
+ console.log(pc.dim(`Transitive: ${String(derived.summary.transitive)} | Composite: ${String(derived.summary.composite)} | Inverse: ${String(derived.summary.inverse)} | Total: ${String(derived.summary.total)}`));
213
+ if (opts.json) {
214
+ console.log(JSON.stringify({
215
+ completeness,
216
+ lifecycle,
217
+ derived,
218
+ }, null, 2));
219
+ }
220
+ },
221
+ };
222
+ // ---------------------------------------------------------------------------
223
+ // Main command
224
+ // ---------------------------------------------------------------------------
225
+ export const inferCommand = {
226
+ name: "infer",
227
+ description: "Infer completeness, lifecycle, impact, and derived relationships",
228
+ subcommands: [
229
+ completenessSubcommand,
230
+ lifecycleSubcommand,
231
+ impactSubcommand,
232
+ derivedSubcommand,
233
+ allSubcommand,
234
+ ],
235
+ };
@@ -43,6 +43,7 @@ import { speckitCommand } from "./commands/speckit.js";
43
43
  import { taskCommand } from "./commands/task.js";
44
44
  import { planCommand } from "./commands/plan.js";
45
45
  import { syncCommandDef } from "./commands/sync.js";
46
+ import { inferCommand } from "./commands/infer.js";
46
47
  export const program = new Command();
47
48
  program
48
49
  .name("sysprom")
@@ -71,6 +72,7 @@ export const commands = [
71
72
  taskCommand,
72
73
  planCommand,
73
74
  syncCommandDef,
75
+ inferCommand,
74
76
  ];
75
77
  for (const cmd of commands) {
76
78
  buildCommander(cmd, program);
@@ -286,6 +286,29 @@ export const RELATIONSHIP_ENDPOINT_TYPES = {
286
286
  from: ["decision", "mode"],
287
287
  to: ["capability", "stage"],
288
288
  },
289
+ // Influence — soft dependency between decisions and across nodes (CHG40)
290
+ influence: {
291
+ from: [
292
+ "intent",
293
+ "concept",
294
+ "capability",
295
+ "element",
296
+ "realisation",
297
+ "decision",
298
+ "change",
299
+ "stage",
300
+ ],
301
+ to: [
302
+ "intent",
303
+ "concept",
304
+ "capability",
305
+ "element",
306
+ "realisation",
307
+ "decision",
308
+ "change",
309
+ "stage",
310
+ ],
311
+ },
289
312
  };
290
313
  /**
291
314
  * Check if a relationship type is valid for the given endpoint node types.
@@ -5,8 +5,8 @@
5
5
  * system came from, what decisions shaped it, and how it reached its current form.
6
6
  * @packageDocumentation
7
7
  */
8
- export { SysProMDocument, Node, Relationship, NodeType, NodeStatus, RelationshipType, Text, Option, Operation, Task, ExternalReference, ExternalReferenceRole, Metadata, NODE_TYPE_LABELS, NODE_LABEL_TO_TYPE, RELATIONSHIP_TYPE_LABELS, RELATIONSHIP_LABEL_TO_TYPE, EXTERNAL_REFERENCE_ROLE_LABELS, EXTERNAL_REFERENCE_LABEL_TO_ROLE, NODE_STATUSES, NODE_FILE_MAP, NODE_ID_PREFIX, toJSONSchema, } from "./schema.js";
9
- export { defineOperation, type OperationDef, type DefinedOperation, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, updateMetadataOp, nextIdOp, initDocumentOp, addPlanTaskOp, updatePlanTaskOp, markTaskDoneOp, markTaskUndoneOp, taskListOp, planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, timelineOp, nodeHistoryOp, stateAtOp, validateOp, statsOp, searchOp, checkOp, graphOp, renameOp, jsonToMarkdownOp, markdownToJsonOp, speckitImportOp, speckitExportOp, speckitSyncOp, speckitDiffOp, type RemoveResult, type ValidationResult, type DocumentStats, type NodeDetail, type TraceNode, type TimelineEvent, type NodeState, type PlanStatusResult, type PhaseProgressResult, type GateResultOutput, type SyncResult, type DiffResult, } from "./operations/index.js";
8
+ export { SysProMDocument, Node, Relationship, NodeType, NodeStatus, RelationshipType, ImpactPolarity, Text, Option, Operation, Task, ExternalReference, ExternalReferenceRole, Metadata, NODE_TYPE_LABELS, NODE_LABEL_TO_TYPE, RELATIONSHIP_TYPE_LABELS, RELATIONSHIP_LABEL_TO_TYPE, IMPACT_POLARITY_LABELS, EXTERNAL_REFERENCE_ROLE_LABELS, EXTERNAL_REFERENCE_LABEL_TO_ROLE, NODE_STATUSES, NODE_FILE_MAP, NODE_ID_PREFIX, toJSONSchema, } from "./schema.js";
9
+ export { defineOperation, type OperationDef, type DefinedOperation, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, updateMetadataOp, nextIdOp, initDocumentOp, addPlanTaskOp, updatePlanTaskOp, markTaskDoneOp, markTaskUndoneOp, taskListOp, planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, timelineOp, nodeHistoryOp, stateAtOp, validateOp, statsOp, searchOp, checkOp, graphOp, renameOp, jsonToMarkdownOp, markdownToJsonOp, speckitImportOp, speckitExportOp, speckitSyncOp, speckitDiffOp, inferCompletenessOp, inferLifecycleOp, inferImpactOp, impactSummaryOp, inferDerivedOp, type RemoveResult, type ValidationResult, type DocumentStats, type NodeDetail, type TraceNode, type TimelineEvent, type NodeState, type PlanStatusResult, type PhaseProgressResult, type GateResultOutput, type SyncResult, type DiffResult, type CompletenessOutput, type LifecycleOutput, type ImpactOutput, type ImpactSummaryOutput, type DerivedOutput, } from "./operations/index.js";
10
10
  export { jsonToMarkdownSingle, jsonToMarkdownMultiDoc, jsonToMarkdown, type ConvertOptions, } from "./json-to-md.js";
11
11
  export { markdownSingleToJson, markdownMultiDocToJson, markdownToJson, } from "./md-to-json.js";
12
12
  export { RELATIONSHIP_ENDPOINT_TYPES, isValidEndpointPair, } from "./endpoint-types.js";
package/dist/src/index.js CHANGED
@@ -6,9 +6,9 @@
6
6
  * @packageDocumentation
7
7
  */
8
8
  // Schema types and validators
9
- export { SysProMDocument, Node, Relationship, NodeType, NodeStatus, RelationshipType, Text, Option, Operation, Task, ExternalReference, ExternalReferenceRole, Metadata, NODE_TYPE_LABELS, NODE_LABEL_TO_TYPE, RELATIONSHIP_TYPE_LABELS, RELATIONSHIP_LABEL_TO_TYPE, EXTERNAL_REFERENCE_ROLE_LABELS, EXTERNAL_REFERENCE_LABEL_TO_ROLE, NODE_STATUSES, NODE_FILE_MAP, NODE_ID_PREFIX, toJSONSchema, } from "./schema.js";
9
+ export { SysProMDocument, Node, Relationship, NodeType, NodeStatus, RelationshipType, ImpactPolarity, Text, Option, Operation, Task, ExternalReference, ExternalReferenceRole, Metadata, NODE_TYPE_LABELS, NODE_LABEL_TO_TYPE, RELATIONSHIP_TYPE_LABELS, RELATIONSHIP_LABEL_TO_TYPE, IMPACT_POLARITY_LABELS, EXTERNAL_REFERENCE_ROLE_LABELS, EXTERNAL_REFERENCE_LABEL_TO_ROLE, NODE_STATUSES, NODE_FILE_MAP, NODE_ID_PREFIX, toJSONSchema, } from "./schema.js";
10
10
  // Operations (single source of truth for domain logic + metadata)
11
- export { defineOperation, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, updateMetadataOp, nextIdOp, initDocumentOp, addPlanTaskOp, updatePlanTaskOp, markTaskDoneOp, markTaskUndoneOp, taskListOp, planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, timelineOp, nodeHistoryOp, stateAtOp, validateOp, statsOp, searchOp, checkOp, graphOp, renameOp, jsonToMarkdownOp, markdownToJsonOp, speckitImportOp, speckitExportOp, speckitSyncOp, speckitDiffOp, } from "./operations/index.js";
11
+ export { defineOperation, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, updateMetadataOp, nextIdOp, initDocumentOp, addPlanTaskOp, updatePlanTaskOp, markTaskDoneOp, markTaskUndoneOp, taskListOp, planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, timelineOp, nodeHistoryOp, stateAtOp, validateOp, statsOp, searchOp, checkOp, graphOp, renameOp, jsonToMarkdownOp, markdownToJsonOp, speckitImportOp, speckitExportOp, speckitSyncOp, speckitDiffOp, inferCompletenessOp, inferLifecycleOp, inferImpactOp, impactSummaryOp, inferDerivedOp, } from "./operations/index.js";
12
12
  // Conversion
13
13
  export { jsonToMarkdownSingle, jsonToMarkdownMultiDoc, jsonToMarkdown, } from "./json-to-md.js";
14
14
  export { markdownSingleToJson, markdownMultiDocToJson, markdownToJson, } from "./md-to-json.js";
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import * as z from "zod";
5
5
  import { loadDocument, saveDocument } from "../io.js";
6
6
  import { NodeType, RelationshipType } from "../schema.js";
7
- import { validateOp, statsOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, nextIdOp, } from "../operations/index.js";
7
+ import { validateOp, statsOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, nextIdOp, inferCompletenessOp, inferLifecycleOp, inferImpactOp, impactSummaryOp, inferDerivedOp, } from "../operations/index.js";
8
8
  // Create MCP server instance
9
9
  const server = new McpServer({
10
10
  name: "sysprom-mcp",
@@ -318,6 +318,117 @@ server.registerTool("remove-relationship", {
318
318
  ],
319
319
  };
320
320
  });
321
+ // Register infer-completeness tool
322
+ server.registerTool("infer-completeness", {
323
+ description: "Infer completeness of nodes based on expected refinement relationships",
324
+ inputSchema: z.object({
325
+ path: z.string().describe("Path to SysProM file"),
326
+ }),
327
+ }, ({ path }) => {
328
+ const { doc } = loadDocument(path);
329
+ const result = inferCompletenessOp({ doc });
330
+ return {
331
+ content: [
332
+ {
333
+ type: "text",
334
+ text: JSON.stringify(result, null, 2),
335
+ },
336
+ ],
337
+ };
338
+ });
339
+ // Register infer-lifecycle tool
340
+ server.registerTool("infer-lifecycle", {
341
+ description: "Infer lifecycle state for nodes based on status and lifecycle fields",
342
+ inputSchema: z.object({
343
+ path: z.string().describe("Path to SysProM file"),
344
+ }),
345
+ }, ({ path }) => {
346
+ const { doc } = loadDocument(path);
347
+ const result = inferLifecycleOp({ doc });
348
+ return {
349
+ content: [
350
+ {
351
+ type: "text",
352
+ text: JSON.stringify(result, null, 2),
353
+ },
354
+ ],
355
+ };
356
+ });
357
+ // Register infer-impact tool
358
+ server.registerTool("infer-impact", {
359
+ description: "Infer impact from a node through the graph following impact relationships (CHG40 with bidirectional traversal)",
360
+ inputSchema: z.object({
361
+ path: z.string().describe("Path to SysProM file"),
362
+ startId: z.string().describe("Node ID to start impact analysis from"),
363
+ direction: z
364
+ .enum(["outgoing", "incoming", "bidirectional"])
365
+ .optional()
366
+ .describe("Traversal direction (default: outgoing)"),
367
+ maxDepth: z
368
+ .number()
369
+ .int()
370
+ .positive()
371
+ .optional()
372
+ .describe("Maximum traversal depth"),
373
+ relationshipFilter: z
374
+ .array(z.string())
375
+ .optional()
376
+ .describe("Relationship types to follow"),
377
+ }),
378
+ }, ({ path, startId, direction, maxDepth, relationshipFilter }) => {
379
+ const { doc } = loadDocument(path);
380
+ const result = inferImpactOp({
381
+ doc,
382
+ startId,
383
+ direction,
384
+ maxDepth,
385
+ relationshipFilter,
386
+ });
387
+ return {
388
+ content: [
389
+ {
390
+ type: "text",
391
+ text: JSON.stringify(result, null, 2),
392
+ },
393
+ ],
394
+ };
395
+ });
396
+ // Register infer-impact-summary tool (CHG40 hotspot analysis)
397
+ server.registerTool("infer-impact-summary", {
398
+ description: "Analyse document for impact hotspots — nodes with high incoming/outgoing impact (CHG40)",
399
+ inputSchema: z.object({
400
+ path: z.string().describe("Path to SysProM file"),
401
+ }),
402
+ }, ({ path }) => {
403
+ const { doc } = loadDocument(path);
404
+ const result = impactSummaryOp({ doc });
405
+ return {
406
+ content: [
407
+ {
408
+ type: "text",
409
+ text: JSON.stringify(result, null, 2),
410
+ },
411
+ ],
412
+ };
413
+ });
414
+ // Register infer-derived tool
415
+ server.registerTool("infer-derived", {
416
+ description: "Infer derived relationships from transitive closure, inverses, and composites",
417
+ inputSchema: z.object({
418
+ path: z.string().describe("Path to SysProM file"),
419
+ }),
420
+ }, ({ path }) => {
421
+ const { doc } = loadDocument(path);
422
+ const result = inferDerivedOp({ doc });
423
+ return {
424
+ content: [
425
+ {
426
+ type: "text",
427
+ text: JSON.stringify(result, null, 2),
428
+ },
429
+ ],
430
+ };
431
+ });
321
432
  // Start server
322
433
  async function main() {
323
434
  const transport = new StdioServerTransport();