sysprom 1.0.0 → 1.0.5

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 (148) hide show
  1. package/README.md +207 -0
  2. package/dist/schema.json +510 -0
  3. package/dist/src/canonical-json.d.ts +23 -0
  4. package/dist/src/canonical-json.js +120 -0
  5. package/dist/src/cli/commands/add.d.ts +22 -0
  6. package/dist/src/cli/commands/add.js +95 -0
  7. package/dist/src/cli/commands/check.d.ts +10 -0
  8. package/dist/src/cli/commands/check.js +33 -0
  9. package/dist/src/cli/commands/graph.d.ts +15 -0
  10. package/dist/src/cli/commands/graph.js +32 -0
  11. package/dist/src/cli/commands/init.d.ts +2 -0
  12. package/dist/src/cli/commands/init.js +44 -0
  13. package/dist/src/cli/commands/json2md.d.ts +2 -0
  14. package/dist/src/cli/commands/json2md.js +60 -0
  15. package/dist/src/cli/commands/md2json.d.ts +2 -0
  16. package/dist/src/cli/commands/md2json.js +29 -0
  17. package/dist/src/cli/commands/plan.d.ts +2 -0
  18. package/dist/src/cli/commands/plan.js +227 -0
  19. package/dist/src/cli/commands/query.d.ts +2 -0
  20. package/dist/src/cli/commands/query.js +275 -0
  21. package/dist/src/cli/commands/remove.d.ts +13 -0
  22. package/dist/src/cli/commands/remove.js +50 -0
  23. package/dist/src/cli/commands/rename.d.ts +14 -0
  24. package/dist/src/cli/commands/rename.js +34 -0
  25. package/dist/src/cli/commands/search.d.ts +11 -0
  26. package/dist/src/cli/commands/search.js +37 -0
  27. package/dist/src/cli/commands/speckit.d.ts +2 -0
  28. package/dist/src/cli/commands/speckit.js +318 -0
  29. package/dist/src/cli/commands/stats.d.ts +10 -0
  30. package/dist/src/cli/commands/stats.js +51 -0
  31. package/dist/src/cli/commands/task.d.ts +2 -0
  32. package/dist/src/cli/commands/task.js +162 -0
  33. package/dist/src/cli/commands/update.d.ts +2 -0
  34. package/dist/src/cli/commands/update.js +219 -0
  35. package/dist/src/cli/commands/validate.d.ts +10 -0
  36. package/dist/src/cli/commands/validate.js +30 -0
  37. package/dist/src/cli/define-command.d.ts +34 -0
  38. package/dist/src/cli/define-command.js +237 -0
  39. package/dist/src/cli/index.d.ts +2 -0
  40. package/dist/src/cli/index.js +3 -0
  41. package/dist/src/cli/program.d.ts +4 -0
  42. package/dist/src/cli/program.js +46 -0
  43. package/dist/src/cli/shared.d.ts +26 -0
  44. package/dist/src/cli/shared.js +41 -0
  45. package/dist/src/generate-schema.d.ts +1 -0
  46. package/dist/src/generate-schema.js +9 -0
  47. package/dist/src/index.d.ts +48 -0
  48. package/dist/src/index.js +99 -0
  49. package/dist/src/io.d.ts +22 -0
  50. package/dist/src/io.js +66 -0
  51. package/dist/src/json-to-md.d.ts +26 -0
  52. package/dist/src/json-to-md.js +498 -0
  53. package/dist/src/md-to-json.d.ts +22 -0
  54. package/dist/src/md-to-json.js +548 -0
  55. package/dist/src/operations/add-node.d.ts +887 -0
  56. package/dist/src/operations/add-node.js +21 -0
  57. package/dist/src/operations/add-plan-task.d.ts +594 -0
  58. package/dist/src/operations/add-plan-task.js +25 -0
  59. package/dist/src/operations/add-relationship.d.ts +635 -0
  60. package/dist/src/operations/add-relationship.js +25 -0
  61. package/dist/src/operations/check.d.ts +301 -0
  62. package/dist/src/operations/check.js +66 -0
  63. package/dist/src/operations/define-operation.d.ts +14 -0
  64. package/dist/src/operations/define-operation.js +21 -0
  65. package/dist/src/operations/graph.d.ts +303 -0
  66. package/dist/src/operations/graph.js +71 -0
  67. package/dist/src/operations/index.d.ts +38 -0
  68. package/dist/src/operations/index.js +45 -0
  69. package/dist/src/operations/init-document.d.ts +299 -0
  70. package/dist/src/operations/init-document.js +26 -0
  71. package/dist/src/operations/json-to-markdown.d.ts +298 -0
  72. package/dist/src/operations/json-to-markdown.js +13 -0
  73. package/dist/src/operations/mark-task-done.d.ts +594 -0
  74. package/dist/src/operations/mark-task-done.js +26 -0
  75. package/dist/src/operations/mark-task-undone.d.ts +594 -0
  76. package/dist/src/operations/mark-task-undone.js +26 -0
  77. package/dist/src/operations/markdown-to-json.d.ts +298 -0
  78. package/dist/src/operations/markdown-to-json.js +13 -0
  79. package/dist/src/operations/next-id.d.ts +322 -0
  80. package/dist/src/operations/next-id.js +29 -0
  81. package/dist/src/operations/node-history.d.ts +313 -0
  82. package/dist/src/operations/node-history.js +55 -0
  83. package/dist/src/operations/plan-add-task.d.ts +595 -0
  84. package/dist/src/operations/plan-add-task.js +18 -0
  85. package/dist/src/operations/plan-gate.d.ts +351 -0
  86. package/dist/src/operations/plan-gate.js +41 -0
  87. package/dist/src/operations/plan-init.d.ts +299 -0
  88. package/dist/src/operations/plan-init.js +17 -0
  89. package/dist/src/operations/plan-progress.d.ts +313 -0
  90. package/dist/src/operations/plan-progress.js +23 -0
  91. package/dist/src/operations/plan-status.d.ts +349 -0
  92. package/dist/src/operations/plan-status.js +41 -0
  93. package/dist/src/operations/query-node.d.ts +1065 -0
  94. package/dist/src/operations/query-node.js +27 -0
  95. package/dist/src/operations/query-nodes.d.ts +594 -0
  96. package/dist/src/operations/query-nodes.js +23 -0
  97. package/dist/src/operations/query-relationships.d.ts +343 -0
  98. package/dist/src/operations/query-relationships.js +27 -0
  99. package/dist/src/operations/remove-node.d.ts +895 -0
  100. package/dist/src/operations/remove-node.js +58 -0
  101. package/dist/src/operations/remove-relationship.d.ts +622 -0
  102. package/dist/src/operations/remove-relationship.js +26 -0
  103. package/dist/src/operations/rename.d.ts +594 -0
  104. package/dist/src/operations/rename.js +113 -0
  105. package/dist/src/operations/search.d.ts +593 -0
  106. package/dist/src/operations/search.js +39 -0
  107. package/dist/src/operations/speckit-diff.d.ts +330 -0
  108. package/dist/src/operations/speckit-diff.js +89 -0
  109. package/dist/src/operations/speckit-export.d.ts +300 -0
  110. package/dist/src/operations/speckit-export.js +17 -0
  111. package/dist/src/operations/speckit-import.d.ts +299 -0
  112. package/dist/src/operations/speckit-import.js +39 -0
  113. package/dist/src/operations/speckit-sync.d.ts +900 -0
  114. package/dist/src/operations/speckit-sync.js +116 -0
  115. package/dist/src/operations/state-at.d.ts +309 -0
  116. package/dist/src/operations/state-at.js +53 -0
  117. package/dist/src/operations/stats.d.ts +324 -0
  118. package/dist/src/operations/stats.js +85 -0
  119. package/dist/src/operations/task-list.d.ts +305 -0
  120. package/dist/src/operations/task-list.js +44 -0
  121. package/dist/src/operations/timeline.d.ts +312 -0
  122. package/dist/src/operations/timeline.js +46 -0
  123. package/dist/src/operations/trace-from-node.d.ts +1197 -0
  124. package/dist/src/operations/trace-from-node.js +36 -0
  125. package/dist/src/operations/update-metadata.d.ts +593 -0
  126. package/dist/src/operations/update-metadata.js +18 -0
  127. package/dist/src/operations/update-node.d.ts +957 -0
  128. package/dist/src/operations/update-node.js +24 -0
  129. package/dist/src/operations/update-plan-task.d.ts +595 -0
  130. package/dist/src/operations/update-plan-task.js +31 -0
  131. package/dist/src/operations/validate.d.ts +310 -0
  132. package/dist/src/operations/validate.js +82 -0
  133. package/dist/src/schema.d.ts +891 -0
  134. package/dist/src/schema.js +356 -0
  135. package/dist/src/speckit/generate.d.ts +7 -0
  136. package/dist/src/speckit/generate.js +546 -0
  137. package/dist/src/speckit/index.d.ts +4 -0
  138. package/dist/src/speckit/index.js +4 -0
  139. package/dist/src/speckit/parse.d.ts +11 -0
  140. package/dist/src/speckit/parse.js +712 -0
  141. package/dist/src/speckit/plan.d.ts +125 -0
  142. package/dist/src/speckit/plan.js +636 -0
  143. package/dist/src/speckit/project.d.ts +39 -0
  144. package/dist/src/speckit/project.js +141 -0
  145. package/dist/src/text.d.ts +23 -0
  146. package/dist/src/text.js +32 -0
  147. package/package.json +86 -8
  148. package/schema.json +510 -0
@@ -0,0 +1,95 @@
1
+ import * as z from "zod";
2
+ import { NodeType, NodeStatus } from "../../schema.js";
3
+ import { addNodeOp, nextIdOp } from "../../operations/index.js";
4
+ import { inputArg, mutationOpts, loadDoc, persistDoc } from "../shared.js";
5
+ const argsSchema = z.object({
6
+ input: inputArg,
7
+ nodeType: z.string().describe("Node type to add"),
8
+ });
9
+ const optsSchema = mutationOpts.extend({
10
+ id: z.string().optional().describe("Node ID (auto-generated if omitted)"),
11
+ name: z.string().describe("Human-readable node name"),
12
+ description: z.string().optional().describe("Node description"),
13
+ status: z.string().optional().describe("Lifecycle status"),
14
+ context: z.string().optional().describe("Decision context"),
15
+ rationale: z.string().optional().describe("Decision rationale"),
16
+ scope: z.array(z.string()).optional().describe("Change scope (repeatable)"),
17
+ selected: z.string().optional().describe("Selected option ID"),
18
+ option: z
19
+ .array(z.string())
20
+ .optional()
21
+ .describe("Option in format 'ID:description' or just 'description' (repeatable)"),
22
+ });
23
+ export const addCommand = {
24
+ name: "add",
25
+ description: addNodeOp.def.description,
26
+ apiLink: addNodeOp.def.name,
27
+ args: argsSchema,
28
+ opts: optsSchema,
29
+ action(args, opts) {
30
+ if (!opts.name) {
31
+ console.error("--name is required.");
32
+ process.exit(1);
33
+ }
34
+ const loaded = loadDoc(args.input);
35
+ const { doc } = loaded;
36
+ const type = args.nodeType;
37
+ if (!NodeType.is(type)) {
38
+ console.error(`Unknown node type: ${type}`);
39
+ process.exit(1);
40
+ }
41
+ const id = opts.id ?? nextIdOp({ doc, type });
42
+ // Build the node from CLI options
43
+ const node = { id, type, name: opts.name };
44
+ if (opts.description) {
45
+ node.description = opts.description;
46
+ }
47
+ if (opts.status) {
48
+ if (!NodeStatus.is(opts.status)) {
49
+ console.error(`Unknown status: ${opts.status}`);
50
+ process.exit(1);
51
+ }
52
+ node.status = opts.status;
53
+ }
54
+ if (opts.context) {
55
+ node.context = opts.context;
56
+ }
57
+ if (opts.rationale) {
58
+ node.rationale = opts.rationale;
59
+ }
60
+ if (opts.scope && opts.scope.length > 0) {
61
+ node.scope = opts.scope;
62
+ }
63
+ if (opts.selected) {
64
+ node.selected = opts.selected;
65
+ }
66
+ if (opts.option && opts.option.length > 0) {
67
+ node.options = opts.option.map((arg, i) => {
68
+ const colonIdx = arg.indexOf(":");
69
+ if (colonIdx >= 0) {
70
+ return {
71
+ id: arg.slice(0, colonIdx),
72
+ description: arg.slice(colonIdx + 1),
73
+ };
74
+ }
75
+ // Auto-generate ID: D26-OPT-A, D26-OPT-B, etc.
76
+ const letter = String.fromCharCode(65 + i); // A, B, C, ...
77
+ return { id: `${id}-OPT-${letter}`, description: arg };
78
+ });
79
+ }
80
+ try {
81
+ const newDoc = addNodeOp({ doc, node });
82
+ persistDoc(newDoc, loaded, opts);
83
+ if (opts.json) {
84
+ console.log(JSON.stringify(node, null, 2));
85
+ }
86
+ else {
87
+ console.log(`${opts.dryRun ? "[dry-run] Would add" : "Added"} ${type} ${id} — ${opts.name}`);
88
+ }
89
+ }
90
+ catch (err) {
91
+ console.error(err instanceof Error ? err.message : String(err));
92
+ process.exit(1);
93
+ }
94
+ },
95
+ };
@@ -0,0 +1,10 @@
1
+ import * as z from "zod";
2
+ 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 checkCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
10
+ export {};
@@ -0,0 +1,33 @@
1
+ import * as z from "zod";
2
+ import { checkOp } from "../../operations/index.js";
3
+ import { inputArg, readOpts, loadDoc } from "../shared.js";
4
+ const argsSchema = z.object({
5
+ input: inputArg,
6
+ });
7
+ const optsSchema = readOpts;
8
+ export const checkCommand = {
9
+ name: "check",
10
+ description: checkOp.def.description,
11
+ apiLink: checkOp.def.name,
12
+ args: argsSchema,
13
+ opts: optsSchema,
14
+ action(args, opts) {
15
+ const { doc } = loadDoc(args.input);
16
+ const result = checkOp({ doc });
17
+ if (opts.json) {
18
+ console.log(JSON.stringify(result, null, 2));
19
+ }
20
+ else {
21
+ if (result.warnings.length === 0 && result.info.length === 0) {
22
+ console.log("No issues found.");
23
+ }
24
+ else {
25
+ for (const w of result.warnings)
26
+ console.log(`⚠ ${w}`);
27
+ for (const i of result.info)
28
+ console.log(`ℹ ${i}`);
29
+ console.log(`\n${String(result.warnings.length)} warning(s), ${String(result.info.length)} info`);
30
+ }
31
+ }
32
+ },
33
+ };
@@ -0,0 +1,15 @@
1
+ import * as z from "zod";
2
+ 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
+ format: z.ZodOptional<z.ZodEnum<{
9
+ dot: "dot";
10
+ mermaid: "mermaid";
11
+ }>>;
12
+ type: z.ZodOptional<z.ZodString>;
13
+ }, z.core.$strip>;
14
+ export declare const graphCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
15
+ export {};
@@ -0,0 +1,32 @@
1
+ import * as z from "zod";
2
+ import { graphOp } from "../../operations/index.js";
3
+ import { inputArg, readOpts, loadDoc } from "../shared.js";
4
+ const argsSchema = z.object({
5
+ input: inputArg,
6
+ });
7
+ const optsSchema = readOpts.extend({
8
+ format: z.enum(["mermaid", "dot"]).optional().describe("Output format"),
9
+ type: z.string().optional().describe("Filter by relationship type"),
10
+ });
11
+ export const graphCommand = {
12
+ name: "graph",
13
+ description: graphOp.def.description,
14
+ apiLink: graphOp.def.name,
15
+ args: argsSchema,
16
+ opts: optsSchema,
17
+ action(args, opts) {
18
+ try {
19
+ const { doc } = loadDoc(args.input);
20
+ const output = graphOp({
21
+ doc,
22
+ format: opts.format ?? "mermaid",
23
+ typeFilter: opts.type,
24
+ });
25
+ console.log(output);
26
+ }
27
+ catch (err) {
28
+ console.error(err instanceof Error ? err.message : String(err));
29
+ process.exit(1);
30
+ }
31
+ },
32
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const initCommand: CommandDef;
@@ -0,0 +1,44 @@
1
+ import * as z from "zod";
2
+ import { writeFileSync, existsSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { canonicalise } from "../../canonical-json.js";
5
+ import { initDocumentOp } from "../../operations/index.js";
6
+ function isArgs(arg) {
7
+ return typeof arg === "object" && arg !== null && "output" in arg;
8
+ }
9
+ function isOpts(opt) {
10
+ return typeof opt === "object" && opt !== null;
11
+ }
12
+ export const initCommand = {
13
+ name: "init",
14
+ description: initDocumentOp.def.description,
15
+ apiLink: initDocumentOp.def.name,
16
+ args: z.object({
17
+ output: z.string().describe("Output file path"),
18
+ }),
19
+ opts: z
20
+ .object({
21
+ title: z.string().optional().describe("Document title"),
22
+ scope: z.string().optional().describe("Document scope"),
23
+ })
24
+ .strict(),
25
+ action(args, opts) {
26
+ if (!isArgs(args))
27
+ throw new Error("Invalid args");
28
+ if (!isOpts(opts))
29
+ throw new Error("Invalid opts");
30
+ const typedArgs = args;
31
+ const typedOpts = opts;
32
+ const outputPath = resolve(typedArgs.output);
33
+ if (existsSync(outputPath)) {
34
+ console.error(`File already exists: ${outputPath}`);
35
+ process.exit(1);
36
+ }
37
+ const doc = initDocumentOp({
38
+ title: typedOpts.title ?? "Untitled",
39
+ scope: typedOpts.scope ?? "system",
40
+ });
41
+ writeFileSync(outputPath, canonicalise(doc, { indent: "\t" }) + "\n");
42
+ console.log(`Created ${outputPath}`);
43
+ },
44
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const json2mdCommand: CommandDef;
@@ -0,0 +1,60 @@
1
+ import * as z from "zod";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { SysProMDocument } from "../../schema.js";
5
+ import { jsonToMarkdown } from "../../json-to-md.js";
6
+ import { jsonToMarkdownOp } from "../../operations/index.js";
7
+ function isArgs(arg) {
8
+ return (typeof arg === "object" && arg !== null && "input" in arg && "output" in arg);
9
+ }
10
+ function isOpts(opt) {
11
+ return typeof opt === "object" && opt !== null;
12
+ }
13
+ export const json2mdCommand = {
14
+ name: "json2md",
15
+ description: jsonToMarkdownOp.def.description,
16
+ apiLink: jsonToMarkdownOp.def.name,
17
+ args: z.object({
18
+ input: z.string().describe("Path to SysProM JSON file"),
19
+ output: z.string().describe("Output path (file or directory)"),
20
+ }),
21
+ opts: z
22
+ .object({
23
+ singleFile: z
24
+ .boolean()
25
+ .optional()
26
+ .describe("Force single-file output format"),
27
+ })
28
+ .strict(),
29
+ action(args, opts) {
30
+ if (!isArgs(args))
31
+ throw new Error("Invalid args");
32
+ if (!isOpts(opts))
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);
38
+ const raw = JSON.parse(readFileSync(inputPath, "utf8"));
39
+ if (!SysProMDocument.is(raw)) {
40
+ const result = SysProMDocument.safeParse(raw);
41
+ if (!result.success) {
42
+ console.error("Input is not a valid SysProM document:");
43
+ for (const issue of result.error.issues) {
44
+ console.error(` ${issue.path.join(".")}: ${issue.message}`);
45
+ }
46
+ }
47
+ process.exit(1);
48
+ }
49
+ const form = typedOpts.singleFile || outputPath.endsWith(".md")
50
+ ? "single-file"
51
+ : "multi-doc";
52
+ jsonToMarkdown(raw, outputPath, { form });
53
+ if (form === "single-file") {
54
+ console.log(`Written to ${outputPath}`);
55
+ }
56
+ else {
57
+ console.log(`Written to ${outputPath}/`);
58
+ }
59
+ },
60
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const md2jsonCommand: CommandDef;
@@ -0,0 +1,29 @@
1
+ import * as z from "zod";
2
+ import { writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { markdownToJson } from "../../md-to-json.js";
5
+ import { canonicalise } from "../../canonical-json.js";
6
+ import { markdownToJsonOp } from "../../operations/index.js";
7
+ function isArgs(arg) {
8
+ return (typeof arg === "object" && arg !== null && "input" in arg && "output" in arg);
9
+ }
10
+ export const md2jsonCommand = {
11
+ name: "md2json",
12
+ description: markdownToJsonOp.def.description,
13
+ apiLink: markdownToJsonOp.def.name,
14
+ args: z.object({
15
+ input: z.string().describe("Path to SysProM Markdown (file or directory)"),
16
+ output: z.string().describe("Output JSON file path"),
17
+ }),
18
+ opts: z.object({}).strict(),
19
+ action(args) {
20
+ if (!isArgs(args))
21
+ throw new Error("Invalid args");
22
+ const typedArgs = args;
23
+ const inputPath = resolve(typedArgs.input);
24
+ const outputPath = resolve(typedArgs.output);
25
+ const doc = markdownToJson(inputPath);
26
+ writeFileSync(outputPath, canonicalise(doc, { indent: "\t" }) + "\n");
27
+ console.log(`Written to ${outputPath}`);
28
+ },
29
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const planCommand: CommandDef;
@@ -0,0 +1,227 @@
1
+ import * as z from "zod";
2
+ import { existsSync } from "node:fs";
3
+ import { loadDocument, saveDocument } from "../../io.js";
4
+ import { planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, } from "../../operations/index.js";
5
+ // ============================================================================
6
+ // Subcommands
7
+ // ============================================================================
8
+ const initArgs = z.object({
9
+ output: z.string().describe("Path to output SysProM file"),
10
+ });
11
+ const initOpts = z.object({
12
+ prefix: z.string().describe("Plan prefix (e.g. PLAN)"),
13
+ name: z.string().optional().describe("Plan name (defaults to prefix)"),
14
+ });
15
+ const initSubcommand = {
16
+ name: "init",
17
+ description: planInitOp.def.description,
18
+ apiLink: planInitOp.def.name,
19
+ args: initArgs,
20
+ opts: initOpts,
21
+ action(args, opts) {
22
+ const outputPath = args.output;
23
+ const prefix = opts.prefix;
24
+ const name = opts.name ?? prefix;
25
+ if (existsSync(outputPath)) {
26
+ console.error(`Output file already exists: ${outputPath}`);
27
+ process.exit(1);
28
+ }
29
+ try {
30
+ const doc = planInitOp({ prefix, name });
31
+ saveDocument(doc, "json", outputPath);
32
+ console.log(`Created ${outputPath} with prefix ${prefix}`);
33
+ }
34
+ catch (err) {
35
+ console.error(err instanceof Error ? err.message : String(err));
36
+ process.exit(1);
37
+ }
38
+ },
39
+ };
40
+ const addTaskArgs = z.object({
41
+ input: z.string().describe("Path to SysProM document"),
42
+ });
43
+ const addTaskOpts = z.object({
44
+ prefix: z.string().describe("Plan prefix"),
45
+ name: z.string().optional().describe("Task name"),
46
+ parent: z.string().optional().describe("Parent task ID"),
47
+ });
48
+ const addTaskSubcommand = {
49
+ name: "add-task",
50
+ description: planAddTaskOp.def.description,
51
+ apiLink: planAddTaskOp.def.name,
52
+ args: addTaskArgs,
53
+ opts: addTaskOpts,
54
+ action(args, opts) {
55
+ const inputPath = args.input;
56
+ const prefix = opts.prefix;
57
+ const name = opts.name;
58
+ const parentId = opts.parent;
59
+ try {
60
+ const { doc, format, path } = loadDocument(inputPath);
61
+ const newDoc = planAddTaskOp({ doc, prefix, name, parent: parentId });
62
+ saveDocument(newDoc, format, path);
63
+ const target = parentId ? `to ${parentId}` : `to ${prefix}-PROT-IMPL`;
64
+ console.log(`Added task ${target}`);
65
+ }
66
+ catch (err) {
67
+ console.error(err instanceof Error ? err.message : String(err));
68
+ process.exit(1);
69
+ }
70
+ },
71
+ };
72
+ const statusArgs = z.object({
73
+ input: z.string().describe("Path to SysProM document"),
74
+ });
75
+ const statusOpts = z.object({
76
+ prefix: z.string().describe("Plan prefix"),
77
+ json: z.boolean().optional().describe("Output as JSON"),
78
+ });
79
+ const statusSubcommand = {
80
+ name: "status",
81
+ description: planStatusOp.def.description,
82
+ apiLink: planStatusOp.def.name,
83
+ args: statusArgs,
84
+ opts: statusOpts,
85
+ action(args, opts) {
86
+ const inputPath = args.input;
87
+ const prefix = opts.prefix;
88
+ const asJson = opts.json === true;
89
+ try {
90
+ const { doc } = loadDocument(inputPath);
91
+ const status = planStatusOp({ doc, prefix });
92
+ if (asJson) {
93
+ console.log(JSON.stringify(status, null, 2));
94
+ return;
95
+ }
96
+ // Format: Constitution, Spec, Plan, Tasks, Checklist status report
97
+ const formatBoolean = (defined) => defined ? "✅ defined" : "❌ not defined";
98
+ console.log(`Constitution: ${formatBoolean(status.constitution.defined)} (${String(status.constitution.principleCount)} principles)`);
99
+ console.log(`Spec: ${formatBoolean(status.spec.defined)} (${String(status.spec.userStoryCount)} user stories)`);
100
+ console.log(`Plan: ${formatBoolean(status.plan.defined)} (${String(status.plan.phaseCount)} phases)`);
101
+ console.log(`Tasks: ${String(status.tasks.done)}/${String(status.tasks.total)} done`);
102
+ console.log(`Checklist: ${formatBoolean(status.checklist.defined)} (${String(status.checklist.done)}/${String(status.checklist.total)})`);
103
+ console.log();
104
+ console.log(`Next: ${status.nextStep}`);
105
+ }
106
+ catch (err) {
107
+ console.error(err instanceof Error ? err.message : String(err));
108
+ process.exit(1);
109
+ }
110
+ },
111
+ };
112
+ const progressArgs = z.object({
113
+ input: z.string().describe("Path to SysProM document"),
114
+ });
115
+ const progressOpts = z.object({
116
+ prefix: z.string().describe("Plan prefix"),
117
+ json: z.boolean().optional().describe("Output as JSON"),
118
+ });
119
+ const progressSubcommand = {
120
+ name: "progress",
121
+ description: planProgressOp.def.description,
122
+ apiLink: planProgressOp.def.name,
123
+ args: progressArgs,
124
+ opts: progressOpts,
125
+ action(args, opts) {
126
+ const inputPath = args.input;
127
+ const prefix = opts.prefix;
128
+ const asJson = opts.json === true;
129
+ try {
130
+ const { doc } = loadDocument(inputPath);
131
+ const progress = planProgressOp({ doc, prefix });
132
+ if (asJson) {
133
+ console.log(JSON.stringify(progress, null, 2));
134
+ return;
135
+ }
136
+ // Format: ASCII progress bars
137
+ // Bar width: 10 chars. Filled: █, empty: ░. Name padded to 20 chars. Percent right-aligned to 3 chars.
138
+ for (const phase of progress) {
139
+ const filledCount = Math.round((phase.percent / 100) * 10);
140
+ const emptyCount = 10 - filledCount;
141
+ const bar = "█".repeat(filledCount) + "░".repeat(emptyCount);
142
+ const name = phase.name.padEnd(20);
143
+ const percent = String(phase.percent).padStart(3);
144
+ const ratio = `(${String(phase.done)}/${String(phase.total)})`;
145
+ console.log(`${name} [${bar}] ${percent}% ${ratio}`);
146
+ }
147
+ }
148
+ catch (err) {
149
+ console.error(err instanceof Error ? err.message : String(err));
150
+ process.exit(1);
151
+ }
152
+ },
153
+ };
154
+ const gateArgs = z.object({
155
+ input: z.string().describe("Path to SysProM document"),
156
+ });
157
+ const gateOpts = z.object({
158
+ prefix: z.string().describe("Plan prefix"),
159
+ phase: z.string().describe("Phase number"),
160
+ json: z.boolean().optional().describe("Output as JSON"),
161
+ });
162
+ const gateSubcommand = {
163
+ name: "gate",
164
+ description: planGateOp.def.description,
165
+ apiLink: planGateOp.def.name,
166
+ args: gateArgs,
167
+ opts: gateOpts,
168
+ action(args, opts) {
169
+ const inputPath = args.input;
170
+ const prefix = opts.prefix;
171
+ const phaseNum = parseInt(opts.phase, 10);
172
+ const asJson = opts.json === true;
173
+ if (isNaN(phaseNum) || phaseNum < 1) {
174
+ console.error(`Invalid phase number: ${opts.phase}`);
175
+ process.exit(1);
176
+ }
177
+ try {
178
+ const { doc } = loadDocument(inputPath);
179
+ const result = planGateOp({ doc, prefix, phase: phaseNum });
180
+ if (asJson) {
181
+ console.log(JSON.stringify(result, null, 2));
182
+ return;
183
+ }
184
+ // Format: Gate check result with detailed issues
185
+ if (result.ready) {
186
+ console.log(`Gate check for phase ${String(phaseNum)}: ✅ READY`);
187
+ }
188
+ else {
189
+ console.log(`Gate check for phase ${String(phaseNum)}: ❌ NOT READY`);
190
+ for (const issue of result.issues) {
191
+ switch (issue.kind) {
192
+ case "previous_tasks_incomplete":
193
+ console.log(` ❌ Phase ${String(issue.phase)} has ${String(issue.remaining)} incomplete tasks`);
194
+ break;
195
+ case "user_story_no_change":
196
+ console.log(` ❌ ${issue.storyId} has no implementing change`);
197
+ break;
198
+ case "user_story_no_acceptance_criteria":
199
+ console.log(` ❌ ${issue.storyId} has no acceptance criteria`);
200
+ break;
201
+ case "fr_no_change":
202
+ console.log(` ❌ ${issue.frId} has no implementing change`);
203
+ break;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ catch (err) {
209
+ console.error(err instanceof Error ? err.message : String(err));
210
+ process.exit(1);
211
+ }
212
+ },
213
+ };
214
+ // ============================================================================
215
+ // Main command
216
+ // ============================================================================
217
+ export const planCommand = {
218
+ name: "plan",
219
+ description: "Manage project plans and phase gates",
220
+ subcommands: [
221
+ initSubcommand,
222
+ addTaskSubcommand,
223
+ statusSubcommand,
224
+ progressSubcommand,
225
+ gateSubcommand,
226
+ ],
227
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from "../define-command.js";
2
+ export declare const queryCommand: CommandDef;