sysprom 1.0.6 → 1.1.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 +29 -28
- package/dist/src/canonical-json.d.ts +2 -0
- package/dist/src/cli/commands/add.d.ts +1 -1
- package/dist/src/cli/commands/add.js +2 -3
- package/dist/src/cli/commands/check.d.ts +6 -8
- package/dist/src/cli/commands/check.js +3 -8
- package/dist/src/cli/commands/graph.d.ts +3 -4
- package/dist/src/cli/commands/graph.js +3 -7
- package/dist/src/cli/commands/init.d.ts +15 -1
- package/dist/src/cli/commands/init.js +57 -27
- package/dist/src/cli/commands/plan.js +21 -39
- package/dist/src/cli/commands/query.js +10 -28
- package/dist/src/cli/commands/remove.d.ts +1 -1
- package/dist/src/cli/commands/remove.js +2 -3
- package/dist/src/cli/commands/rename.d.ts +1 -1
- package/dist/src/cli/commands/rename.js +2 -3
- package/dist/src/cli/commands/search.d.ts +1 -1
- package/dist/src/cli/commands/search.js +2 -3
- package/dist/src/cli/commands/stats.d.ts +6 -8
- package/dist/src/cli/commands/stats.js +3 -8
- package/dist/src/cli/commands/task.js +19 -24
- package/dist/src/cli/commands/update.js +6 -14
- package/dist/src/cli/commands/validate.d.ts +6 -8
- package/dist/src/cli/commands/validate.js +3 -8
- package/dist/src/cli/shared.d.ts +14 -3
- package/dist/src/cli/shared.js +61 -3
- package/dist/src/index.d.ts +1 -33
- package/dist/src/index.js +5 -82
- package/dist/src/io.d.ts +5 -0
- package/dist/src/json-to-md.d.ts +1 -0
- package/dist/src/operations/add-node.d.ts +5 -0
- package/dist/src/operations/add-node.js +5 -0
- package/dist/src/operations/add-plan-task.d.ts +5 -0
- package/dist/src/operations/add-plan-task.js +5 -0
- package/dist/src/operations/add-relationship.d.ts +5 -0
- package/dist/src/operations/add-relationship.js +5 -0
- package/dist/src/operations/check.d.ts +5 -0
- package/dist/src/operations/check.js +5 -0
- package/dist/src/operations/define-operation.d.ts +31 -0
- package/dist/src/operations/define-operation.js +8 -0
- package/dist/src/operations/graph.d.ts +1 -0
- package/dist/src/operations/graph.js +1 -0
- package/dist/src/operations/init-document.d.ts +1 -0
- package/dist/src/operations/init-document.js +1 -0
- package/dist/src/operations/json-to-markdown.d.ts +1 -0
- package/dist/src/operations/json-to-markdown.js +1 -0
- package/dist/src/operations/mark-task-done.d.ts +5 -0
- package/dist/src/operations/mark-task-done.js +5 -0
- package/dist/src/operations/mark-task-undone.d.ts +5 -0
- package/dist/src/operations/mark-task-undone.js +5 -0
- package/dist/src/operations/markdown-to-json.d.ts +1 -0
- package/dist/src/operations/markdown-to-json.js +1 -0
- package/dist/src/operations/next-id.d.ts +6 -0
- package/dist/src/operations/next-id.js +6 -0
- package/dist/src/operations/node-history.d.ts +3 -0
- package/dist/src/operations/node-history.js +2 -0
- package/dist/src/operations/plan-add-task.d.ts +1 -0
- package/dist/src/operations/plan-add-task.js +1 -0
- package/dist/src/operations/plan-gate.d.ts +4 -0
- package/dist/src/operations/plan-gate.js +2 -0
- package/dist/src/operations/plan-init.d.ts +1 -0
- package/dist/src/operations/plan-init.js +1 -0
- package/dist/src/operations/plan-progress.d.ts +2 -0
- package/dist/src/operations/plan-progress.js +1 -0
- package/dist/src/operations/plan-status.d.ts +2 -0
- package/dist/src/operations/plan-status.js +1 -0
- package/dist/src/operations/query-node.d.ts +3 -0
- package/dist/src/operations/query-node.js +2 -0
- package/dist/src/operations/query-nodes.d.ts +1 -0
- package/dist/src/operations/query-nodes.js +1 -0
- package/dist/src/operations/query-relationships.d.ts +1 -0
- package/dist/src/operations/query-relationships.js +1 -0
- package/dist/src/operations/remove-node.d.ts +8 -0
- package/dist/src/operations/remove-node.js +7 -0
- package/dist/src/operations/remove-relationship.d.ts +5 -0
- package/dist/src/operations/remove-relationship.js +5 -0
- package/dist/src/operations/rename.d.ts +7 -0
- package/dist/src/operations/rename.js +7 -0
- package/dist/src/operations/search.d.ts +1 -0
- package/dist/src/operations/search.js +1 -0
- package/dist/src/operations/speckit-diff.d.ts +3 -0
- package/dist/src/operations/speckit-diff.js +2 -0
- package/dist/src/operations/speckit-export.d.ts +1 -0
- package/dist/src/operations/speckit-export.js +1 -0
- package/dist/src/operations/speckit-import.d.ts +1 -0
- package/dist/src/operations/speckit-import.js +1 -0
- package/dist/src/operations/speckit-sync.d.ts +3 -0
- package/dist/src/operations/speckit-sync.js +2 -0
- package/dist/src/operations/state-at.d.ts +3 -0
- package/dist/src/operations/state-at.js +2 -0
- package/dist/src/operations/stats.d.ts +3 -0
- package/dist/src/operations/stats.js +2 -0
- package/dist/src/operations/task-list.d.ts +6 -0
- package/dist/src/operations/task-list.js +6 -0
- package/dist/src/operations/timeline.d.ts +3 -0
- package/dist/src/operations/timeline.js +2 -0
- package/dist/src/operations/trace-from-node.d.ts +3 -0
- package/dist/src/operations/trace-from-node.js +2 -0
- package/dist/src/operations/update-metadata.d.ts +1 -0
- package/dist/src/operations/update-metadata.js +1 -0
- package/dist/src/operations/update-node.d.ts +5 -0
- package/dist/src/operations/update-node.js +5 -0
- package/dist/src/operations/update-plan-task.d.ts +5 -0
- package/dist/src/operations/update-plan-task.js +5 -0
- package/dist/src/operations/validate.d.ts +10 -0
- package/dist/src/operations/validate.js +9 -0
- package/dist/src/schema.d.ts +44 -0
- package/dist/src/schema.js +31 -0
- package/dist/src/speckit/generate.d.ts +6 -0
- package/dist/src/speckit/generate.js +6 -0
- package/dist/src/speckit/parse.d.ts +9 -0
- package/dist/src/speckit/parse.js +6 -0
- package/dist/src/speckit/plan.d.ts +5 -0
- package/dist/src/speckit/project.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,38 +23,38 @@ Both `sysprom` and `spm` are available as commands.
|
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
25
|
# Convert between formats
|
|
26
|
-
spm json2md
|
|
27
|
-
spm md2json
|
|
26
|
+
spm json2md .spm.json ./.spm
|
|
27
|
+
spm md2json ./.spm output.spm.json
|
|
28
28
|
|
|
29
|
-
# Validate and summarise
|
|
30
|
-
spm validate
|
|
31
|
-
spm stats
|
|
29
|
+
# Validate and summarise (auto-detects .spm.json in current directory)
|
|
30
|
+
spm validate
|
|
31
|
+
spm stats
|
|
32
32
|
|
|
33
33
|
# Query nodes and relationships
|
|
34
|
-
spm query nodes
|
|
35
|
-
spm query node
|
|
36
|
-
spm query rels
|
|
37
|
-
spm query trace
|
|
38
|
-
spm query timeline
|
|
39
|
-
spm query state-at
|
|
34
|
+
spm query nodes --type decision
|
|
35
|
+
spm query node D1
|
|
36
|
+
spm query rels --from D1
|
|
37
|
+
spm query trace I1
|
|
38
|
+
spm query timeline
|
|
39
|
+
spm query state-at 2026-03-22
|
|
40
40
|
|
|
41
41
|
# Add nodes (ID auto-generated from type prefix if --id omitted)
|
|
42
|
-
spm add
|
|
43
|
-
spm add
|
|
42
|
+
spm add invariant --name "New Rule" --description "Must hold"
|
|
43
|
+
spm add decision --name "Choose X" \
|
|
44
44
|
--option "OPT-A:Use framework X" --option "OPT-B:Use framework Y" \
|
|
45
45
|
--selected OPT-A --rationale "Lower migration effort"
|
|
46
46
|
|
|
47
47
|
# Remove nodes
|
|
48
|
-
spm remove
|
|
48
|
+
spm remove INV23
|
|
49
49
|
|
|
50
50
|
# Update nodes, relationships, and metadata
|
|
51
|
-
spm update node
|
|
52
|
-
spm update add-rel
|
|
53
|
-
spm update remove-rel
|
|
54
|
-
spm update meta
|
|
51
|
+
spm update node D1 --status deprecated
|
|
52
|
+
spm update add-rel D1 affects EL5
|
|
53
|
+
spm update remove-rel D1 affects EL5
|
|
54
|
+
spm update meta --fields version=2
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
All commands auto-detect
|
|
57
|
+
All commands auto-detect the document — they search the current directory for `.spm.json`, `.spm.md`, or `.spm/` (in that priority order), then fall back to `*.spm.json`, `*.spm.md`, or `*.spm/`. Use `--path` to specify an explicit path.
|
|
58
58
|
|
|
59
59
|
## Programmatic API
|
|
60
60
|
|
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
} from "sysprom";
|
|
102
102
|
|
|
103
103
|
// Validate
|
|
104
|
-
const doc = JSON.parse(fs.readFileSync("
|
|
104
|
+
const doc = JSON.parse(fs.readFileSync(".spm.json", "utf8"));
|
|
105
105
|
const result = validate(doc);
|
|
106
106
|
console.log(result.valid, result.issues);
|
|
107
107
|
|
|
@@ -139,6 +139,7 @@ SysProM models systems as directed graphs across abstraction layers — intent,
|
|
|
139
139
|
| [RFC Processes](https://www.rfc-editor.org/rfc/rfc2026) | ✅ | | | ✅ | 🔶 | | | | | | | | 🔶 | 🔶 |
|
|
140
140
|
| [Ralplan](https://github.com/yeachan-heo/oh-my-claudecode/blob/main/skills/ralplan/SKILL.md) | ✅ | | 🔶 | ✅ | | 🔶 | | | | | | | ✅ | 🔶 |
|
|
141
141
|
| [GSD](https://github.com/gsd-build/get-shit-done) | ✅ | | | 🔶 | | 🔶 | | | | | | | | |
|
|
142
|
+
| [Superpowers](https://github.com/obra/superpowers) | ✅ | 🔶 | 🔶 | 🔶 | 🔶 | ✅ | 🔶 | | ✅ | 🔶 | | ✅ | ✅ | ✅ |
|
|
142
143
|
| **SysProM** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🔶 | | 🔶 | ✅ | ✅ | ✅ | ✅ |
|
|
143
144
|
|
|
144
145
|
✅ = first-class support. 🔶 = partial or implicit.
|
|
@@ -182,26 +183,26 @@ pnpm spm <command> # Run the CLI from source (e.g. pnpm spm validate ...)
|
|
|
182
183
|
|
|
183
184
|
## Self-Description
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
`.spm.json` is SysProM describing itself — the specification, its decisions, invariants, changes, and worked examples are all encoded as a SysProM document. The `./.spm/` folder contains the same content as human-readable Markdown.
|
|
186
187
|
|
|
187
|
-
All significant activity — decisions, changes, new capabilities, and invariants — should be recorded in the self-describing document. Updates can be made either by editing the Markdown files in
|
|
188
|
+
All significant activity — decisions, changes, new capabilities, and invariants — should be recorded in the self-describing document. Updates can be made either by editing the Markdown files in `./.spm/` directly or by using the CLI:
|
|
188
189
|
|
|
189
190
|
```sh
|
|
190
191
|
# Add a decision via the CLI
|
|
191
|
-
spm add
|
|
192
|
+
spm add decision --id D23 --name "My Decision" --context "Why this was needed"
|
|
192
193
|
|
|
193
|
-
# Or edit
|
|
194
|
-
spm md2json
|
|
194
|
+
# Or edit ./.spm/DECISIONS.md directly, then sync
|
|
195
|
+
spm md2json ./.spm .spm.json
|
|
195
196
|
```
|
|
196
197
|
|
|
197
198
|
Keep both representations in sync after any change:
|
|
198
199
|
|
|
199
200
|
```sh
|
|
200
201
|
# JSON → Markdown
|
|
201
|
-
spm json2md
|
|
202
|
+
spm json2md .spm.json ./.spm
|
|
202
203
|
|
|
203
204
|
# Markdown → JSON
|
|
204
|
-
spm md2json
|
|
205
|
+
spm md2json ./.spm .spm.json
|
|
205
206
|
```
|
|
206
207
|
|
|
207
|
-
> **Important:** Always keep
|
|
208
|
+
> **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.
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* - null, true, false as literals
|
|
11
11
|
* - undefined values omitted from objects
|
|
12
12
|
*/
|
|
13
|
+
/** Options for controlling canonical JSON output formatting. */
|
|
13
14
|
export interface FormatOptions {
|
|
15
|
+
/** Indentation string (e.g. `"\t"` or `" "`). Empty string produces compact output. */
|
|
14
16
|
indent?: string;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
@@ -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
|
nodeType: 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
|
dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
10
|
sync: z.ZodOptional<z.ZodString>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { NodeType, NodeStatus } from "../../schema.js";
|
|
3
3
|
import { addNodeOp, nextIdOp } from "../../operations/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { mutationOpts, loadDoc, persistDoc } from "../shared.js";
|
|
5
5
|
const argsSchema = z.object({
|
|
6
|
-
input: inputArg,
|
|
7
6
|
nodeType: z.string().describe("Node type to add"),
|
|
8
7
|
});
|
|
9
8
|
const optsSchema = mutationOpts.extend({
|
|
@@ -31,7 +30,7 @@ export const addCommand = {
|
|
|
31
30
|
console.error("--name is required.");
|
|
32
31
|
process.exit(1);
|
|
33
32
|
}
|
|
34
|
-
const loaded = loadDoc(
|
|
33
|
+
const loaded = loadDoc(opts.path);
|
|
35
34
|
const { doc } = loaded;
|
|
36
35
|
const type = args.nodeType;
|
|
37
36
|
if (!NodeType.is(type)) {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import * as z from "zod";
|
|
2
1
|
import type { CommandDef } from "../define-command.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export declare const checkCommand: 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 checkCommand: CommandDef<typeof noArgs, typeof optsSchema>;
|
|
10
8
|
export {};
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import * as z from "zod";
|
|
2
1
|
import { checkOp } from "../../operations/index.js";
|
|
3
|
-
import {
|
|
4
|
-
const argsSchema = z.object({
|
|
5
|
-
input: inputArg,
|
|
6
|
-
});
|
|
2
|
+
import { readOpts, loadDoc } from "../shared.js";
|
|
7
3
|
const optsSchema = readOpts;
|
|
8
4
|
export const checkCommand = {
|
|
9
5
|
name: "check",
|
|
10
6
|
description: checkOp.def.description,
|
|
11
7
|
apiLink: checkOp.def.name,
|
|
12
|
-
args: argsSchema,
|
|
13
8
|
opts: optsSchema,
|
|
14
|
-
action(
|
|
15
|
-
const { doc } = loadDoc(
|
|
9
|
+
action(_args, opts) {
|
|
10
|
+
const { doc } = loadDoc(opts.path);
|
|
16
11
|
const result = checkOp({ doc });
|
|
17
12
|
if (opts.json) {
|
|
18
13
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import type { CommandDef } from "../define-command.js";
|
|
3
|
-
|
|
4
|
-
input: z.ZodString;
|
|
5
|
-
}, z.core.$strip>;
|
|
3
|
+
import { noArgs } from "../shared.js";
|
|
6
4
|
declare const optsSchema: z.ZodObject<{
|
|
5
|
+
path: z.ZodOptional<z.ZodString>;
|
|
7
6
|
json: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
8
7
|
format: z.ZodOptional<z.ZodEnum<{
|
|
9
8
|
dot: "dot";
|
|
@@ -11,5 +10,5 @@ declare const optsSchema: z.ZodObject<{
|
|
|
11
10
|
}>>;
|
|
12
11
|
type: z.ZodOptional<z.ZodString>;
|
|
13
12
|
}, z.core.$strip>;
|
|
14
|
-
export declare const graphCommand: CommandDef<typeof
|
|
13
|
+
export declare const graphCommand: CommandDef<typeof noArgs, typeof optsSchema>;
|
|
15
14
|
export {};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { graphOp } from "../../operations/index.js";
|
|
3
|
-
import {
|
|
4
|
-
const argsSchema = z.object({
|
|
5
|
-
input: inputArg,
|
|
6
|
-
});
|
|
3
|
+
import { readOpts, loadDoc } from "../shared.js";
|
|
7
4
|
const optsSchema = readOpts.extend({
|
|
8
5
|
format: z.enum(["mermaid", "dot"]).optional().describe("Output format"),
|
|
9
6
|
type: z.string().optional().describe("Filter by relationship type"),
|
|
@@ -12,11 +9,10 @@ export const graphCommand = {
|
|
|
12
9
|
name: "graph",
|
|
13
10
|
description: graphOp.def.description,
|
|
14
11
|
apiLink: graphOp.def.name,
|
|
15
|
-
args: argsSchema,
|
|
16
12
|
opts: optsSchema,
|
|
17
|
-
action(
|
|
13
|
+
action(_args, opts) {
|
|
18
14
|
try {
|
|
19
|
-
const { doc } = loadDoc(
|
|
15
|
+
const { doc } = loadDoc(opts.path);
|
|
20
16
|
const output = graphOp({
|
|
21
17
|
doc,
|
|
22
18
|
format: opts.format ?? "mermaid",
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
1
2
|
import type { CommandDef } from "../define-command.js";
|
|
2
|
-
|
|
3
|
+
declare const argsSchema: z.ZodObject<{
|
|
4
|
+
path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
declare const optsSchema: z.ZodObject<{
|
|
7
|
+
title: z.ZodOptional<z.ZodString>;
|
|
8
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
9
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
dir: "dir";
|
|
11
|
+
json: "json";
|
|
12
|
+
md: "md";
|
|
13
|
+
}>>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export declare const initCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
|
|
16
|
+
export {};
|
|
@@ -1,44 +1,74 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import {
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import { canonicalise } from "../../canonical-json.js";
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
|
+
import { resolve, join } from "node:path";
|
|
5
4
|
import { initDocumentOp } from "../../operations/index.js";
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { saveDocument } from "../../io.js";
|
|
6
|
+
const formatChoices = ["json", "md", "dir"];
|
|
7
|
+
function formatToIoFormat(fmt) {
|
|
8
|
+
switch (fmt) {
|
|
9
|
+
case "json":
|
|
10
|
+
return "json";
|
|
11
|
+
case "md":
|
|
12
|
+
return "single-md";
|
|
13
|
+
case "dir":
|
|
14
|
+
return "multi-md";
|
|
15
|
+
}
|
|
8
16
|
}
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
const suffixMap = {
|
|
18
|
+
json: ".spm.json",
|
|
19
|
+
md: ".spm.md",
|
|
20
|
+
dir: ".spm",
|
|
21
|
+
};
|
|
22
|
+
function resolveInitTarget(pathArg, format) {
|
|
23
|
+
const resolved = resolve(pathArg);
|
|
24
|
+
const isExistingDir = existsSync(resolved) && statSync(resolved).isDirectory();
|
|
25
|
+
if (isExistingDir) {
|
|
26
|
+
const fmt = format ?? "json";
|
|
27
|
+
return {
|
|
28
|
+
outputPath: join(resolved, suffixMap[fmt]),
|
|
29
|
+
ioFormat: formatToIoFormat(fmt),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const fmt = format ?? "dir";
|
|
33
|
+
return {
|
|
34
|
+
outputPath: `${resolved}${suffixMap[fmt]}`,
|
|
35
|
+
ioFormat: formatToIoFormat(fmt),
|
|
36
|
+
};
|
|
11
37
|
}
|
|
38
|
+
const argsSchema = z.object({
|
|
39
|
+
path: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.default(".")
|
|
43
|
+
.describe("Target path (default: current directory)"),
|
|
44
|
+
});
|
|
45
|
+
const optsSchema = z
|
|
46
|
+
.object({
|
|
47
|
+
title: z.string().optional().describe("Document title"),
|
|
48
|
+
scope: z.string().optional().describe("Document scope"),
|
|
49
|
+
format: z
|
|
50
|
+
.enum(formatChoices)
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Output format: json, md, or dir"),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
12
55
|
export const initCommand = {
|
|
13
56
|
name: "init",
|
|
14
57
|
description: initDocumentOp.def.description,
|
|
15
58
|
apiLink: initDocumentOp.def.name,
|
|
16
|
-
args:
|
|
17
|
-
|
|
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(),
|
|
59
|
+
args: argsSchema,
|
|
60
|
+
opts: optsSchema,
|
|
25
61
|
action(args, opts) {
|
|
26
|
-
|
|
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);
|
|
62
|
+
const { outputPath, ioFormat } = resolveInitTarget(args.path, opts.format);
|
|
33
63
|
if (existsSync(outputPath)) {
|
|
34
|
-
console.error(`
|
|
64
|
+
console.error(`Already exists: ${outputPath}`);
|
|
35
65
|
process.exit(1);
|
|
36
66
|
}
|
|
37
67
|
const doc = initDocumentOp({
|
|
38
|
-
title:
|
|
39
|
-
scope:
|
|
68
|
+
title: opts.title ?? "Untitled",
|
|
69
|
+
scope: opts.scope ?? "system",
|
|
40
70
|
});
|
|
41
|
-
|
|
71
|
+
saveDocument(doc, ioFormat, outputPath);
|
|
42
72
|
console.log(`Created ${outputPath}`);
|
|
43
73
|
},
|
|
44
74
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { saveDocument } from "../../io.js";
|
|
4
|
+
import { loadDoc, mutationOpts, persistDoc } from "../shared.js";
|
|
4
5
|
import { planInitOp, planAddTaskOp, planStatusOp, planProgressOp, planGateOp, } from "../../operations/index.js";
|
|
5
6
|
// ============================================================================
|
|
6
7
|
// Subcommands
|
|
@@ -37,10 +38,7 @@ const initSubcommand = {
|
|
|
37
38
|
}
|
|
38
39
|
},
|
|
39
40
|
};
|
|
40
|
-
const
|
|
41
|
-
input: z.string().describe("Path to SysProM document"),
|
|
42
|
-
});
|
|
43
|
-
const addTaskOpts = z.object({
|
|
41
|
+
const addTaskOpts = mutationOpts.pick({ path: true }).extend({
|
|
44
42
|
prefix: z.string().describe("Plan prefix"),
|
|
45
43
|
name: z.string().optional().describe("Task name"),
|
|
46
44
|
parent: z.string().optional().describe("Parent task ID"),
|
|
@@ -49,17 +47,20 @@ const addTaskSubcommand = {
|
|
|
49
47
|
name: "add-task",
|
|
50
48
|
description: planAddTaskOp.def.description,
|
|
51
49
|
apiLink: planAddTaskOp.def.name,
|
|
52
|
-
args: addTaskArgs,
|
|
53
50
|
opts: addTaskOpts,
|
|
54
|
-
action(
|
|
55
|
-
const inputPath = args.input;
|
|
51
|
+
action(_args, opts) {
|
|
56
52
|
const prefix = opts.prefix;
|
|
57
53
|
const name = opts.name;
|
|
58
54
|
const parentId = opts.parent;
|
|
59
55
|
try {
|
|
60
|
-
const
|
|
61
|
-
const newDoc = planAddTaskOp({
|
|
62
|
-
|
|
56
|
+
const loaded = loadDoc(opts.path);
|
|
57
|
+
const newDoc = planAddTaskOp({
|
|
58
|
+
doc: loaded.doc,
|
|
59
|
+
prefix,
|
|
60
|
+
name,
|
|
61
|
+
parent: parentId,
|
|
62
|
+
});
|
|
63
|
+
persistDoc(newDoc, loaded, { ...opts, json: false, dryRun: false });
|
|
63
64
|
const target = parentId ? `to ${parentId}` : `to ${prefix}-PROT-IMPL`;
|
|
64
65
|
console.log(`Added task ${target}`);
|
|
65
66
|
}
|
|
@@ -69,10 +70,7 @@ const addTaskSubcommand = {
|
|
|
69
70
|
}
|
|
70
71
|
},
|
|
71
72
|
};
|
|
72
|
-
const
|
|
73
|
-
input: z.string().describe("Path to SysProM document"),
|
|
74
|
-
});
|
|
75
|
-
const statusOpts = z.object({
|
|
73
|
+
const statusOpts = mutationOpts.pick({ path: true }).extend({
|
|
76
74
|
prefix: z.string().describe("Plan prefix"),
|
|
77
75
|
json: z.boolean().optional().describe("Output as JSON"),
|
|
78
76
|
});
|
|
@@ -80,20 +78,17 @@ const statusSubcommand = {
|
|
|
80
78
|
name: "status",
|
|
81
79
|
description: planStatusOp.def.description,
|
|
82
80
|
apiLink: planStatusOp.def.name,
|
|
83
|
-
args: statusArgs,
|
|
84
81
|
opts: statusOpts,
|
|
85
|
-
action(
|
|
86
|
-
const inputPath = args.input;
|
|
82
|
+
action(_args, opts) {
|
|
87
83
|
const prefix = opts.prefix;
|
|
88
84
|
const asJson = opts.json === true;
|
|
89
85
|
try {
|
|
90
|
-
const { doc } =
|
|
86
|
+
const { doc } = loadDoc(opts.path);
|
|
91
87
|
const status = planStatusOp({ doc, prefix });
|
|
92
88
|
if (asJson) {
|
|
93
89
|
console.log(JSON.stringify(status, null, 2));
|
|
94
90
|
return;
|
|
95
91
|
}
|
|
96
|
-
// Format: Constitution, Spec, Plan, Tasks, Checklist status report
|
|
97
92
|
const formatBoolean = (defined) => defined ? "✅ defined" : "❌ not defined";
|
|
98
93
|
console.log(`Constitution: ${formatBoolean(status.constitution.defined)} (${String(status.constitution.principleCount)} principles)`);
|
|
99
94
|
console.log(`Spec: ${formatBoolean(status.spec.defined)} (${String(status.spec.userStoryCount)} user stories)`);
|
|
@@ -109,10 +104,7 @@ const statusSubcommand = {
|
|
|
109
104
|
}
|
|
110
105
|
},
|
|
111
106
|
};
|
|
112
|
-
const
|
|
113
|
-
input: z.string().describe("Path to SysProM document"),
|
|
114
|
-
});
|
|
115
|
-
const progressOpts = z.object({
|
|
107
|
+
const progressOpts = mutationOpts.pick({ path: true }).extend({
|
|
116
108
|
prefix: z.string().describe("Plan prefix"),
|
|
117
109
|
json: z.boolean().optional().describe("Output as JSON"),
|
|
118
110
|
});
|
|
@@ -120,21 +112,17 @@ const progressSubcommand = {
|
|
|
120
112
|
name: "progress",
|
|
121
113
|
description: planProgressOp.def.description,
|
|
122
114
|
apiLink: planProgressOp.def.name,
|
|
123
|
-
args: progressArgs,
|
|
124
115
|
opts: progressOpts,
|
|
125
|
-
action(
|
|
126
|
-
const inputPath = args.input;
|
|
116
|
+
action(_args, opts) {
|
|
127
117
|
const prefix = opts.prefix;
|
|
128
118
|
const asJson = opts.json === true;
|
|
129
119
|
try {
|
|
130
|
-
const { doc } =
|
|
120
|
+
const { doc } = loadDoc(opts.path);
|
|
131
121
|
const progress = planProgressOp({ doc, prefix });
|
|
132
122
|
if (asJson) {
|
|
133
123
|
console.log(JSON.stringify(progress, null, 2));
|
|
134
124
|
return;
|
|
135
125
|
}
|
|
136
|
-
// Format: ASCII progress bars
|
|
137
|
-
// Bar width: 10 chars. Filled: █, empty: ░. Name padded to 20 chars. Percent right-aligned to 3 chars.
|
|
138
126
|
for (const phase of progress) {
|
|
139
127
|
const filledCount = Math.round((phase.percent / 100) * 10);
|
|
140
128
|
const emptyCount = 10 - filledCount;
|
|
@@ -151,10 +139,7 @@ const progressSubcommand = {
|
|
|
151
139
|
}
|
|
152
140
|
},
|
|
153
141
|
};
|
|
154
|
-
const
|
|
155
|
-
input: z.string().describe("Path to SysProM document"),
|
|
156
|
-
});
|
|
157
|
-
const gateOpts = z.object({
|
|
142
|
+
const gateOpts = mutationOpts.pick({ path: true }).extend({
|
|
158
143
|
prefix: z.string().describe("Plan prefix"),
|
|
159
144
|
phase: z.string().describe("Phase number"),
|
|
160
145
|
json: z.boolean().optional().describe("Output as JSON"),
|
|
@@ -163,10 +148,8 @@ const gateSubcommand = {
|
|
|
163
148
|
name: "gate",
|
|
164
149
|
description: planGateOp.def.description,
|
|
165
150
|
apiLink: planGateOp.def.name,
|
|
166
|
-
args: gateArgs,
|
|
167
151
|
opts: gateOpts,
|
|
168
|
-
action(
|
|
169
|
-
const inputPath = args.input;
|
|
152
|
+
action(_args, opts) {
|
|
170
153
|
const prefix = opts.prefix;
|
|
171
154
|
const phaseNum = parseInt(opts.phase, 10);
|
|
172
155
|
const asJson = opts.json === true;
|
|
@@ -175,13 +158,12 @@ const gateSubcommand = {
|
|
|
175
158
|
process.exit(1);
|
|
176
159
|
}
|
|
177
160
|
try {
|
|
178
|
-
const { doc } =
|
|
161
|
+
const { doc } = loadDoc(opts.path);
|
|
179
162
|
const result = planGateOp({ doc, prefix, phase: phaseNum });
|
|
180
163
|
if (asJson) {
|
|
181
164
|
console.log(JSON.stringify(result, null, 2));
|
|
182
165
|
return;
|
|
183
166
|
}
|
|
184
|
-
// Format: Gate check result with detailed issues
|
|
185
167
|
if (result.ready) {
|
|
186
168
|
console.log(`Gate check for phase ${String(phaseNum)}: ✅ READY`);
|
|
187
169
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import pc from "picocolors";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { textToString } from "../../text.js";
|
|
4
|
-
import {
|
|
4
|
+
import { readOpts, loadDoc } from "../shared.js";
|
|
5
5
|
import { queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, timelineOp, nodeHistoryOp, stateAtOp, } from "../../operations/index.js";
|
|
6
6
|
import { NodeType, NodeStatus } from "../../schema.js";
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
@@ -65,39 +65,27 @@ function printTraceTree(tn, depth) {
|
|
|
65
65
|
// ---------------------------------------------------------------------------
|
|
66
66
|
// Arg/opt schemas
|
|
67
67
|
// ---------------------------------------------------------------------------
|
|
68
|
-
const nodesArgs = z.object({
|
|
69
|
-
input: inputArg,
|
|
70
|
-
});
|
|
71
68
|
const nodesOpts = readOpts.extend({
|
|
72
69
|
type: NodeType.optional().describe("filter by node type"),
|
|
73
70
|
status: NodeStatus.optional().describe("filter by node status"),
|
|
74
71
|
});
|
|
75
72
|
const nodeArgs = z.object({
|
|
76
|
-
input: inputArg,
|
|
77
73
|
id: z.string().describe("node ID to retrieve"),
|
|
78
74
|
});
|
|
79
75
|
const nodeOpts = readOpts;
|
|
80
|
-
const relsArgs = z.object({
|
|
81
|
-
input: inputArg,
|
|
82
|
-
});
|
|
83
76
|
const relsOpts = readOpts.extend({
|
|
84
77
|
from: z.string().optional().describe("filter relationships by source node"),
|
|
85
78
|
to: z.string().optional().describe("filter relationships by target node"),
|
|
86
79
|
type: z.string().optional().describe("filter by relationship type"),
|
|
87
80
|
});
|
|
88
81
|
const traceArgs = z.object({
|
|
89
|
-
input: inputArg,
|
|
90
82
|
id: z.string().describe("node ID to start trace from"),
|
|
91
83
|
});
|
|
92
84
|
const traceOpts = readOpts;
|
|
93
|
-
const timelineArgs = z.object({
|
|
94
|
-
input: inputArg,
|
|
95
|
-
});
|
|
96
85
|
const timelineOpts = readOpts.extend({
|
|
97
86
|
node: z.string().optional().describe("filter events to a specific node"),
|
|
98
87
|
});
|
|
99
88
|
const stateAtArgs = z.object({
|
|
100
|
-
input: inputArg,
|
|
101
89
|
time: z.string().describe("ISO timestamp to query"),
|
|
102
90
|
});
|
|
103
91
|
const stateAtOpts = readOpts;
|
|
@@ -108,12 +96,10 @@ const nodesSubcommand = {
|
|
|
108
96
|
name: "nodes",
|
|
109
97
|
description: queryNodesOp.def.description,
|
|
110
98
|
apiLink: queryNodesOp.def.name,
|
|
111
|
-
args: nodesArgs,
|
|
112
99
|
opts: nodesOpts,
|
|
113
|
-
action(
|
|
114
|
-
const args = nodesArgs.parse(rawArgs);
|
|
100
|
+
action(_rawArgs, rawOpts) {
|
|
115
101
|
const opts = nodesOpts.parse(rawOpts);
|
|
116
|
-
const { doc } = loadDoc(
|
|
102
|
+
const { doc } = loadDoc(opts.path);
|
|
117
103
|
const nodes = queryNodesOp({ doc, type: opts.type, status: opts.status });
|
|
118
104
|
if (opts.json) {
|
|
119
105
|
console.log(JSON.stringify(nodes, null, 2));
|
|
@@ -134,7 +120,7 @@ const nodeSubcommand = {
|
|
|
134
120
|
action(rawArgs, rawOpts) {
|
|
135
121
|
const args = nodeArgs.parse(rawArgs);
|
|
136
122
|
const opts = nodeOpts.parse(rawOpts);
|
|
137
|
-
const { doc } = loadDoc(
|
|
123
|
+
const { doc } = loadDoc(opts.path);
|
|
138
124
|
const result = queryNodeOp({ doc, id: args.id });
|
|
139
125
|
if (!result) {
|
|
140
126
|
console.error(`Node not found: ${args.id}`);
|
|
@@ -162,12 +148,10 @@ const relsSubcommand = {
|
|
|
162
148
|
name: "rels",
|
|
163
149
|
description: queryRelationshipsOp.def.description,
|
|
164
150
|
apiLink: queryRelationshipsOp.def.name,
|
|
165
|
-
args: relsArgs,
|
|
166
151
|
opts: relsOpts,
|
|
167
|
-
action(
|
|
168
|
-
const args = relsArgs.parse(rawArgs);
|
|
152
|
+
action(_rawArgs, rawOpts) {
|
|
169
153
|
const opts = relsOpts.parse(rawOpts);
|
|
170
|
-
const { doc } = loadDoc(
|
|
154
|
+
const { doc } = loadDoc(opts.path);
|
|
171
155
|
const rels = queryRelationshipsOp({
|
|
172
156
|
doc,
|
|
173
157
|
from: opts.from,
|
|
@@ -194,7 +178,7 @@ const traceSubcommand = {
|
|
|
194
178
|
action(rawArgs, rawOpts) {
|
|
195
179
|
const args = traceArgs.parse(rawArgs);
|
|
196
180
|
const opts = traceOpts.parse(rawOpts);
|
|
197
|
-
const { doc } = loadDoc(
|
|
181
|
+
const { doc } = loadDoc(opts.path);
|
|
198
182
|
const trace = traceFromNodeOp({ doc, startId: args.id });
|
|
199
183
|
if (opts.json) {
|
|
200
184
|
console.log(JSON.stringify(trace, null, 2));
|
|
@@ -208,12 +192,10 @@ const timelineSubcommand = {
|
|
|
208
192
|
name: "timeline",
|
|
209
193
|
description: timelineOp.def.description,
|
|
210
194
|
apiLink: timelineOp.def.name,
|
|
211
|
-
args: timelineArgs,
|
|
212
195
|
opts: timelineOpts,
|
|
213
|
-
action(
|
|
214
|
-
const args = timelineArgs.parse(rawArgs);
|
|
196
|
+
action(_rawArgs, rawOpts) {
|
|
215
197
|
const opts = timelineOpts.parse(rawOpts);
|
|
216
|
-
const { doc } = loadDoc(
|
|
198
|
+
const { doc } = loadDoc(opts.path);
|
|
217
199
|
const events = opts.node
|
|
218
200
|
? nodeHistoryOp({ doc, nodeId: opts.node })
|
|
219
201
|
: timelineOp({ doc });
|
|
@@ -241,7 +223,7 @@ const stateAtSubcommand = {
|
|
|
241
223
|
action(rawArgs, rawOpts) {
|
|
242
224
|
const args = stateAtArgs.parse(rawArgs);
|
|
243
225
|
const opts = stateAtOpts.parse(rawOpts);
|
|
244
|
-
const { doc } = loadDoc(
|
|
226
|
+
const { doc } = loadDoc(opts.path);
|
|
245
227
|
const result = stateAtOp({ doc, timestamp: args.time });
|
|
246
228
|
if (opts.json) {
|
|
247
229
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -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
|
nodeId: 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
|
dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
10
|
sync: z.ZodOptional<z.ZodString>;
|