sysprom 1.0.7 → 1.2.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 (113) hide show
  1. package/README.md +28 -28
  2. package/dist/src/canonical-json.d.ts +2 -0
  3. package/dist/src/cli/commands/add.d.ts +1 -1
  4. package/dist/src/cli/commands/add.js +2 -3
  5. package/dist/src/cli/commands/check.d.ts +6 -8
  6. package/dist/src/cli/commands/check.js +3 -8
  7. package/dist/src/cli/commands/graph.d.ts +3 -4
  8. package/dist/src/cli/commands/graph.js +3 -7
  9. package/dist/src/cli/commands/init.d.ts +15 -1
  10. package/dist/src/cli/commands/init.js +57 -27
  11. package/dist/src/cli/commands/plan.js +21 -39
  12. package/dist/src/cli/commands/query.js +10 -28
  13. package/dist/src/cli/commands/remove.d.ts +1 -1
  14. package/dist/src/cli/commands/remove.js +2 -3
  15. package/dist/src/cli/commands/rename.d.ts +1 -1
  16. package/dist/src/cli/commands/rename.js +2 -3
  17. package/dist/src/cli/commands/search.d.ts +1 -1
  18. package/dist/src/cli/commands/search.js +2 -3
  19. package/dist/src/cli/commands/stats.d.ts +6 -8
  20. package/dist/src/cli/commands/stats.js +3 -8
  21. package/dist/src/cli/commands/task.js +19 -24
  22. package/dist/src/cli/commands/update.js +6 -14
  23. package/dist/src/cli/commands/validate.d.ts +6 -8
  24. package/dist/src/cli/commands/validate.js +3 -8
  25. package/dist/src/cli/shared.d.ts +18 -3
  26. package/dist/src/cli/shared.js +100 -3
  27. package/dist/src/io.d.ts +5 -0
  28. package/dist/src/json-to-md.d.ts +1 -0
  29. package/dist/src/operations/add-node.d.ts +5 -0
  30. package/dist/src/operations/add-node.js +5 -0
  31. package/dist/src/operations/add-plan-task.d.ts +5 -0
  32. package/dist/src/operations/add-plan-task.js +5 -0
  33. package/dist/src/operations/add-relationship.d.ts +5 -0
  34. package/dist/src/operations/add-relationship.js +5 -0
  35. package/dist/src/operations/check.d.ts +5 -0
  36. package/dist/src/operations/check.js +5 -0
  37. package/dist/src/operations/define-operation.d.ts +31 -0
  38. package/dist/src/operations/define-operation.js +8 -0
  39. package/dist/src/operations/graph.d.ts +1 -0
  40. package/dist/src/operations/graph.js +1 -0
  41. package/dist/src/operations/init-document.d.ts +1 -0
  42. package/dist/src/operations/init-document.js +1 -0
  43. package/dist/src/operations/json-to-markdown.d.ts +1 -0
  44. package/dist/src/operations/json-to-markdown.js +1 -0
  45. package/dist/src/operations/mark-task-done.d.ts +5 -0
  46. package/dist/src/operations/mark-task-done.js +5 -0
  47. package/dist/src/operations/mark-task-undone.d.ts +5 -0
  48. package/dist/src/operations/mark-task-undone.js +5 -0
  49. package/dist/src/operations/markdown-to-json.d.ts +1 -0
  50. package/dist/src/operations/markdown-to-json.js +1 -0
  51. package/dist/src/operations/next-id.d.ts +6 -0
  52. package/dist/src/operations/next-id.js +6 -0
  53. package/dist/src/operations/node-history.d.ts +3 -0
  54. package/dist/src/operations/node-history.js +2 -0
  55. package/dist/src/operations/plan-add-task.d.ts +1 -0
  56. package/dist/src/operations/plan-add-task.js +1 -0
  57. package/dist/src/operations/plan-gate.d.ts +4 -0
  58. package/dist/src/operations/plan-gate.js +2 -0
  59. package/dist/src/operations/plan-init.d.ts +1 -0
  60. package/dist/src/operations/plan-init.js +1 -0
  61. package/dist/src/operations/plan-progress.d.ts +2 -0
  62. package/dist/src/operations/plan-progress.js +1 -0
  63. package/dist/src/operations/plan-status.d.ts +2 -0
  64. package/dist/src/operations/plan-status.js +1 -0
  65. package/dist/src/operations/query-node.d.ts +3 -0
  66. package/dist/src/operations/query-node.js +2 -0
  67. package/dist/src/operations/query-nodes.d.ts +1 -0
  68. package/dist/src/operations/query-nodes.js +1 -0
  69. package/dist/src/operations/query-relationships.d.ts +1 -0
  70. package/dist/src/operations/query-relationships.js +1 -0
  71. package/dist/src/operations/remove-node.d.ts +8 -0
  72. package/dist/src/operations/remove-node.js +7 -0
  73. package/dist/src/operations/remove-relationship.d.ts +5 -0
  74. package/dist/src/operations/remove-relationship.js +5 -0
  75. package/dist/src/operations/rename.d.ts +7 -0
  76. package/dist/src/operations/rename.js +7 -0
  77. package/dist/src/operations/search.d.ts +1 -0
  78. package/dist/src/operations/search.js +1 -0
  79. package/dist/src/operations/speckit-diff.d.ts +3 -0
  80. package/dist/src/operations/speckit-diff.js +2 -0
  81. package/dist/src/operations/speckit-export.d.ts +1 -0
  82. package/dist/src/operations/speckit-export.js +1 -0
  83. package/dist/src/operations/speckit-import.d.ts +1 -0
  84. package/dist/src/operations/speckit-import.js +1 -0
  85. package/dist/src/operations/speckit-sync.d.ts +3 -0
  86. package/dist/src/operations/speckit-sync.js +2 -0
  87. package/dist/src/operations/state-at.d.ts +3 -0
  88. package/dist/src/operations/state-at.js +2 -0
  89. package/dist/src/operations/stats.d.ts +3 -0
  90. package/dist/src/operations/stats.js +2 -0
  91. package/dist/src/operations/task-list.d.ts +6 -0
  92. package/dist/src/operations/task-list.js +6 -0
  93. package/dist/src/operations/timeline.d.ts +3 -0
  94. package/dist/src/operations/timeline.js +2 -0
  95. package/dist/src/operations/trace-from-node.d.ts +3 -0
  96. package/dist/src/operations/trace-from-node.js +2 -0
  97. package/dist/src/operations/update-metadata.d.ts +1 -0
  98. package/dist/src/operations/update-metadata.js +1 -0
  99. package/dist/src/operations/update-node.d.ts +5 -0
  100. package/dist/src/operations/update-node.js +5 -0
  101. package/dist/src/operations/update-plan-task.d.ts +5 -0
  102. package/dist/src/operations/update-plan-task.js +5 -0
  103. package/dist/src/operations/validate.d.ts +10 -0
  104. package/dist/src/operations/validate.js +9 -0
  105. package/dist/src/schema.d.ts +44 -0
  106. package/dist/src/schema.js +31 -0
  107. package/dist/src/speckit/generate.d.ts +6 -0
  108. package/dist/src/speckit/generate.js +6 -0
  109. package/dist/src/speckit/parse.d.ts +9 -0
  110. package/dist/src/speckit/parse.js +6 -0
  111. package/dist/src/speckit/plan.d.ts +5 -0
  112. package/dist/src/speckit/project.d.ts +6 -0
  113. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
1
  import * as z from "zod";
2
2
  import type { CommandDef } from "../define-command.js";
3
3
  declare const argsSchema: z.ZodObject<{
4
- input: z.ZodString;
5
4
  oldId: z.ZodString;
6
5
  newId: z.ZodString;
7
6
  }, z.core.$strip>;
8
7
  declare const optsSchema: z.ZodObject<{
8
+ path: z.ZodOptional<z.ZodString>;
9
9
  json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
10
10
  dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
11
  sync: z.ZodOptional<z.ZodString>;
@@ -1,8 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { renameOp } from "../../operations/index.js";
3
- import { inputArg, mutationOpts, loadDoc, persistDoc } from "../shared.js";
3
+ import { mutationOpts, loadDoc, persistDoc } from "../shared.js";
4
4
  const argsSchema = z.object({
5
- input: inputArg,
6
5
  oldId: z.string().describe("Current node ID"),
7
6
  newId: z.string().describe("New node ID"),
8
7
  });
@@ -15,7 +14,7 @@ export const renameCommand = {
15
14
  opts: optsSchema,
16
15
  action(args, opts) {
17
16
  try {
18
- const loaded = loadDoc(args.input);
17
+ const loaded = loadDoc(opts.path);
19
18
  const { doc } = loaded;
20
19
  const updated = renameOp({ doc, oldId: args.oldId, newId: args.newId });
21
20
  persistDoc(updated, loaded, opts);
@@ -1,10 +1,10 @@
1
1
  import * as z from "zod";
2
2
  import type { CommandDef } from "../define-command.js";
3
3
  declare const argsSchema: z.ZodObject<{
4
- input: z.ZodString;
5
4
  term: z.ZodString;
6
5
  }, z.core.$strip>;
7
6
  declare const optsSchema: z.ZodObject<{
7
+ path: z.ZodOptional<z.ZodString>;
8
8
  json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
9
  }, z.core.$strip>;
10
10
  export declare const searchCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
@@ -1,9 +1,8 @@
1
1
  import * as z from "zod";
2
2
  import { searchOp } from "../../operations/index.js";
3
- import { inputArg, readOpts, loadDoc } from "../shared.js";
3
+ import { readOpts, loadDoc } from "../shared.js";
4
4
  import { textToString } from "../../text.js";
5
5
  const argsSchema = z.object({
6
- input: inputArg,
7
6
  term: z.string().describe("Search term"),
8
7
  });
9
8
  const optsSchema = readOpts;
@@ -14,7 +13,7 @@ export const searchCommand = {
14
13
  args: argsSchema,
15
14
  opts: optsSchema,
16
15
  action(args, opts) {
17
- const { doc } = loadDoc(args.input);
16
+ const { doc } = loadDoc(opts.path);
18
17
  const matches = searchOp({ doc, term: args.term });
19
18
  if (opts.json) {
20
19
  console.log(JSON.stringify(matches, null, 2));
@@ -1,10 +1,8 @@
1
- import * as z from "zod";
2
1
  import type { CommandDef } from "../define-command.js";
3
- declare const argsSchema: z.ZodObject<{
4
- input: z.ZodString;
5
- }, z.core.$strip>;
6
- declare const optsSchema: z.ZodObject<{
7
- json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
8
- }, z.core.$strip>;
9
- export declare const statsCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
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>;
10
8
  export {};
@@ -1,19 +1,14 @@
1
- import * as z from "zod";
2
1
  import pc from "picocolors";
3
2
  import { statsOp } from "../../operations/index.js";
4
- import { inputArg, readOpts, loadDoc } from "../shared.js";
5
- const argsSchema = z.object({
6
- input: inputArg,
7
- });
3
+ import { readOpts, loadDoc } from "../shared.js";
8
4
  const optsSchema = readOpts;
9
5
  export const statsCommand = {
10
6
  name: "stats",
11
7
  description: statsOp.def.description,
12
8
  apiLink: statsOp.def.name,
13
- args: argsSchema,
14
9
  opts: optsSchema,
15
- action(args) {
16
- const { doc } = loadDoc(args.input);
10
+ action(_args, opts) {
11
+ const { doc } = loadDoc(opts.path);
17
12
  const s = statsOp({ doc });
18
13
  console.log(`${pc.bold("SysProM Document")}: ${s.title}`);
19
14
  console.log("");
@@ -1,25 +1,20 @@
1
1
  import * as z from "zod";
2
- import { loadDocument, saveDocument } from "../../io.js";
2
+ import { loadDoc, mutationOpts, persistDoc } from "../shared.js";
3
3
  import { addPlanTaskOp, markTaskDoneOp, markTaskUndoneOp, taskListOp, } from "../../operations/index.js";
4
4
  // ============================================================================
5
5
  // Subcommands
6
6
  // ============================================================================
7
- const listArgs = z.object({
8
- input: z.string().describe("Path to SysProM document"),
9
- });
10
- const listOpts = z.object({
7
+ const listOpts = mutationOpts.pick({ path: true, json: true }).extend({
11
8
  change: z.string().optional().describe("Filter by change ID"),
12
9
  pending: z.boolean().optional().describe("Show only pending tasks"),
13
- json: z.boolean().optional().describe("Output as JSON"),
14
10
  });
15
11
  const listSubcommand = {
16
12
  name: "list",
17
13
  description: taskListOp.def.description,
18
14
  apiLink: taskListOp.def.name,
19
- args: listArgs,
20
15
  opts: listOpts,
21
- action(args, opts) {
22
- const { doc } = loadDocument(args.input);
16
+ action(_args, opts) {
17
+ const { doc } = loadDoc(opts.path);
23
18
  try {
24
19
  const rows = taskListOp({
25
20
  doc,
@@ -47,26 +42,26 @@ const listSubcommand = {
47
42
  },
48
43
  };
49
44
  const addArgs = z.object({
50
- input: z.string().describe("Path to SysProM document"),
51
45
  changeId: z.string().describe("Change node ID"),
52
46
  description: z.string().describe("Task description"),
53
47
  });
54
- const addOpts = z.object({});
48
+ const addOpts = mutationOpts.pick({ path: true });
55
49
  const addSubcommand = {
56
50
  name: "add",
57
51
  description: addPlanTaskOp.def.description,
58
52
  apiLink: addPlanTaskOp.def.name,
59
53
  args: addArgs,
60
54
  opts: addOpts,
61
- action(args) {
62
- const { doc, format, path } = loadDocument(args.input);
55
+ action(args, opts) {
56
+ const loaded = loadDoc(opts.path);
57
+ const { doc } = loaded;
63
58
  try {
64
59
  const newDoc = addPlanTaskOp({
65
60
  doc,
66
61
  changeId: args.changeId,
67
62
  description: args.description,
68
63
  });
69
- saveDocument(newDoc, format, path);
64
+ persistDoc(newDoc, loaded, { ...opts, json: false, dryRun: false });
70
65
  const node = newDoc.nodes.find((n) => n.id === args.changeId);
71
66
  if (!node)
72
67
  throw new Error(`Node ${args.changeId} not found`);
@@ -80,19 +75,19 @@ const addSubcommand = {
80
75
  },
81
76
  };
82
77
  const doneArgs = z.object({
83
- input: z.string().describe("Path to SysProM document"),
84
78
  changeId: z.string().describe("Change node ID"),
85
79
  taskIndex: z.string().describe("Task index"),
86
80
  });
87
- const doneOpts = z.object({});
81
+ const doneOpts = mutationOpts.pick({ path: true });
88
82
  const doneSubcommand = {
89
83
  name: "done",
90
84
  description: markTaskDoneOp.def.description,
91
85
  apiLink: markTaskDoneOp.def.name,
92
86
  args: doneArgs,
93
87
  opts: doneOpts,
94
- action(args) {
95
- const { doc, format, path } = loadDocument(args.input);
88
+ action(args, opts) {
89
+ const loaded = loadDoc(opts.path);
90
+ const { doc } = loaded;
96
91
  const taskIndex = parseInt(args.taskIndex, 10);
97
92
  if (isNaN(taskIndex) || taskIndex < 0) {
98
93
  console.error(`Invalid task index: ${args.taskIndex}`);
@@ -104,7 +99,7 @@ const doneSubcommand = {
104
99
  changeId: args.changeId,
105
100
  taskIndex,
106
101
  });
107
- saveDocument(newDoc, format, path);
102
+ persistDoc(newDoc, loaded, { ...opts, json: false, dryRun: false });
108
103
  console.log(`Marked task ${String(taskIndex)} done on ${args.changeId}`);
109
104
  }
110
105
  catch (err) {
@@ -114,19 +109,19 @@ const doneSubcommand = {
114
109
  },
115
110
  };
116
111
  const undoneArgs = z.object({
117
- input: z.string().describe("Path to SysProM document"),
118
112
  changeId: z.string().describe("Change node ID"),
119
113
  taskIndex: z.string().describe("Task index"),
120
114
  });
121
- const undoneOpts = z.object({});
115
+ const undoneOpts = mutationOpts.pick({ path: true });
122
116
  const undoneSubcommand = {
123
117
  name: "undone",
124
118
  description: markTaskUndoneOp.def.description,
125
119
  apiLink: markTaskUndoneOp.def.name,
126
120
  args: undoneArgs,
127
121
  opts: undoneOpts,
128
- action(args) {
129
- const { doc, format, path } = loadDocument(args.input);
122
+ action(args, opts) {
123
+ const loaded = loadDoc(opts.path);
124
+ const { doc } = loaded;
130
125
  const taskIndex = parseInt(args.taskIndex, 10);
131
126
  if (isNaN(taskIndex) || taskIndex < 0) {
132
127
  console.error(`Invalid task index: ${args.taskIndex}`);
@@ -138,7 +133,7 @@ const undoneSubcommand = {
138
133
  changeId: args.changeId,
139
134
  taskIndex,
140
135
  });
141
- saveDocument(newDoc, format, path);
136
+ persistDoc(newDoc, loaded, { ...opts, json: false, dryRun: false });
142
137
  console.log(`Marked task ${String(taskIndex)} undone on ${args.changeId}`);
143
138
  }
144
139
  catch (err) {
@@ -1,7 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { RelationshipType, NodeStatus } from "../../schema.js";
3
3
  import { updateNodeOp, addRelationshipOp, removeRelationshipOp, updateMetadataOp, } from "../../operations/index.js";
4
- import { inputArg, mutationOpts, loadDoc, persistDoc } from "../shared.js";
4
+ import { mutationOpts, loadDoc, persistDoc } from "../shared.js";
5
5
  // ---------------------------------------------------------------------------
6
6
  // CLI helper functions
7
7
  // ---------------------------------------------------------------------------
@@ -25,7 +25,6 @@ function parseMetaValue(val) {
25
25
  // Arg/opt schemas
26
26
  // ---------------------------------------------------------------------------
27
27
  const updateNodeArgs = z.object({
28
- input: inputArg,
29
28
  id: z.string().describe("node ID to update"),
30
29
  });
31
30
  const updateNodeOpts = mutationOpts.extend({
@@ -39,22 +38,17 @@ const updateNodeOpts = mutationOpts.extend({
39
38
  .describe("set lifecycle state (key=value format)"),
40
39
  });
41
40
  const addRelArgs = z.object({
42
- input: inputArg,
43
41
  from: z.string().describe("source node ID"),
44
42
  type: RelationshipType.describe("relationship type"),
45
43
  to: z.string().describe("target node ID"),
46
44
  });
47
45
  const addRelOpts = mutationOpts;
48
46
  const removeRelArgs = z.object({
49
- input: inputArg,
50
47
  from: z.string().describe("source node ID"),
51
48
  type: RelationshipType.describe("relationship type"),
52
49
  to: z.string().describe("target node ID"),
53
50
  });
54
51
  const removeRelOpts = mutationOpts;
55
- const metaArgs = z.object({
56
- input: inputArg,
57
- });
58
52
  const metaOpts = mutationOpts.extend({
59
53
  fields: z
60
54
  .array(z.string())
@@ -72,7 +66,7 @@ const nodeSubcommand = {
72
66
  action(rawArgs, rawOpts) {
73
67
  const args = updateNodeArgs.parse(rawArgs);
74
68
  const opts = updateNodeOpts.parse(rawOpts);
75
- const loaded = loadDoc(args.input);
69
+ const loaded = loadDoc(opts.path);
76
70
  const { doc } = loaded;
77
71
  const node = doc.nodes.find((n) => n.id === args.id);
78
72
  if (!node) {
@@ -126,7 +120,7 @@ const addRelSubcommand = {
126
120
  action(rawArgs, rawOpts) {
127
121
  const args = addRelArgs.parse(rawArgs);
128
122
  const opts = addRelOpts.parse(rawOpts);
129
- const loaded = loadDoc(args.input);
123
+ const loaded = loadDoc(opts.path);
130
124
  const { doc } = loaded;
131
125
  const newDoc = addRelationshipOp({
132
126
  doc,
@@ -151,7 +145,7 @@ const removeRelSubcommand = {
151
145
  action(rawArgs, rawOpts) {
152
146
  const args = removeRelArgs.parse(rawArgs);
153
147
  const opts = removeRelOpts.parse(rawOpts);
154
- const loaded = loadDoc(args.input);
148
+ const loaded = loadDoc(opts.path);
155
149
  const { doc } = loaded;
156
150
  const newDoc = removeRelationshipOp({
157
151
  doc,
@@ -172,16 +166,14 @@ const metaSubcommand = {
172
166
  name: "meta",
173
167
  description: updateMetadataOp.def.description,
174
168
  apiLink: updateMetadataOp.def.name,
175
- args: metaArgs,
176
169
  opts: metaOpts,
177
- action(rawArgs, rawOpts) {
178
- const args = metaArgs.parse(rawArgs);
170
+ action(_rawArgs, rawOpts) {
179
171
  const opts = metaOpts.parse(rawOpts);
180
172
  if (opts.fields.length === 0) {
181
173
  console.error("No metadata fields specified. Use --fields key=value");
182
174
  process.exit(1);
183
175
  }
184
- const loaded = loadDoc(args.input);
176
+ const loaded = loadDoc(opts.path);
185
177
  const { doc } = loaded;
186
178
  const fields = {};
187
179
  for (const kv of opts.fields) {
@@ -1,10 +1,8 @@
1
- import * as z from "zod";
2
1
  import type { CommandDef } from "../define-command.js";
3
- declare const argsSchema: z.ZodObject<{
4
- input: z.ZodString;
5
- }, z.core.$strip>;
6
- declare const optsSchema: z.ZodObject<{
7
- json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
8
- }, z.core.$strip>;
9
- export declare const validateCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
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>;
10
8
  export {};
@@ -1,19 +1,14 @@
1
- import * as z from "zod";
2
1
  import pc from "picocolors";
3
2
  import { validateOp } from "../../operations/index.js";
4
- import { inputArg, readOpts, loadDoc } from "../shared.js";
5
- const argsSchema = z.object({
6
- input: inputArg,
7
- });
3
+ import { readOpts, loadDoc } from "../shared.js";
8
4
  const optsSchema = readOpts;
9
5
  export const validateCommand = {
10
6
  name: "validate",
11
7
  description: validateOp.def.description,
12
8
  apiLink: validateOp.def.name,
13
- args: argsSchema,
14
9
  opts: optsSchema,
15
- action(args) {
16
- const { doc } = loadDoc(args.input);
10
+ action(_args, opts) {
11
+ const { doc } = loadDoc(opts.path);
17
12
  const result = validateOp({ doc });
18
13
  if (result.valid) {
19
14
  console.log(pc.green("Valid SysProM document."));
@@ -1,14 +1,29 @@
1
1
  import * as z from "zod";
2
2
  import { type Format } from "../io.js";
3
3
  import type { SysProMDocument } from "../schema.js";
4
- /** Positional argument for a SysProM document path. */
4
+ /** @deprecated Use --path option in readOpts/mutationOpts instead. */
5
5
  export declare const inputArg: z.ZodString;
6
+ /** Empty args schema for commands that take no positional arguments. */
7
+ export declare const noArgs: z.ZodObject<{}, z.core.$strict>;
8
+ /**
9
+ * Resolve a SysProM document path. If no explicit path is given, search the
10
+ * working directory by priority:
11
+ * 1. .spm.json 2. .spm.md 3. .spm/
12
+ * 4. .sysprom.json 5. .sysprom.md 6. .sysprom/
13
+ * 7. *.spm.json 8. *.spm.md 9. *.spm/
14
+ * 10. *.sysprom.json 11. *.sysprom.md 12. *.sysprom/
15
+ *
16
+ * All matching is case-insensitive. Glob tiers must have exactly one match.
17
+ */
18
+ export declare function resolveInput(input?: string, cwd?: string): string;
6
19
  /** Common output/persistence options for read-only commands. */
7
20
  export declare const readOpts: z.ZodObject<{
21
+ path: z.ZodOptional<z.ZodString>;
8
22
  json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
23
  }, z.core.$strip>;
10
24
  /** Common output/persistence options for mutation commands. */
11
25
  export declare const mutationOpts: z.ZodObject<{
26
+ path: z.ZodOptional<z.ZodString>;
12
27
  json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
28
  dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
14
29
  sync: z.ZodOptional<z.ZodString>;
@@ -20,7 +35,7 @@ export interface LoadedDoc {
20
35
  format: Format;
21
36
  path: string;
22
37
  }
23
- /** Load a document from a CLI input path. */
24
- export declare function loadDoc(input: string): LoadedDoc;
38
+ /** Load a document from a CLI input path (auto-resolved if omitted). */
39
+ export declare function loadDoc(input?: string): LoadedDoc;
25
40
  /** Persist a document and optionally sync to markdown. */
26
41
  export declare function persistDoc(doc: SysProMDocument, loaded: LoadedDoc, opts: MutationOpts): void;
@@ -1,19 +1,116 @@
1
1
  import * as z from "zod";
2
+ import { readdirSync, statSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
2
4
  import { loadDocument, saveDocument } from "../io.js";
3
5
  import { jsonToMarkdownMultiDoc } from "../json-to-md.js";
4
6
  // ---------------------------------------------------------------------------
5
7
  // Reusable CLI schemas — shared across all commands
6
8
  // ---------------------------------------------------------------------------
7
- /** Positional argument for a SysProM document path. */
9
+ /** @deprecated Use --path option in readOpts/mutationOpts instead. */
8
10
  export const inputArg = z
9
11
  .string()
10
12
  .describe("SysProM document path (JSON, .md, or directory)");
13
+ /** Empty args schema for commands that take no positional arguments. */
14
+ export const noArgs = z.object({}).strict();
15
+ /** Shared --path option for specifying the SysProM document location. */
16
+ const pathOpt = z
17
+ .string()
18
+ .optional()
19
+ .describe("SysProM document path (auto-detected if omitted)");
20
+ // ---------------------------------------------------------------------------
21
+ // Default input resolution
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Resolve a SysProM document path. If no explicit path is given, search the
25
+ * working directory by priority:
26
+ * 1. .spm.json 2. .spm.md 3. .spm/
27
+ * 4. .sysprom.json 5. .sysprom.md 6. .sysprom/
28
+ * 7. *.spm.json 8. *.spm.md 9. *.spm/
29
+ * 10. *.sysprom.json 11. *.sysprom.md 12. *.sysprom/
30
+ *
31
+ * All matching is case-insensitive. Glob tiers must have exactly one match.
32
+ */
33
+ export function resolveInput(input, cwd) {
34
+ if (input)
35
+ return input;
36
+ const dir = resolve(cwd ?? ".");
37
+ // Exact names to check, in priority order (case-insensitive)
38
+ const exactNames = [
39
+ ".spm.json",
40
+ ".spm.md",
41
+ ".spm",
42
+ ".sysprom.json",
43
+ ".sysprom.md",
44
+ ".sysprom",
45
+ ];
46
+ const entries = readdirSync(dir);
47
+ for (const name of exactNames) {
48
+ const isDirSuffix = name.endsWith(".spm") || name.endsWith(".sysprom");
49
+ const found = entries.filter((e) => e.toLowerCase() === name);
50
+ if (found.length > 1) {
51
+ throw new Error(`Multiple SysProM documents found: ${found.join(", ")}. Specify one explicitly.`);
52
+ }
53
+ if (found.length === 1) {
54
+ const candidate = join(dir, found[0]);
55
+ if (isDirSuffix) {
56
+ try {
57
+ if (statSync(candidate).isDirectory())
58
+ return candidate;
59
+ }
60
+ catch {
61
+ /* skip */
62
+ }
63
+ }
64
+ else {
65
+ return candidate;
66
+ }
67
+ }
68
+ }
69
+ // Glob suffixes in priority order (case-insensitive)
70
+ const globSuffixes = [
71
+ ".spm.json",
72
+ ".spm.md",
73
+ ".spm",
74
+ ".sysprom.json",
75
+ ".sysprom.md",
76
+ ".sysprom",
77
+ ];
78
+ for (const suffix of globSuffixes) {
79
+ const isDirSuffix = suffix === ".spm" || suffix === ".sysprom";
80
+ const matches = entries
81
+ .filter((e) => {
82
+ const lower = e.toLowerCase();
83
+ return lower.endsWith(suffix) && lower !== suffix;
84
+ })
85
+ .map((e) => join(dir, e))
86
+ .filter((p) => {
87
+ if (isDirSuffix) {
88
+ try {
89
+ return statSync(p).isDirectory();
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ return true;
96
+ });
97
+ if (matches.length === 1)
98
+ return matches[0];
99
+ if (matches.length > 1) {
100
+ const names = matches.map((m) => m.slice(dir.length + 1)).join(", ");
101
+ throw new Error(`Multiple SysProM documents found: ${names}. Specify one explicitly.`);
102
+ }
103
+ }
104
+ throw new Error("No SysProM document found in current directory. Specify a path or run `spm init`.");
105
+ }
11
106
  /** Common output/persistence options for read-only commands. */
12
107
  export const readOpts = z.object({
108
+ path: pathOpt,
13
109
  json: z.boolean().optional().default(false).describe("output as JSON"),
14
110
  });
15
111
  /** Common output/persistence options for mutation commands. */
16
112
  export const mutationOpts = z.object({
113
+ path: pathOpt,
17
114
  json: z.boolean().optional().default(false).describe("output as JSON"),
18
115
  dryRun: z
19
116
  .boolean()
@@ -25,9 +122,9 @@ export const mutationOpts = z.object({
25
122
  .optional()
26
123
  .describe("sync to markdown directory after saving"),
27
124
  });
28
- /** Load a document from a CLI input path. */
125
+ /** Load a document from a CLI input path (auto-resolved if omitted). */
29
126
  export function loadDoc(input) {
30
- return loadDocument(input);
127
+ return loadDocument(resolveInput(input));
31
128
  }
32
129
  /** Persist a document and optionally sync to markdown. */
33
130
  export function persistDoc(doc, loaded, opts) {
package/dist/src/io.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  import { SysProMDocument } from "./schema.js";
2
+ /** Supported serialisation formats for SysProM documents. */
2
3
  export type Format = "json" | "single-md" | "multi-md";
4
+ /** The result of loading a SysProM document from disc. */
3
5
  export interface LoadedDocument {
6
+ /** The parsed SysProM document. */
4
7
  doc: SysProMDocument;
8
+ /** The detected serialisation format. */
5
9
  format: Format;
10
+ /** The resolved absolute path the document was loaded from. */
6
11
  path: string;
7
12
  }
8
13
  /**
@@ -1,4 +1,5 @@
1
1
  import { type SysProMDocument } from "./schema.js";
2
+ /** Options for controlling JSON-to-Markdown conversion. */
2
3
  export interface ConvertOptions {
3
4
  form: "single-file" | "multi-doc";
4
5
  }
@@ -1,4 +1,9 @@
1
1
  import * as z from "zod";
2
+ /**
3
+ * Add a node to a SysProM document. Returns a new document with the node appended.
4
+ *
5
+ * @throws If a node with the same ID already exists.
6
+ */
2
7
  export declare const addNodeOp: import("./define-operation.js").DefinedOperation<z.ZodObject<{
3
8
  doc: z.ZodObject<{
4
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -1,6 +1,11 @@
1
1
  import * as z from "zod";
2
2
  import { defineOperation } from "./define-operation.js";
3
3
  import { SysProMDocument, Node } from "../schema.js";
4
+ /**
5
+ * Add a node to a SysProM document. Returns a new document with the node appended.
6
+ *
7
+ * @throws If a node with the same ID already exists.
8
+ */
4
9
  export const addNodeOp = defineOperation({
5
10
  name: "addNode",
6
11
  description: "Add a node to the document. Throws if the ID already exists.",
@@ -1,4 +1,9 @@
1
1
  import * as z from "zod";
2
+ /**
3
+ * Append a new task to a change node's plan array. Returns a new document with the task added.
4
+ *
5
+ * @throws If the change node is not found.
6
+ */
2
7
  export declare const addPlanTaskOp: import("./define-operation.js").DefinedOperation<z.ZodObject<{
3
8
  doc: z.ZodObject<{
4
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -1,6 +1,11 @@
1
1
  import * as z from "zod";
2
2
  import { defineOperation } from "./define-operation.js";
3
3
  import { SysProMDocument } from "../schema.js";
4
+ /**
5
+ * Append a new task to a change node's plan array. Returns a new document with the task added.
6
+ *
7
+ * @throws If the change node is not found.
8
+ */
4
9
  export const addPlanTaskOp = defineOperation({
5
10
  name: "addPlanTask",
6
11
  description: "Append a new task to a change node's plan array. Returns a new document.",
@@ -1,4 +1,9 @@
1
1
  import * as z from "zod";
2
+ /**
3
+ * Add a relationship to a SysProM document. Returns a new document with the relationship appended.
4
+ *
5
+ * @throws If either endpoint node does not exist in the document.
6
+ */
2
7
  export declare const addRelationshipOp: import("./define-operation.js").DefinedOperation<z.ZodObject<{
3
8
  doc: z.ZodObject<{
4
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -1,6 +1,11 @@
1
1
  import * as z from "zod";
2
2
  import { defineOperation } from "./define-operation.js";
3
3
  import { SysProMDocument, Relationship } from "../schema.js";
4
+ /**
5
+ * Add a relationship to a SysProM document. Returns a new document with the relationship appended.
6
+ *
7
+ * @throws If either endpoint node does not exist in the document.
8
+ */
4
9
  export const addRelationshipOp = defineOperation({
5
10
  name: "addRelationship",
6
11
  description: "Add a relationship to the document. Throws if either endpoint node does not exist.",
@@ -1,4 +1,9 @@
1
1
  import * as z from "zod";
2
+ /**
3
+ * Check a SysProM document for quality issues and warnings beyond schema
4
+ * validation — missing rationale, context, scope, descriptions, orphan nodes,
5
+ * and dangling scope references.
6
+ */
2
7
  export declare const checkOp: import("./define-operation.js").DefinedOperation<z.ZodObject<{
3
8
  doc: z.ZodObject<{
4
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -50,6 +50,11 @@ function performCheck(doc) {
50
50
  }
51
51
  return { warnings, info };
52
52
  }
53
+ /**
54
+ * Check a SysProM document for quality issues and warnings beyond schema
55
+ * validation — missing rationale, context, scope, descriptions, orphan nodes,
56
+ * and dangling scope references.
57
+ */
53
58
  export const checkOp = defineOperation({
54
59
  name: "check",
55
60
  description: "Check a SysProM document for issues and warnings. Performs quality/lint checks beyond schema validation.",