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.
- package/README.md +19 -2
- package/dist/schema.json +18 -1
- package/dist/src/cli/commands/infer.d.ts +2 -0
- package/dist/src/cli/commands/infer.js +235 -0
- package/dist/src/cli/program.js +2 -0
- package/dist/src/endpoint-types.js +23 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/mcp/server.js +112 -1
- package/dist/src/operations/add-node.d.ts +51 -9
- package/dist/src/operations/add-plan-task.d.ts +34 -6
- package/dist/src/operations/add-relationship.d.ts +48 -8
- package/dist/src/operations/check.d.ts +17 -3
- package/dist/src/operations/graph.d.ts +17 -3
- package/dist/src/operations/index.d.ts +4 -0
- package/dist/src/operations/index.js +5 -0
- package/dist/src/operations/infer-completeness.d.ts +428 -0
- package/dist/src/operations/infer-completeness.js +131 -0
- package/dist/src/operations/infer-derived.d.ts +389 -0
- package/dist/src/operations/infer-derived.js +158 -0
- package/dist/src/operations/infer-impact.d.ts +2299 -0
- package/dist/src/operations/infer-impact.js +262 -0
- package/dist/src/operations/infer-lifecycle.d.ts +435 -0
- package/dist/src/operations/infer-lifecycle.js +119 -0
- package/dist/src/operations/init-document.d.ts +17 -3
- package/dist/src/operations/json-to-markdown.d.ts +17 -3
- package/dist/src/operations/mark-task-done.d.ts +34 -6
- package/dist/src/operations/mark-task-undone.d.ts +34 -6
- package/dist/src/operations/markdown-to-json.d.ts +17 -3
- package/dist/src/operations/next-id.d.ts +17 -3
- package/dist/src/operations/node-history.d.ts +17 -3
- package/dist/src/operations/plan-add-task.d.ts +34 -6
- package/dist/src/operations/plan-gate.d.ts +17 -3
- package/dist/src/operations/plan-init.d.ts +17 -3
- package/dist/src/operations/plan-progress.d.ts +17 -3
- package/dist/src/operations/plan-status.d.ts +17 -3
- package/dist/src/operations/query-node.d.ts +107 -17
- package/dist/src/operations/query-nodes.d.ts +34 -6
- package/dist/src/operations/query-relationships.d.ts +31 -5
- package/dist/src/operations/remove-node.d.ts +51 -9
- package/dist/src/operations/remove-relationship.d.ts +36 -7
- package/dist/src/operations/rename.d.ts +34 -6
- package/dist/src/operations/search.d.ts +34 -6
- package/dist/src/operations/speckit-diff.d.ts +17 -3
- package/dist/src/operations/speckit-export.d.ts +17 -3
- package/dist/src/operations/speckit-import.d.ts +17 -3
- package/dist/src/operations/speckit-sync.d.ts +51 -9
- package/dist/src/operations/state-at.d.ts +17 -3
- package/dist/src/operations/stats.d.ts +17 -3
- package/dist/src/operations/sync.d.ts +51 -9
- package/dist/src/operations/task-list.d.ts +17 -3
- package/dist/src/operations/timeline.d.ts +17 -3
- package/dist/src/operations/trace-from-node.d.ts +51 -9
- package/dist/src/operations/update-metadata.d.ts +34 -6
- package/dist/src/operations/update-node.d.ts +48 -8
- package/dist/src/operations/update-plan-task.d.ts +34 -6
- package/dist/src/operations/validate.d.ts +17 -3
- package/dist/src/schema.d.ts +70 -10
- package/dist/src/schema.js +21 -0
- package/package.json +1 -1
- 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
|
|
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
|
|
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,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
|
+
};
|
package/dist/src/cli/program.js
CHANGED
|
@@ -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.
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -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();
|