sysprom 1.7.1 → 1.9.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 CHANGED
@@ -237,3 +237,75 @@ spm md2json ./.spm .spm.json
237
237
  ```
238
238
 
239
239
  > **Important:** Always keep `.spm.json` and `./.spm/` up to date with current activity and in sync with each other. Record all decisions, changes, and new capabilities as they happen. After any change to either representation, run the appropriate conversion command above. Validate with `spm validate` before committing.
240
+
241
+ ## Claude Code Plugin
242
+
243
+ SysProM is available as a Claude Code plugin with 28 skills for managing provenance documents. The plugin is defined in `.claude-plugin/marketplace.json` with skills in `.claude/skills/`.
244
+
245
+ ### Install from Marketplace
246
+
247
+ ```sh
248
+ # Add the SysProM marketplace
249
+ /plugin marketplace add ExaDev/SysProM
250
+
251
+ # Install the plugin
252
+ /plugin install sysprom@sysprom
253
+ ```
254
+
255
+ Skills are namespaced when installed as a plugin (e.g. `/sysprom:add-decision`, `/sysprom:query-nodes`).
256
+
257
+ ### Local Development
258
+
259
+ When working on the SysProM repo itself, skills in `.claude/skills/` are auto-discovered without plugin installation. Skills use short names (e.g. `/add-decision`, `/query-nodes`).
260
+
261
+ ### Skills by Category
262
+
263
+ **Node Creation (4 skills)**
264
+ - `add-decision` — Create decision nodes with context, options, rationale, and invariant links
265
+ - `add-change` — Create change nodes with scope, operations, and task tracking
266
+ - `add-invariant` — Create invariant nodes representing system rules and constraints
267
+ - `add-node` — Generic node creation for any SysProM type
268
+
269
+ **Node Modification (3 skills)**
270
+ - `update-node` — Modify node fields, status, lifecycle, context, or rationale
271
+ - `remove-node` — Delete nodes with safety flags (hard delete, recursive, repair)
272
+ - `rename-node` — Rename node IDs across all references
273
+
274
+ **Relationships (2 skills)**
275
+ - `add-relationship` — Create relationships between nodes with specific types
276
+ - `remove-relationship` — Delete relationships
277
+
278
+ **Query & Analysis (5 skills)**
279
+ - `query-nodes` — Search nodes by type, status, text, or ID
280
+ - `query-relationships` — Query relationships by source, target, or type
281
+ - `trace-node` — Trace refinement chains through abstraction layers
282
+ - `check-document` — Validate document structure and report issues
283
+ - `stats` — Show document statistics and composition metrics
284
+
285
+ **Visualisation (1 skill)**
286
+ - `graph` — Generate Mermaid or DOT graphs with filtering
287
+
288
+ **Format Conversion (4 skills)**
289
+ - `init-document` — Create new SysProM documents with metadata
290
+ - `json-to-markdown` — Convert JSON to Markdown format
291
+ - `markdown-to-json` — Convert Markdown to JSON format
292
+ - `sync-formats` — Bidirectional sync between JSON and Markdown
293
+
294
+ **Spec-Kit Integration (4 skills)**
295
+ - `speckit-import` — Import Spec-Kit features as SysProM nodes
296
+ - `speckit-export` — Export SysProM nodes to Spec-Kit format
297
+ - `speckit-sync` — Bidirectional sync with Spec-Kit specifications
298
+ - `speckit-diff` — Show differences between SysProM and Spec-Kit
299
+
300
+ **Task Management (3 skills)**
301
+ - `task-list` — List tasks in a change node with progress
302
+ - `task-add` — Add tasks to a change
303
+ - `task-mark-done` — Mark tasks as complete
304
+
305
+ **Plan Management (2 skills)**
306
+ - `plan-init` — Initialise plans with phases and gates
307
+ - `plan-status` — Show plan progress and phase gates
308
+
309
+ ### Fallback to `npx`
310
+
311
+ If `spm` is not globally installed, skills automatically fall back to `npx -y sysprom` for command execution. All skills work with either global or per-project installation.
@@ -1,8 +1,3 @@
1
1
  import type { CommandDef } from "../define-command.js";
2
- import { noArgs } from "../shared.js";
3
- declare const optsSchema: import("zod").ZodObject<{
4
- path: import("zod").ZodOptional<import("zod").ZodString>;
5
- json: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
6
- }, import("zod/v4/core").$strip>;
7
- export declare const checkCommand: CommandDef<typeof noArgs, typeof optsSchema>;
8
- export {};
2
+ import { noArgs, readOpts } from "../shared.js";
3
+ export declare const checkCommand: CommandDef<typeof noArgs, typeof readOpts>;
@@ -1,11 +1,10 @@
1
1
  import { checkOp } from "../../operations/index.js";
2
2
  import { readOpts, loadDoc } from "../shared.js";
3
- const optsSchema = readOpts;
4
3
  export const checkCommand = {
5
4
  name: "check",
6
5
  description: checkOp.def.description,
7
6
  apiLink: checkOp.def.name,
8
- opts: optsSchema,
7
+ opts: readOpts,
9
8
  action(_args, opts) {
10
9
  const { doc } = loadDoc(opts.path);
11
10
  const result = checkOp({ doc });
@@ -31,10 +31,8 @@ export const json2mdCommand = {
31
31
  throw new Error("Invalid args");
32
32
  if (!isOpts(opts))
33
33
  throw new Error("Invalid opts");
34
- const typedArgs = args;
35
- const typedOpts = opts;
36
- const inputPath = resolve(typedArgs.input);
37
- const outputPath = resolve(typedArgs.output);
34
+ const inputPath = resolve(args.input);
35
+ const outputPath = resolve(args.output);
38
36
  const raw = JSON.parse(readFileSync(inputPath, "utf8"));
39
37
  if (!SysProMDocument.is(raw)) {
40
38
  const result = SysProMDocument.safeParse(raw);
@@ -46,7 +44,7 @@ export const json2mdCommand = {
46
44
  }
47
45
  process.exit(1);
48
46
  }
49
- const form = typedOpts.singleFile || outputPath.endsWith(".md")
47
+ const form = opts.singleFile || outputPath.endsWith(".md")
50
48
  ? "single-file"
51
49
  : "multi-doc";
52
50
  jsonToMarkdown(raw, outputPath, { form });
@@ -19,9 +19,8 @@ export const md2jsonCommand = {
19
19
  action(args) {
20
20
  if (!isArgs(args))
21
21
  throw new Error("Invalid args");
22
- const typedArgs = args;
23
- const inputPath = resolve(typedArgs.input);
24
- const outputPath = resolve(typedArgs.output);
22
+ const inputPath = resolve(args.input);
23
+ const outputPath = resolve(args.output);
25
24
  const doc = markdownToJson(inputPath);
26
25
  writeFileSync(outputPath, canonicalise(doc, { indent: "\t" }) + "\n");
27
26
  console.log(`Written to ${outputPath}`);
@@ -72,7 +72,6 @@ const nodesOpts = readOpts.extend({
72
72
  const nodeArgs = z.object({
73
73
  id: z.string().describe("node ID to retrieve"),
74
74
  });
75
- const nodeOpts = readOpts;
76
75
  const relsOpts = readOpts.extend({
77
76
  from: z.string().optional().describe("filter relationships by source node"),
78
77
  to: z.string().optional().describe("filter relationships by target node"),
@@ -81,14 +80,12 @@ const relsOpts = readOpts.extend({
81
80
  const traceArgs = z.object({
82
81
  id: z.string().describe("node ID to start trace from"),
83
82
  });
84
- const traceOpts = readOpts;
85
83
  const timelineOpts = readOpts.extend({
86
84
  node: z.string().optional().describe("filter events to a specific node"),
87
85
  });
88
86
  const stateAtArgs = z.object({
89
87
  time: z.string().describe("ISO timestamp to query"),
90
88
  });
91
- const stateAtOpts = readOpts;
92
89
  // ---------------------------------------------------------------------------
93
90
  // Subcommands
94
91
  // ---------------------------------------------------------------------------
@@ -116,10 +113,10 @@ const nodeSubcommand = {
116
113
  description: queryNodeOp.def.description,
117
114
  apiLink: queryNodeOp.def.name,
118
115
  args: nodeArgs,
119
- opts: nodeOpts,
116
+ opts: readOpts,
120
117
  action(rawArgs, rawOpts) {
121
118
  const args = nodeArgs.parse(rawArgs);
122
- const opts = nodeOpts.parse(rawOpts);
119
+ const opts = readOpts.parse(rawOpts);
123
120
  const { doc } = loadDoc(opts.path);
124
121
  const result = queryNodeOp({ doc, id: args.id });
125
122
  if (!result) {
@@ -174,10 +171,10 @@ const traceSubcommand = {
174
171
  description: traceFromNodeOp.def.description,
175
172
  apiLink: traceFromNodeOp.def.name,
176
173
  args: traceArgs,
177
- opts: traceOpts,
174
+ opts: readOpts,
178
175
  action(rawArgs, rawOpts) {
179
176
  const args = traceArgs.parse(rawArgs);
180
- const opts = traceOpts.parse(rawOpts);
177
+ const opts = readOpts.parse(rawOpts);
181
178
  const { doc } = loadDoc(opts.path);
182
179
  const trace = traceFromNodeOp({ doc, startId: args.id });
183
180
  if (opts.json) {
@@ -219,10 +216,10 @@ const stateAtSubcommand = {
219
216
  description: stateAtOp.def.description,
220
217
  apiLink: stateAtOp.def.name,
221
218
  args: stateAtArgs,
222
- opts: stateAtOpts,
219
+ opts: readOpts,
223
220
  action(rawArgs, rawOpts) {
224
221
  const args = stateAtArgs.parse(rawArgs);
225
- const opts = stateAtOpts.parse(rawOpts);
222
+ const opts = readOpts.parse(rawOpts);
226
223
  const { doc } = loadDoc(opts.path);
227
224
  const result = stateAtOp({ doc, timestamp: args.time });
228
225
  if (opts.json) {
@@ -8,6 +8,9 @@ declare const optsSchema: z.ZodObject<{
8
8
  json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
9
  dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
10
10
  sync: z.ZodOptional<z.ZodString>;
11
+ hard: z.ZodOptional<z.ZodBoolean>;
12
+ recursive: z.ZodOptional<z.ZodBoolean>;
13
+ repair: z.ZodOptional<z.ZodBoolean>;
11
14
  }, z.core.$strip>;
12
15
  export declare const removeCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
13
16
  export {};
@@ -4,7 +4,11 @@ import { mutationOpts, loadDoc, persistDoc } from "../shared.js";
4
4
  const argsSchema = z.object({
5
5
  nodeId: z.string().describe("ID of the node to remove"),
6
6
  });
7
- const optsSchema = mutationOpts;
7
+ const optsSchema = mutationOpts.extend({
8
+ hard: z.boolean().optional().describe("Hard delete (physical removal)"),
9
+ recursive: z.boolean().optional().describe("Allow subsystem removal"),
10
+ repair: z.boolean().optional().describe("Repair must_follow chains"),
11
+ });
8
12
  export const removeCommand = {
9
13
  name: "remove",
10
14
  description: removeNodeOp.def.description,
@@ -17,7 +21,13 @@ export const removeCommand = {
17
21
  const targetId = args.nodeId;
18
22
  const removedNode = doc.nodes.find((n) => n.id === targetId);
19
23
  try {
20
- const result = removeNodeOp({ doc, id: targetId });
24
+ const result = removeNodeOp({
25
+ doc,
26
+ id: targetId,
27
+ hard: opts.hard,
28
+ recursive: opts.recursive,
29
+ repair: opts.repair,
30
+ });
21
31
  // Count removed relationships
22
32
  const before = (doc.relationships ?? []).length;
23
33
  const after = (result.doc.relationships ?? []).length;
@@ -1,14 +1,9 @@
1
1
  import * as z from "zod";
2
2
  import type { CommandDef } from "../define-command.js";
3
+ import { mutationOpts } from "../shared.js";
3
4
  declare const argsSchema: z.ZodObject<{
4
5
  oldId: z.ZodString;
5
6
  newId: z.ZodString;
6
7
  }, z.core.$strip>;
7
- declare const optsSchema: z.ZodObject<{
8
- path: z.ZodOptional<z.ZodString>;
9
- json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
10
- dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
- sync: z.ZodOptional<z.ZodString>;
12
- }, z.core.$strip>;
13
- export declare const renameCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
8
+ export declare const renameCommand: CommandDef<typeof argsSchema, typeof mutationOpts>;
14
9
  export {};
@@ -5,13 +5,12 @@ const argsSchema = z.object({
5
5
  oldId: z.string().describe("Current node ID"),
6
6
  newId: z.string().describe("New node ID"),
7
7
  });
8
- const optsSchema = mutationOpts;
9
8
  export const renameCommand = {
10
9
  name: "rename",
11
10
  description: renameOp.def.description,
12
11
  apiLink: renameOp.def.name,
13
12
  args: argsSchema,
14
- opts: optsSchema,
13
+ opts: mutationOpts,
15
14
  action(args, opts) {
16
15
  try {
17
16
  const loaded = loadDoc(opts.path);
@@ -1,11 +1,8 @@
1
1
  import * as z from "zod";
2
2
  import type { CommandDef } from "../define-command.js";
3
+ import { readOpts } from "../shared.js";
3
4
  declare const argsSchema: z.ZodObject<{
4
5
  term: z.ZodString;
5
6
  }, z.core.$strip>;
6
- declare const optsSchema: z.ZodObject<{
7
- path: z.ZodOptional<z.ZodString>;
8
- json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
- }, z.core.$strip>;
10
- export declare const searchCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
7
+ export declare const searchCommand: CommandDef<typeof argsSchema, typeof readOpts>;
11
8
  export {};
@@ -5,13 +5,12 @@ import { textToString } from "../../text.js";
5
5
  const argsSchema = z.object({
6
6
  term: z.string().describe("Search term"),
7
7
  });
8
- const optsSchema = readOpts;
9
8
  export const searchCommand = {
10
9
  name: "search",
11
10
  description: searchOp.def.description,
12
11
  apiLink: searchOp.def.name,
13
12
  args: argsSchema,
14
- opts: optsSchema,
13
+ opts: readOpts,
15
14
  action(args, opts) {
16
15
  const { doc } = loadDoc(opts.path);
17
16
  const matches = searchOp({ doc, term: args.term });
@@ -1,8 +1,3 @@
1
1
  import type { CommandDef } from "../define-command.js";
2
- import { noArgs } from "../shared.js";
3
- declare const optsSchema: import("zod").ZodObject<{
4
- path: import("zod").ZodOptional<import("zod").ZodString>;
5
- json: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
6
- }, import("zod/v4/core").$strip>;
7
- export declare const statsCommand: CommandDef<typeof noArgs, typeof optsSchema>;
8
- export {};
2
+ import { noArgs, readOpts } from "../shared.js";
3
+ export declare const statsCommand: CommandDef<typeof noArgs, typeof readOpts>;
@@ -1,12 +1,11 @@
1
1
  import pc from "picocolors";
2
2
  import { statsOp } from "../../operations/index.js";
3
3
  import { readOpts, loadDoc } from "../shared.js";
4
- const optsSchema = readOpts;
5
4
  export const statsCommand = {
6
5
  name: "stats",
7
6
  description: statsOp.def.description,
8
7
  apiLink: statsOp.def.name,
9
- opts: optsSchema,
8
+ opts: readOpts,
10
9
  action(_args, opts) {
11
10
  const { doc } = loadDoc(opts.path);
12
11
  const s = statsOp({ doc });
@@ -42,13 +42,11 @@ const addRelArgs = z.object({
42
42
  type: RelationshipType.describe("relationship type"),
43
43
  to: z.string().describe("target node ID"),
44
44
  });
45
- const addRelOpts = mutationOpts;
46
45
  const removeRelArgs = z.object({
47
46
  from: z.string().describe("source node ID"),
48
47
  type: RelationshipType.describe("relationship type"),
49
48
  to: z.string().describe("target node ID"),
50
49
  });
51
- const removeRelOpts = mutationOpts;
52
50
  const metaOpts = mutationOpts.extend({
53
51
  fields: z
54
52
  .array(z.string())
@@ -116,10 +114,10 @@ const addRelSubcommand = {
116
114
  description: addRelationshipOp.def.description,
117
115
  apiLink: addRelationshipOp.def.name,
118
116
  args: addRelArgs,
119
- opts: addRelOpts,
117
+ opts: mutationOpts,
120
118
  action(rawArgs, rawOpts) {
121
119
  const args = addRelArgs.parse(rawArgs);
122
- const opts = addRelOpts.parse(rawOpts);
120
+ const opts = mutationOpts.parse(rawOpts);
123
121
  const loaded = loadDoc(opts.path);
124
122
  const { doc } = loaded;
125
123
  const newDoc = addRelationshipOp({
@@ -141,10 +139,10 @@ const removeRelSubcommand = {
141
139
  description: removeRelationshipOp.def.description,
142
140
  apiLink: removeRelationshipOp.def.name,
143
141
  args: removeRelArgs,
144
- opts: removeRelOpts,
142
+ opts: mutationOpts,
145
143
  action(rawArgs, rawOpts) {
146
144
  const args = removeRelArgs.parse(rawArgs);
147
- const opts = removeRelOpts.parse(rawOpts);
145
+ const opts = mutationOpts.parse(rawOpts);
148
146
  const loaded = loadDoc(opts.path);
149
147
  const { doc } = loaded;
150
148
  const result = removeRelationshipOp({
@@ -1,8 +1,3 @@
1
1
  import type { CommandDef } from "../define-command.js";
2
- import { noArgs } from "../shared.js";
3
- declare const optsSchema: import("zod").ZodObject<{
4
- path: import("zod").ZodOptional<import("zod").ZodString>;
5
- json: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
6
- }, import("zod/v4/core").$strip>;
7
- export declare const validateCommand: CommandDef<typeof noArgs, typeof optsSchema>;
8
- export {};
2
+ import { noArgs, readOpts } from "../shared.js";
3
+ export declare const validateCommand: CommandDef<typeof noArgs, typeof readOpts>;
@@ -1,12 +1,11 @@
1
1
  import pc from "picocolors";
2
2
  import { validateOp } from "../../operations/index.js";
3
3
  import { readOpts, loadDoc } from "../shared.js";
4
- const optsSchema = readOpts;
5
4
  export const validateCommand = {
6
5
  name: "validate",
7
6
  description: validateOp.def.description,
8
7
  apiLink: validateOp.def.name,
9
- opts: optsSchema,
8
+ opts: readOpts,
10
9
  action(_args, opts) {
11
10
  const { doc } = loadDoc(opts.path);
12
11
  const result = validateOp({ doc });
@@ -467,12 +467,11 @@ export function jsonToMarkdownMultiDoc(doc, outDir) {
467
467
  .replace(/[^a-z0-9]+/g, "-")
468
468
  .replace(/-$/, "")}`;
469
469
  // Auto-group when 2+ subsystems share the same type
470
- let parentDir = outDir;
471
- if ((typeCounts.get(n.type) ?? 0) >= 2 && NodeType.is(n.type)) {
472
- const groupLabel = NODE_TYPE_LABELS[n.type]
473
- .toLowerCase()
474
- .replace(/ /g, "-");
475
- parentDir = join(outDir, groupLabel);
470
+ const shouldGroup = (typeCounts.get(n.type) ?? 0) >= 2 && NodeType.is(n.type);
471
+ const parentDir = shouldGroup
472
+ ? join(outDir, NODE_TYPE_LABELS[n.type].toLowerCase().replace(/ /g, "-"))
473
+ : outDir;
474
+ if (shouldGroup) {
476
475
  mkdirSync(parentDir, { recursive: true });
477
476
  }
478
477
  if (fileCounts <= 1) {
@@ -83,35 +83,26 @@ export const removeNodeOp = defineOperation({
83
83
  }
84
84
  // Clean up all references to the removed node (both soft and hard)
85
85
  const cleanedNodes = newNodes.map((n) => {
86
- let updated = n;
86
+ const updates = {};
87
87
  // Remove from view includes
88
88
  if (n.includes?.includes(id)) {
89
89
  const newIncludes = n.includes.filter((i) => i !== id);
90
- updated = {
91
- ...updated,
92
- includes: newIncludes.length > 0 ? newIncludes : undefined,
93
- };
90
+ updates.includes = newIncludes.length > 0 ? newIncludes : undefined;
94
91
  }
95
92
  // Remove from scope
96
93
  if (n.scope?.includes(id)) {
97
94
  const newScope = n.scope.filter((s) => s !== id);
98
95
  warnings.push(`${n.id} scope still references ${id}`);
99
- updated = {
100
- ...updated,
101
- scope: newScope.length > 0 ? newScope : undefined,
102
- };
96
+ updates.scope = newScope.length > 0 ? newScope : undefined;
103
97
  }
104
98
  // Remove from operations
105
99
  const opsWithTarget = n.operations?.some((op) => op.target === id);
106
100
  if (opsWithTarget) {
107
101
  const newOps = n.operations?.filter((op) => op.target !== id);
108
102
  warnings.push(`${n.id} operations still reference ${id}`);
109
- updated = {
110
- ...updated,
111
- operations: newOps && newOps.length > 0 ? newOps : undefined,
112
- };
103
+ updates.operations = (newOps?.length ?? 0) > 0 ? newOps : undefined;
113
104
  }
114
- return updated;
105
+ return Object.keys(updates).length > 0 ? { ...n, ...updates } : n;
115
106
  });
116
107
  // Remove from external references
117
108
  const newExternalRefs = (doc.external_references ?? []).filter((ref) => ref.node_id !== id);
@@ -298,306 +298,6 @@ declare const TraceNodeSchema: z.ZodObject<{
298
298
  }>;
299
299
  children: z.ZodArray<typeof TraceNodeSchema>;
300
300
  }, z.core.$strip>;
301
- /** Zod schema for a node in the refinement trace tree. */
302
- export declare const TraceNode: z.ZodObject<{
303
- id: z.ZodString;
304
- node: z.ZodOptional<z.ZodObject<{
305
- id: z.ZodString;
306
- type: z.ZodEnum<{
307
- intent: "intent";
308
- concept: "concept";
309
- capability: "capability";
310
- element: "element";
311
- realisation: "realisation";
312
- invariant: "invariant";
313
- principle: "principle";
314
- policy: "policy";
315
- protocol: "protocol";
316
- stage: "stage";
317
- role: "role";
318
- gate: "gate";
319
- mode: "mode";
320
- artefact: "artefact";
321
- artefact_flow: "artefact_flow";
322
- decision: "decision";
323
- change: "change";
324
- view: "view";
325
- milestone: "milestone";
326
- version: "version";
327
- }> & {
328
- is(value: unknown): value is "intent" | "concept" | "capability" | "element" | "realisation" | "invariant" | "principle" | "policy" | "protocol" | "stage" | "role" | "gate" | "mode" | "artefact" | "artefact_flow" | "decision" | "change" | "view" | "milestone" | "version";
329
- };
330
- name: z.ZodString;
331
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
332
- is(value: unknown): value is string | string[];
333
- }>;
334
- status: z.ZodOptional<z.ZodEnum<{
335
- deprecated: "deprecated";
336
- proposed: "proposed";
337
- accepted: "accepted";
338
- active: "active";
339
- implemented: "implemented";
340
- adopted: "adopted";
341
- defined: "defined";
342
- introduced: "introduced";
343
- in_progress: "in_progress";
344
- complete: "complete";
345
- consolidated: "consolidated";
346
- experimental: "experimental";
347
- retired: "retired";
348
- superseded: "superseded";
349
- abandoned: "abandoned";
350
- deferred: "deferred";
351
- }> & {
352
- is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
353
- }>;
354
- lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
355
- context: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
356
- is(value: unknown): value is string | string[];
357
- }>;
358
- options: z.ZodOptional<z.ZodArray<z.ZodObject<{
359
- id: z.ZodString;
360
- description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
361
- is(value: unknown): value is string | string[];
362
- };
363
- }, z.core.$loose> & {
364
- is(value: unknown): value is {
365
- [x: string]: unknown;
366
- id: string;
367
- description: string | string[];
368
- };
369
- }>>;
370
- selected: z.ZodOptional<z.ZodString>;
371
- rationale: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
372
- is(value: unknown): value is string | string[];
373
- }>;
374
- scope: z.ZodOptional<z.ZodArray<z.ZodString>>;
375
- operations: z.ZodOptional<z.ZodArray<z.ZodObject<{
376
- type: z.ZodEnum<{
377
- link: "link";
378
- add: "add";
379
- update: "update";
380
- remove: "remove";
381
- }>;
382
- target: z.ZodOptional<z.ZodString>;
383
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
384
- is(value: unknown): value is string | string[];
385
- }>;
386
- }, z.core.$loose> & {
387
- is(value: unknown): value is {
388
- [x: string]: unknown;
389
- type: "link" | "add" | "update" | "remove";
390
- target?: string | undefined;
391
- description?: string | string[] | undefined;
392
- };
393
- }>>;
394
- plan: z.ZodOptional<z.ZodArray<z.ZodObject<{
395
- description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
396
- is(value: unknown): value is string | string[];
397
- };
398
- done: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
399
- }, z.core.$loose> & {
400
- is(value: unknown): value is {
401
- [x: string]: unknown;
402
- description: string | string[];
403
- done?: boolean | undefined;
404
- };
405
- }>>;
406
- propagation: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
407
- includes: z.ZodOptional<z.ZodArray<z.ZodString>>;
408
- input: z.ZodOptional<z.ZodString>;
409
- output: z.ZodOptional<z.ZodString>;
410
- external_references: z.ZodOptional<z.ZodArray<z.ZodObject<{
411
- role: z.ZodEnum<{
412
- output: "output";
413
- input: "input";
414
- context: "context";
415
- evidence: "evidence";
416
- source: "source";
417
- standard: "standard";
418
- prior_art: "prior_art";
419
- }> & {
420
- is(value: unknown): value is "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
421
- };
422
- identifier: z.ZodString;
423
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
424
- is(value: unknown): value is string | string[];
425
- }>;
426
- node_id: z.ZodOptional<z.ZodString>;
427
- internalised: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
428
- is(value: unknown): value is string | string[];
429
- }>;
430
- }, z.core.$strip> & {
431
- is(value: unknown): value is {
432
- role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
433
- identifier: string;
434
- description?: string | string[] | undefined;
435
- node_id?: string | undefined;
436
- internalised?: string | string[] | undefined;
437
- };
438
- }>>;
439
- readonly subsystem: z.ZodOptional<z.ZodObject<{
440
- $schema: z.ZodOptional<z.ZodString>;
441
- metadata: z.ZodOptional<z.ZodObject<{
442
- title: z.ZodOptional<z.ZodString>;
443
- doc_type: z.ZodOptional<z.ZodString>;
444
- scope: z.ZodOptional<z.ZodString>;
445
- status: z.ZodOptional<z.ZodString>;
446
- version: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodInt]>>;
447
- }, z.core.$loose> & {
448
- is(value: unknown): value is {
449
- [x: string]: unknown;
450
- title?: string | undefined;
451
- doc_type?: string | undefined;
452
- scope?: string | undefined;
453
- status?: string | undefined;
454
- version?: string | number | undefined;
455
- };
456
- }>;
457
- nodes: z.ZodArray<z.ZodObject</*elided*/ any, z.core.$loose>>;
458
- relationships: z.ZodOptional<z.ZodArray<z.ZodObject<{
459
- from: z.ZodString;
460
- to: z.ZodString;
461
- type: z.ZodEnum<{
462
- refines: "refines";
463
- realises: "realises";
464
- implements: "implements";
465
- depends_on: "depends_on";
466
- constrained_by: "constrained_by";
467
- affects: "affects";
468
- supersedes: "supersedes";
469
- must_preserve: "must_preserve";
470
- performs: "performs";
471
- part_of: "part_of";
472
- precedes: "precedes";
473
- must_follow: "must_follow";
474
- blocks: "blocks";
475
- routes_to: "routes_to";
476
- governed_by: "governed_by";
477
- modifies: "modifies";
478
- triggered_by: "triggered_by";
479
- applies_to: "applies_to";
480
- produces: "produces";
481
- consumes: "consumes";
482
- transforms_into: "transforms_into";
483
- selects: "selects";
484
- requires: "requires";
485
- disables: "disables";
486
- }> & {
487
- is(value: unknown): value is "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
488
- };
489
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
490
- is(value: unknown): value is string | string[];
491
- }>;
492
- }, z.core.$loose> & {
493
- is(value: unknown): value is {
494
- [x: string]: unknown;
495
- from: string;
496
- to: string;
497
- type: "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
498
- description?: string | string[] | undefined;
499
- };
500
- }>>;
501
- external_references: z.ZodOptional<z.ZodArray<z.ZodObject<{
502
- role: z.ZodEnum<{
503
- output: "output";
504
- input: "input";
505
- context: "context";
506
- evidence: "evidence";
507
- source: "source";
508
- standard: "standard";
509
- prior_art: "prior_art";
510
- }> & {
511
- is(value: unknown): value is "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
512
- };
513
- identifier: z.ZodString;
514
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
515
- is(value: unknown): value is string | string[];
516
- }>;
517
- node_id: z.ZodOptional<z.ZodString>;
518
- internalised: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
519
- is(value: unknown): value is string | string[];
520
- }>;
521
- }, z.core.$strip> & {
522
- is(value: unknown): value is {
523
- role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
524
- identifier: string;
525
- description?: string | string[] | undefined;
526
- node_id?: string | undefined;
527
- internalised?: string | string[] | undefined;
528
- };
529
- }>>;
530
- }, z.core.$strip>>;
531
- }, z.core.$loose> & {
532
- is(value: unknown): value is {
533
- [x: string]: unknown;
534
- id: string;
535
- type: "intent" | "concept" | "capability" | "element" | "realisation" | "invariant" | "principle" | "policy" | "protocol" | "stage" | "role" | "gate" | "mode" | "artefact" | "artefact_flow" | "decision" | "change" | "view" | "milestone" | "version";
536
- name: string;
537
- description?: string | string[] | undefined;
538
- status?: "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred" | undefined;
539
- lifecycle?: Record<string, string | boolean> | undefined;
540
- context?: string | string[] | undefined;
541
- options?: {
542
- [x: string]: unknown;
543
- id: string;
544
- description: string | string[];
545
- }[] | undefined;
546
- selected?: string | undefined;
547
- rationale?: string | string[] | undefined;
548
- scope?: string[] | undefined;
549
- operations?: {
550
- [x: string]: unknown;
551
- type: "link" | "add" | "update" | "remove";
552
- target?: string | undefined;
553
- description?: string | string[] | undefined;
554
- }[] | undefined;
555
- plan?: {
556
- [x: string]: unknown;
557
- description: string | string[];
558
- done?: boolean | undefined;
559
- }[] | undefined;
560
- propagation?: Record<string, boolean> | undefined;
561
- includes?: string[] | undefined;
562
- input?: string | undefined;
563
- output?: string | undefined;
564
- external_references?: {
565
- role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
566
- identifier: string;
567
- description?: string | string[] | undefined;
568
- node_id?: string | undefined;
569
- internalised?: string | string[] | undefined;
570
- }[] | undefined;
571
- subsystem?: {
572
- nodes: /*elided*/ any[];
573
- $schema?: string | undefined;
574
- metadata?: {
575
- [x: string]: unknown;
576
- title?: string | undefined;
577
- doc_type?: string | undefined;
578
- scope?: string | undefined;
579
- status?: string | undefined;
580
- version?: string | number | undefined;
581
- } | undefined;
582
- relationships?: {
583
- [x: string]: unknown;
584
- from: string;
585
- to: string;
586
- type: "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
587
- description?: string | string[] | undefined;
588
- }[] | undefined;
589
- external_references?: {
590
- role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
591
- identifier: string;
592
- description?: string | string[] | undefined;
593
- node_id?: string | undefined;
594
- internalised?: string | string[] | undefined;
595
- }[] | undefined;
596
- } | undefined;
597
- };
598
- }>;
599
- children: z.ZodArray<typeof TraceNodeSchema>;
600
- }, z.core.$strip>;
601
301
  /** A node in the refinement trace tree, with optional children that refine/realise/implement it. */
602
302
  export type TraceNode = z.infer<typeof TraceNodeSchema>;
603
303
  /** Trace the refinement chain from a node, following `refines`, `realises`, and `implements` relationships recursively. */
@@ -9,8 +9,6 @@ const TraceNodeSchema = z.object({
9
9
  return z.array(TraceNodeSchema);
10
10
  },
11
11
  });
12
- /** Zod schema for a node in the refinement trace tree. */
13
- export const TraceNode = TraceNodeSchema;
14
12
  /** Trace the refinement chain from a node, following `refines`, `realises`, and `implements` relationships recursively. */
15
13
  export const traceFromNodeOp = defineOperation({
16
14
  name: "trace-from-node",
@@ -19,7 +17,7 @@ export const traceFromNodeOp = defineOperation({
19
17
  doc: SysProMDocument,
20
18
  startId: z.string(),
21
19
  }),
22
- output: TraceNode,
20
+ output: TraceNodeSchema,
23
21
  fn: (input) => {
24
22
  const visited = new Set();
25
23
  function trace(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sysprom",
3
- "version": "1.7.1",
3
+ "version": "1.9.0",
4
4
  "description": "SysProM — System Provenance Model CLI and library",
5
5
  "author": "ExaDev",
6
6
  "homepage": "https://exadev.github.io/SysProM",
@@ -71,6 +71,7 @@
71
71
  "@eslint-community/eslint-plugin-eslint-comments": "4.7.1",
72
72
  "@eslint/js": "10.0.1",
73
73
  "@semantic-release/changelog": "6.0.3",
74
+ "@semantic-release/exec": "7.1.0",
74
75
  "@semantic-release/git": "10.0.1",
75
76
  "@types/node": "25.5.0",
76
77
  "c8": "11.0.0",