sysprom 1.6.0 → 1.7.1
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/dist/src/endpoint-types.d.ts +7 -3
- package/dist/src/endpoint-types.js +8 -8
- package/dist/src/mcp/index.d.ts +2 -0
- package/dist/src/mcp/index.js +3 -0
- package/dist/src/mcp/server.d.ts +2 -0
- package/dist/src/mcp/server.js +348 -0
- package/dist/src/md-to-json.js +9 -1
- package/dist/src/sync.d.ts +12 -9
- package/dist/src/sync.js +19 -12
- package/package.json +5 -2
|
@@ -12,9 +12,13 @@ export declare const RELATIONSHIP_ENDPOINT_TYPES: Record<RelationshipType, {
|
|
|
12
12
|
}>;
|
|
13
13
|
/**
|
|
14
14
|
* Check if a relationship type is valid for the given endpoint node types.
|
|
15
|
-
* @param relType The relationship type
|
|
16
|
-
* @param fromType The source node type
|
|
17
|
-
* @param toType The target node type
|
|
15
|
+
* @param relType - The relationship type
|
|
16
|
+
* @param fromType - The source node type
|
|
17
|
+
* @param toType - The target node type
|
|
18
18
|
* @returns true if the endpoint types are valid for this relationship
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* isValidEndpointPair("refines", "intent", "concept") // true
|
|
22
|
+
* ```
|
|
19
23
|
*/
|
|
20
24
|
export declare function isValidEndpointPair(relType: RelationshipType, fromType: NodeType, toType: NodeType): boolean;
|
|
@@ -288,16 +288,16 @@ export const RELATIONSHIP_ENDPOINT_TYPES = {
|
|
|
288
288
|
};
|
|
289
289
|
/**
|
|
290
290
|
* Check if a relationship type is valid for the given endpoint node types.
|
|
291
|
-
* @param relType The relationship type
|
|
292
|
-
* @param fromType The source node type
|
|
293
|
-
* @param toType The target node type
|
|
291
|
+
* @param relType - The relationship type
|
|
292
|
+
* @param fromType - The source node type
|
|
293
|
+
* @param toType - The target node type
|
|
294
294
|
* @returns true if the endpoint types are valid for this relationship
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* isValidEndpointPair("refines", "intent", "concept") // true
|
|
298
|
+
* ```
|
|
295
299
|
*/
|
|
296
300
|
export function isValidEndpointPair(relType, fromType, toType) {
|
|
297
301
|
const endpoints = RELATIONSHIP_ENDPOINT_TYPES[relType];
|
|
298
|
-
|
|
299
|
-
// Unknown relationship type — should be caught by schema validation
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
return (endpoints.from.includes(fromType) && endpoints.to.includes(toType));
|
|
302
|
+
return endpoints.from.includes(fromType) && endpoints.to.includes(toType);
|
|
303
303
|
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { loadDocument } from "../io.js";
|
|
6
|
+
import { RelationshipType } from "../schema.js";
|
|
7
|
+
import { validateOp, statsOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, nextIdOp, } from "../operations/index.js";
|
|
8
|
+
// Create MCP server instance
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: "sysprom-mcp",
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
});
|
|
13
|
+
// Register validate tool
|
|
14
|
+
server.registerTool("validate", {
|
|
15
|
+
description: "Validate a SysProM document and return any validation issues",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
path: z.string().describe("Path to SysProM file"),
|
|
18
|
+
}),
|
|
19
|
+
}, ({ path }) => {
|
|
20
|
+
const { doc } = loadDocument(path);
|
|
21
|
+
const result = validateOp({ doc });
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: JSON.stringify(result, null, 2),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
// Register stats tool
|
|
32
|
+
server.registerTool("stats", {
|
|
33
|
+
description: "Get statistics about a SysProM document",
|
|
34
|
+
inputSchema: z.object({
|
|
35
|
+
path: z.string().describe("Path to SysProM file"),
|
|
36
|
+
}),
|
|
37
|
+
}, ({ path }) => {
|
|
38
|
+
const { doc } = loadDocument(path);
|
|
39
|
+
const result = statsOp({ doc });
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: JSON.stringify(result, null, 2),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
// Register query-nodes tool
|
|
50
|
+
server.registerTool("query-nodes", {
|
|
51
|
+
description: "Query nodes by type, status, or other criteria",
|
|
52
|
+
inputSchema: z.object({
|
|
53
|
+
path: z.string().describe("Path to SysProM file"),
|
|
54
|
+
type: z.string().optional().describe("Filter by node type"),
|
|
55
|
+
status: z.string().optional().describe("Filter by node status"),
|
|
56
|
+
}),
|
|
57
|
+
}, ({ path, type, status }) => {
|
|
58
|
+
const { doc } = loadDocument(path);
|
|
59
|
+
const results = queryNodesOp({
|
|
60
|
+
doc,
|
|
61
|
+
type,
|
|
62
|
+
status,
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: JSON.stringify(results, null, 2),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
// Register query-node tool
|
|
74
|
+
server.registerTool("query-node", {
|
|
75
|
+
description: "Get a specific node by ID",
|
|
76
|
+
inputSchema: z.object({
|
|
77
|
+
path: z.string().describe("Path to SysProM file"),
|
|
78
|
+
id: z.string().describe("Node ID"),
|
|
79
|
+
}),
|
|
80
|
+
}, ({ path, id }) => {
|
|
81
|
+
const { doc } = loadDocument(path);
|
|
82
|
+
const result = queryNodeOp({ doc, id });
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: JSON.stringify(result, null, 2),
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
// Register query-relationships tool
|
|
93
|
+
server.registerTool("query-relationships", {
|
|
94
|
+
description: "Query relationships by source, target, or type",
|
|
95
|
+
inputSchema: z.object({
|
|
96
|
+
path: z.string().describe("Path to SysProM file"),
|
|
97
|
+
from: z.string().optional().describe("Filter by source node ID"),
|
|
98
|
+
to: z.string().optional().describe("Filter by target node ID"),
|
|
99
|
+
type: z.string().optional().describe("Filter by relationship type"),
|
|
100
|
+
}),
|
|
101
|
+
}, ({ path, from, to, type }) => {
|
|
102
|
+
const { doc } = loadDocument(path);
|
|
103
|
+
const results = queryRelationshipsOp({
|
|
104
|
+
doc,
|
|
105
|
+
from,
|
|
106
|
+
to,
|
|
107
|
+
type,
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: JSON.stringify(results, null, 2),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
// Register trace tool
|
|
119
|
+
server.registerTool("trace", {
|
|
120
|
+
description: "Trace impacts from a node through the graph",
|
|
121
|
+
inputSchema: z.object({
|
|
122
|
+
path: z.string().describe("Path to SysProM file"),
|
|
123
|
+
from: z.string().describe("Starting node ID"),
|
|
124
|
+
}),
|
|
125
|
+
}, ({ path, from }) => {
|
|
126
|
+
const { doc } = loadDocument(path);
|
|
127
|
+
const result = traceFromNodeOp({ doc, startId: from });
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: JSON.stringify(result, null, 2),
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
// Register add-node tool
|
|
138
|
+
server.registerTool("add-node", {
|
|
139
|
+
description: "Add a new node to the SysProM document",
|
|
140
|
+
inputSchema: z.object({
|
|
141
|
+
path: z.string().describe("Path to SysProM file"),
|
|
142
|
+
type: z.string().describe("Node type"),
|
|
143
|
+
id: z.string().optional().describe("Node ID (auto-generated if omitted)"),
|
|
144
|
+
name: z.string().describe("Node name"),
|
|
145
|
+
description: z.string().optional().describe("Node description"),
|
|
146
|
+
}),
|
|
147
|
+
}, ({ path, type, id, name, description }) => {
|
|
148
|
+
const { doc } = loadDocument(path);
|
|
149
|
+
const nodeType = z
|
|
150
|
+
.enum([
|
|
151
|
+
"intent",
|
|
152
|
+
"concept",
|
|
153
|
+
"capability",
|
|
154
|
+
"element",
|
|
155
|
+
"realisation",
|
|
156
|
+
"invariant",
|
|
157
|
+
"principle",
|
|
158
|
+
"policy",
|
|
159
|
+
"protocol",
|
|
160
|
+
"stage",
|
|
161
|
+
"role",
|
|
162
|
+
"gate",
|
|
163
|
+
"mode",
|
|
164
|
+
"artefact",
|
|
165
|
+
"artefact_flow",
|
|
166
|
+
"decision",
|
|
167
|
+
"change",
|
|
168
|
+
"view",
|
|
169
|
+
"version",
|
|
170
|
+
])
|
|
171
|
+
.safeParse(type);
|
|
172
|
+
if (!nodeType.success) {
|
|
173
|
+
throw new Error(`Invalid node type: ${type}`);
|
|
174
|
+
}
|
|
175
|
+
const nodeId = id ?? nextIdOp({ doc, type: nodeType.data });
|
|
176
|
+
const updated = addNodeOp({
|
|
177
|
+
doc,
|
|
178
|
+
node: {
|
|
179
|
+
id: nodeId,
|
|
180
|
+
type: nodeType.data,
|
|
181
|
+
name,
|
|
182
|
+
...(description && { description }),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: JSON.stringify({
|
|
190
|
+
message: "Node added",
|
|
191
|
+
id: nodeId,
|
|
192
|
+
nodeCount: updated.nodes.length,
|
|
193
|
+
}, null, 2),
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
// Register remove-node tool
|
|
199
|
+
server.registerTool("remove-node", {
|
|
200
|
+
description: "Remove a node from the SysProM document",
|
|
201
|
+
inputSchema: z.object({
|
|
202
|
+
path: z.string().describe("Path to SysProM file"),
|
|
203
|
+
id: z.string().describe("Node ID"),
|
|
204
|
+
}),
|
|
205
|
+
}, ({ path, id }) => {
|
|
206
|
+
const { doc } = loadDocument(path);
|
|
207
|
+
const result = removeNodeOp({ doc, id });
|
|
208
|
+
return {
|
|
209
|
+
content: [
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: JSON.stringify({
|
|
213
|
+
message: `Node ${id} removed`,
|
|
214
|
+
nodeCount: result.doc.nodes.length,
|
|
215
|
+
warnings: result.warnings,
|
|
216
|
+
}, null, 2),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
// Register update-node tool
|
|
222
|
+
server.registerTool("update-node", {
|
|
223
|
+
description: "Update node properties",
|
|
224
|
+
inputSchema: z.object({
|
|
225
|
+
path: z.string().describe("Path to SysProM file"),
|
|
226
|
+
id: z.string().describe("Node ID"),
|
|
227
|
+
fields: z.record(z.string(), z.unknown()).describe("Fields to update"),
|
|
228
|
+
}),
|
|
229
|
+
}, ({ path, id, fields }) => {
|
|
230
|
+
const { doc } = loadDocument(path);
|
|
231
|
+
// Validate fields are valid node property updates
|
|
232
|
+
const validFields = Object.entries(fields).reduce((acc, [key, value]) => {
|
|
233
|
+
// Allow common node fields; unknown fields are silently ignored
|
|
234
|
+
if ([
|
|
235
|
+
"name",
|
|
236
|
+
"description",
|
|
237
|
+
"status",
|
|
238
|
+
"context",
|
|
239
|
+
"options",
|
|
240
|
+
"selected",
|
|
241
|
+
"rationale",
|
|
242
|
+
"scope",
|
|
243
|
+
"operations",
|
|
244
|
+
"plan",
|
|
245
|
+
"propagation",
|
|
246
|
+
"includes",
|
|
247
|
+
"input",
|
|
248
|
+
"output",
|
|
249
|
+
"external_references",
|
|
250
|
+
].includes(key)) {
|
|
251
|
+
acc[key] = value;
|
|
252
|
+
}
|
|
253
|
+
return acc;
|
|
254
|
+
}, {});
|
|
255
|
+
const updated = updateNodeOp({
|
|
256
|
+
doc,
|
|
257
|
+
id,
|
|
258
|
+
fields: validFields,
|
|
259
|
+
});
|
|
260
|
+
const node = updated.nodes.find((n) => n.id === id);
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: JSON.stringify({ message: "Node updated", node }, null, 2),
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
// Register add-relationship tool
|
|
271
|
+
server.registerTool("add-relationship", {
|
|
272
|
+
description: "Add a relationship between two nodes",
|
|
273
|
+
inputSchema: z.object({
|
|
274
|
+
path: z.string().describe("Path to SysProM file"),
|
|
275
|
+
from: z.string().describe("Source node ID"),
|
|
276
|
+
to: z.string().describe("Target node ID"),
|
|
277
|
+
type: z.string().describe("Relationship type"),
|
|
278
|
+
}),
|
|
279
|
+
}, ({ path, from, to, type }) => {
|
|
280
|
+
const { doc } = loadDocument(path);
|
|
281
|
+
const relType = RelationshipType.safeParse(type);
|
|
282
|
+
if (!relType.success) {
|
|
283
|
+
throw new Error(`Invalid relationship type: ${type}`);
|
|
284
|
+
}
|
|
285
|
+
const updated = addRelationshipOp({
|
|
286
|
+
doc,
|
|
287
|
+
rel: {
|
|
288
|
+
from,
|
|
289
|
+
to,
|
|
290
|
+
type: relType.data,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: JSON.stringify({
|
|
298
|
+
message: "Relationship added",
|
|
299
|
+
relationshipCount: (updated.relationships ?? []).length,
|
|
300
|
+
}, null, 2),
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
// Register remove-relationship tool
|
|
306
|
+
server.registerTool("remove-relationship", {
|
|
307
|
+
description: "Remove a relationship between two nodes",
|
|
308
|
+
inputSchema: z.object({
|
|
309
|
+
path: z.string().describe("Path to SysProM file"),
|
|
310
|
+
from: z.string().describe("Source node ID"),
|
|
311
|
+
to: z.string().describe("Target node ID"),
|
|
312
|
+
type: z.string().describe("Relationship type"),
|
|
313
|
+
}),
|
|
314
|
+
}, ({ path, from, to, type }) => {
|
|
315
|
+
const { doc } = loadDocument(path);
|
|
316
|
+
const relType = RelationshipType.safeParse(type);
|
|
317
|
+
if (!relType.success) {
|
|
318
|
+
throw new Error(`Invalid relationship type: ${type}`);
|
|
319
|
+
}
|
|
320
|
+
const result = removeRelationshipOp({
|
|
321
|
+
doc,
|
|
322
|
+
from,
|
|
323
|
+
to,
|
|
324
|
+
type: relType.data,
|
|
325
|
+
});
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
message: "Relationship removed",
|
|
332
|
+
relationshipCount: (result.doc.relationships ?? []).length,
|
|
333
|
+
}, null, 2),
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
// Start server
|
|
339
|
+
async function main() {
|
|
340
|
+
const transport = new StdioServerTransport();
|
|
341
|
+
await server.connect(transport);
|
|
342
|
+
console.error("SysProM MCP server running...");
|
|
343
|
+
}
|
|
344
|
+
main().catch((error) => {
|
|
345
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
346
|
+
console.error("Server error:", message);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
});
|
package/dist/src/md-to-json.js
CHANGED
|
@@ -35,7 +35,15 @@ function parseText(raw) {
|
|
|
35
35
|
const lines = raw.split("\n");
|
|
36
36
|
return lines.length === 1 ? lines[0] : lines;
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Separate $schema from front matter so it becomes a top-level document key.
|
|
40
|
+
* @param front - The front matter object
|
|
41
|
+
* @returns An object with extracted schema and remaining metadata
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const { schema, metadata } = extractSchema({ $schema: "...", foo: "bar" });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
39
47
|
function extractSchema(front) {
|
|
40
48
|
const schema = typeof front.$schema === "string" ? front.$schema : undefined;
|
|
41
49
|
const metadata = { ...front };
|
package/dist/src/sync.d.ts
CHANGED
|
@@ -12,15 +12,18 @@ export interface DetectionResult {
|
|
|
12
12
|
/**
|
|
13
13
|
* Detect whether JSON and/or Markdown have changed.
|
|
14
14
|
* Strategy:
|
|
15
|
-
* 1. Parse both JSON and Markdown to document objects
|
|
16
|
-
* 2. If documents are identical
|
|
17
|
-
* 3. If documents differ
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @param jsonPath Path to JSON file
|
|
23
|
-
* @param mdPath Path to Markdown file (single or multi-doc)
|
|
15
|
+
* 1. Parse both JSON and Markdown to document objects.
|
|
16
|
+
* 2. If documents are identical, no change.
|
|
17
|
+
* 3. If documents differ, use file modification times to determine which was
|
|
18
|
+
* edited more recently. The newer file is considered the "changed" one.
|
|
19
|
+
* If modification times are very close (< 100ms), treat as conflict.
|
|
20
|
+
* @param jsonPath - Path to JSON file
|
|
21
|
+
* @param mdPath - Path to Markdown file (single or multi-doc)
|
|
24
22
|
* @returns Detection result with jsonChanged, mdChanged, and conflict flags
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const result = detectChanges("doc.spm.json", "doc.spm.md");
|
|
26
|
+
* if (result.conflict) throw new Error("Both files changed");
|
|
27
|
+
* ```
|
|
25
28
|
*/
|
|
26
29
|
export declare function detectChanges(jsonPath: string, mdPath: string): DetectionResult;
|
package/dist/src/sync.js
CHANGED
|
@@ -5,31 +5,38 @@ import { SysProMDocument } from "./schema.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Compute a normalised hash of a document for comparison.
|
|
7
7
|
* Uses canonical JSON representation.
|
|
8
|
-
* @param doc The SysProM document
|
|
8
|
+
* @param doc - The SysProM document
|
|
9
9
|
* @returns SHA256 hash of the canonicalised document
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const hash = normaliseHash({ nodes: [], relationships: [] });
|
|
13
|
+
* ```
|
|
10
14
|
*/
|
|
11
15
|
function normaliseHash(doc) {
|
|
12
|
-
const
|
|
16
|
+
const keys = doc && typeof doc === "object" ? Object.keys(doc).sort() : [];
|
|
17
|
+
const sorted = JSON.stringify(doc, keys);
|
|
13
18
|
return createHash("sha256").update(sorted).digest("hex");
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
21
|
* Detect whether JSON and/or Markdown have changed.
|
|
17
22
|
* Strategy:
|
|
18
|
-
* 1. Parse both JSON and Markdown to document objects
|
|
19
|
-
* 2. If documents are identical
|
|
20
|
-
* 3. If documents differ
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @param jsonPath Path to JSON file
|
|
26
|
-
* @param mdPath Path to Markdown file (single or multi-doc)
|
|
23
|
+
* 1. Parse both JSON and Markdown to document objects.
|
|
24
|
+
* 2. If documents are identical, no change.
|
|
25
|
+
* 3. If documents differ, use file modification times to determine which was
|
|
26
|
+
* edited more recently. The newer file is considered the "changed" one.
|
|
27
|
+
* If modification times are very close (< 100ms), treat as conflict.
|
|
28
|
+
* @param jsonPath - Path to JSON file
|
|
29
|
+
* @param mdPath - Path to Markdown file (single or multi-doc)
|
|
27
30
|
* @returns Detection result with jsonChanged, mdChanged, and conflict flags
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const result = detectChanges("doc.spm.json", "doc.spm.md");
|
|
34
|
+
* if (result.conflict) throw new Error("Both files changed");
|
|
35
|
+
* ```
|
|
28
36
|
*/
|
|
29
37
|
export function detectChanges(jsonPath, mdPath) {
|
|
30
38
|
// Read files
|
|
31
39
|
const jsonContent = readFileSync(jsonPath, "utf8");
|
|
32
|
-
const mdContent = readFileSync(mdPath, "utf8");
|
|
33
40
|
// Parse JSON
|
|
34
41
|
const jsonDoc = JSON.parse(jsonContent);
|
|
35
42
|
if (!SysProMDocument.is(jsonDoc)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sysprom",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "SysProM — System Provenance Model CLI and library",
|
|
5
5
|
"author": "ExaDev",
|
|
6
6
|
"homepage": "https://exadev.github.io/SysProM",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
],
|
|
36
36
|
"bin": {
|
|
37
37
|
"sysprom": "dist/src/cli/index.js",
|
|
38
|
-
"spm": "dist/src/cli/index.js"
|
|
38
|
+
"spm": "dist/src/cli/index.js",
|
|
39
|
+
"sysprom-mcp": "dist/src/mcp/index.js"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "turbo run _build",
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"prepare": "husky"
|
|
60
61
|
},
|
|
61
62
|
"dependencies": {
|
|
63
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
62
64
|
"commander": "14.0.3",
|
|
63
65
|
"picocolors": "1.1.1",
|
|
64
66
|
"zod": "4.3.6"
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
"devDependencies": {
|
|
67
69
|
"@commitlint/cli": "20.5.0",
|
|
68
70
|
"@commitlint/config-conventional": "20.5.0",
|
|
71
|
+
"@eslint-community/eslint-plugin-eslint-comments": "4.7.1",
|
|
69
72
|
"@eslint/js": "10.0.1",
|
|
70
73
|
"@semantic-release/changelog": "6.0.3",
|
|
71
74
|
"@semantic-release/git": "10.0.1",
|