@dogsbay/commands 0.2.0-beta.101
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 +137 -0
- package/dist/commands/content.d.ts +78 -0
- package/dist/commands/content.d.ts.map +1 -0
- package/dist/commands/content.js +634 -0
- package/dist/commands/content.js.map +1 -0
- package/dist/commands/examples.d.ts +49 -0
- package/dist/commands/examples.d.ts.map +1 -0
- package/dist/commands/examples.js +173 -0
- package/dist/commands/examples.js.map +1 -0
- package/dist/commands/keys.d.ts +14 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +87 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/link-retarget.d.ts +18 -0
- package/dist/commands/link-retarget.d.ts.map +1 -0
- package/dist/commands/link-retarget.js +104 -0
- package/dist/commands/link-retarget.js.map +1 -0
- package/dist/commands/refactor-files.d.ts +27 -0
- package/dist/commands/refactor-files.d.ts.map +1 -0
- package/dist/commands/refactor-files.js +264 -0
- package/dist/commands/refactor-files.js.map +1 -0
- package/dist/commands/refactor-keys.d.ts +21 -0
- package/dist/commands/refactor-keys.d.ts.map +1 -0
- package/dist/commands/refactor-keys.js +224 -0
- package/dist/commands/refactor-keys.js.map +1 -0
- package/dist/commands/refactor-structure.d.ts +19 -0
- package/dist/commands/refactor-structure.d.ts.map +1 -0
- package/dist/commands/refactor-structure.js +314 -0
- package/dist/commands/refactor-structure.js.map +1 -0
- package/dist/commands/reindex.d.ts +10 -0
- package/dist/commands/reindex.d.ts.map +1 -0
- package/dist/commands/reindex.js +23 -0
- package/dist/commands/reindex.js.map +1 -0
- package/dist/commands/topicmap-edit.d.ts +65 -0
- package/dist/commands/topicmap-edit.d.ts.map +1 -0
- package/dist/commands/topicmap-edit.js +194 -0
- package/dist/commands/topicmap-edit.js.map +1 -0
- package/dist/commands/topicmap.d.ts +13 -0
- package/dist/commands/topicmap.d.ts.map +1 -0
- package/dist/commands/topicmap.js +69 -0
- package/dist/commands/topicmap.js.map +1 -0
- package/dist/commands/validate.d.ts +13 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +753 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/where-used.d.ts +16 -0
- package/dist/commands/where-used.d.ts.map +1 -0
- package/dist/commands/where-used.js +83 -0
- package/dist/commands/where-used.js.map +1 -0
- package/dist/edits.d.ts +54 -0
- package/dist/edits.d.ts.map +1 -0
- package/dist/edits.js +158 -0
- package/dist/edits.js.map +1 -0
- package/dist/engine.d.ts +103 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +115 -0
- package/dist/engine.js.map +1 -0
- package/dist/flags.d.ts +31 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/flags.js +73 -0
- package/dist/flags.js.map +1 -0
- package/dist/frontmatter-schema.d.ts +97 -0
- package/dist/frontmatter-schema.d.ts.map +1 -0
- package/dist/frontmatter-schema.js +240 -0
- package/dist/frontmatter-schema.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes.d.ts +175 -0
- package/dist/indexes.d.ts.map +1 -0
- package/dist/indexes.js +536 -0
- package/dist/indexes.js.map +1 -0
- package/dist/json-schema.d.ts +10 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +50 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/storage.d.ts +68 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +193 -0
- package/dist/storage.js.map +1 -0
- package/dist/workspace.d.ts +30 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +63 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# @dogsbay/commands
|
|
2
|
+
|
|
3
|
+
Typed, headless-first command engine for the Dogsbay platform (Plan 04
|
|
4
|
+
Phase 1). One command definition — a name, a zod input schema, and a
|
|
5
|
+
pure handler over a `Workspace` — yields every surface: the `dogsbay`
|
|
6
|
+
CLI generates its flags from the schema, and the JSON-RPC and MCP
|
|
7
|
+
servers (Phase 3) will consume the same registry. Parity between
|
|
8
|
+
surfaces is by construction, enforced by a schema conformance test.
|
|
9
|
+
|
|
10
|
+
## Architecture
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Storage (injected I/O) NodeStorage | MemoryStorage | (Electron IPC …)
|
|
14
|
+
└─ Workspace lazy indexes + invalidate()/reindex()
|
|
15
|
+
└─ CommandContext what every handler sees
|
|
16
|
+
└─ CommandRegistry.run(name, input, ctx) → CommandResult
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **Storage** is the only I/O boundary. Commands never touch `node:fs`.
|
|
20
|
+
All paths are workspace-root-relative posix paths.
|
|
21
|
+
- **Indexes** are built in one pass: per-doc table (frontmatter,
|
|
22
|
+
headings, anchors, links, includes, `key:` uses), topic-map table,
|
|
23
|
+
key-definition table (from `*.keys.yaml`), and the inverted
|
|
24
|
+
inbound-reference graph.
|
|
25
|
+
- **Results** are envelopes, never exceptions: `{ ok, data, issues }`
|
|
26
|
+
for success (issues = workspace problems), `{ ok: false, error }` for
|
|
27
|
+
input/execution failures.
|
|
28
|
+
|
|
29
|
+
## Built-in commands
|
|
30
|
+
|
|
31
|
+
| Command | What it does |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `validate` | Broken links, missing anchors, missing include targets, undefined keys, key conflicts, malformed keys files, topic-map errors, missing map targets, cross-map cycles |
|
|
34
|
+
| `where-used <target>` | Inbound references to a file, `file#anchor`, or `key:name` — links, images, includes, topic-map entries, key uses |
|
|
35
|
+
| `keys-list` | Every key definition with defining file, conflicts, and uses |
|
|
36
|
+
| `keys-resolve <name>` | Winning definition + all definitions + uses for one key |
|
|
37
|
+
| `topicmap-resolve <map>` | Resolve a topic map (recursive includes + minja variables) through injected storage |
|
|
38
|
+
| `reindex` | Rebuild indexes and report statistics |
|
|
39
|
+
|
|
40
|
+
## Refactor commands (Plan 04 Phase 2)
|
|
41
|
+
|
|
42
|
+
All reuse-safe: mutations are index lookups plus surgical text edits,
|
|
43
|
+
inbound references are rewritten in the same run, and anything that
|
|
44
|
+
cannot be repaired mechanically becomes an issue — never a silent drop.
|
|
45
|
+
Golden-diff tests pin the exact rewrites, and `validate` must stay
|
|
46
|
+
error-free after every refactor.
|
|
47
|
+
|
|
48
|
+
| Command | What it does |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `rename-file <from> <to>` | Move/rename; rewrites inbound links (fragments kept), includes, and topic-map hrefs, AND the moved doc's own outbound relative references |
|
|
51
|
+
| `safe-delete <file> [--force]` | Delete only when nothing references it (links, includes, maps, key hrefs); `--force` deletes and issues each now-broken referrer |
|
|
52
|
+
| `extract-include <file> --start N --end M --snippet path` | Extract a line range into a snippet, replace it with `{% include %}`; refuses ranges that split a code fence |
|
|
53
|
+
| `rename-key <from> <to>` | Rename across keys files, `key:` links, and `{{ var }}` text references (flagged for attribute-name ambiguity) |
|
|
54
|
+
| `keyify <file> <key>` | Define a key for a file and convert inbound direct links to `key:` references (fragment links skipped with a note) |
|
|
55
|
+
| `split-topic <file>` | Split at level-2 headings into sibling topics + a topic map; inbound `#anchor` links follow their sections |
|
|
56
|
+
| `merge-topics <into> --sources a.md --sources b.md` | Merge sources as anchored H2 sections (headings shifted, links/includes rebased, source-to-source links become same-document anchors); inbound refs re-pointed |
|
|
57
|
+
| `topic-read <file>` | Enriched read: title, frontmatter, section outline (levels, ids, slugs, line ranges), links, includes, key uses, inbound refs, directive count; `--includeBody` |
|
|
58
|
+
| `topic-set-title <file> <title>` | Rewrite the H1 text only (keeps `{#id}`); warns when frontmatter `title` differs |
|
|
59
|
+
| `section-replace <file> <section> --body` | Replace one section's content (heading kept unless `--keepHeading false`); selector = `{#id}`, heading text or slug; ambiguity refused |
|
|
60
|
+
| `block-insert <file> --content [--section --position start\|end] [--line N]` | Insert a block; refuses mid-fence |
|
|
61
|
+
| `block-delete <file> <section>` | Delete a section subtree; warns about orphaned inbound `#anchor` links; refuses the H1 |
|
|
62
|
+
| `props-get <file>` / `props-set <file> --set k=v --unset k` | Frontmatter read / in-place set+unset (only the touched keys' lines change; creates frontmatter when absent) |
|
|
63
|
+
| `map-list <map>` | Topic-map entries with dotted pointers |
|
|
64
|
+
| `map-add-ref <map> <href> [--navtitle --parent --index --format --toc --singlePage]` | Add an entry; dangling local href / self-include refused |
|
|
65
|
+
| `map-move-ref <map> <from> [--toParent --index]` | Move a subtree; into-own-subtree refused |
|
|
66
|
+
| `map-remove-ref <map> <at> [--keepChildren]` | Remove (children go with it and are reported) or hoist |
|
|
67
|
+
| `map-set-props <map> <at> [--navtitle --href --format --toc --singlePage]` | Set/unset (null) entry props; new href must resolve |
|
|
68
|
+
|
|
69
|
+
## Using the engine
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import {
|
|
73
|
+
createDefaultRegistry,
|
|
74
|
+
Workspace,
|
|
75
|
+
NodeStorage,
|
|
76
|
+
MemoryStorage,
|
|
77
|
+
} from "@dogsbay/commands";
|
|
78
|
+
|
|
79
|
+
const registry = createDefaultRegistry();
|
|
80
|
+
const workspace = new Workspace(new NodeStorage("/path/to/docs"));
|
|
81
|
+
|
|
82
|
+
const result = await registry.run("validate", {}, { workspace });
|
|
83
|
+
if (result.ok) {
|
|
84
|
+
console.log(result.summary, result.issues);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
In tests (or a browser), swap the storage:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const workspace = new Workspace(
|
|
92
|
+
new MemoryStorage({ "index.md": "# Home\n[link](guide.md)" })
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Defining a command
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { z } from "zod";
|
|
100
|
+
import { defineCommand } from "@dogsbay/commands";
|
|
101
|
+
|
|
102
|
+
export const outlineCommand = defineCommand({
|
|
103
|
+
name: "outline",
|
|
104
|
+
description: "Heading outline of one document",
|
|
105
|
+
input: z.object({
|
|
106
|
+
file: z.string().min(1).describe("Workspace-relative .md path"),
|
|
107
|
+
}).strict(),
|
|
108
|
+
positionals: ["file"], // CLI: `dogsbay outline <file>`
|
|
109
|
+
async run(ctx, { file }) {
|
|
110
|
+
const index = await ctx.workspace.index();
|
|
111
|
+
const doc = index.docs.get(file);
|
|
112
|
+
return { data: { headings: doc?.headings ?? [] } };
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Register it and every surface picks it up. Schema fields may be:
|
|
118
|
+
`string`, `number`, `boolean`, `enum`, `array(string)`,
|
|
119
|
+
`record(string)` — each optionally `.optional()`, `.default()`,
|
|
120
|
+
`.describe()`. The conformance test (`tests/engine.test.ts`) fails CI
|
|
121
|
+
if a registered command uses anything the surface generators cannot
|
|
122
|
+
express.
|
|
123
|
+
|
|
124
|
+
## CLI surface
|
|
125
|
+
|
|
126
|
+
Every command is available on the `dogsbay` CLI with uniform flags:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
dogsbay validate --root docs/ # exit 1 on errors
|
|
130
|
+
dogsbay where-used guide/setup.md --root docs/
|
|
131
|
+
dogsbay where-used key:product-name --root docs/ --json
|
|
132
|
+
dogsbay keys-list --root docs/
|
|
133
|
+
dogsbay topicmap-resolve root.topicmap.yml --variables product=Acme --root docs/
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`--root <dir>` selects the workspace (default: cwd); `--json` prints
|
|
137
|
+
the full machine-readable envelope for CI and agents.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content verbs — the agent broker's typed alternative to "edit the file"
|
|
3
|
+
* (plans/agent-broker.md Phase 3): `topic-read`, `topic-set-title`,
|
|
4
|
+
* `section-replace`, `block-insert`, `block-delete`, `props-get`,
|
|
5
|
+
* `props-set`.
|
|
6
|
+
*
|
|
7
|
+
* All of them are LINE-WISE edits on the shared fence grammar (a `## x`
|
|
8
|
+
* inside a code example is never structure) with the frontmatter split
|
|
9
|
+
* off first, so every byte outside the edited span is untouched by
|
|
10
|
+
* construction — no parse/serialize round-trip that could normalise
|
|
11
|
+
* directives, tables, or whitespace elsewhere in the file. Every mutation
|
|
12
|
+
* invalidates the workspace index and reports what changed; what cannot
|
|
13
|
+
* be done safely (a mid-fence insert, an ambiguous section, deleting the
|
|
14
|
+
* H1) is refused with an issue, never guessed.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
export interface SectionInfo {
|
|
18
|
+
level: number;
|
|
19
|
+
text: string;
|
|
20
|
+
id?: string;
|
|
21
|
+
/** The anchor the site assigns: the explicit id, else the deduped text slug (`usage`, `usage-1`). */
|
|
22
|
+
slug: string;
|
|
23
|
+
/** 0-based line index of the heading within the BODY (after frontmatter). */
|
|
24
|
+
line: number;
|
|
25
|
+
/** 0-based exclusive end line of the section (next heading of level ≤ this, or EOF). */
|
|
26
|
+
end: number;
|
|
27
|
+
/** 0-based exclusive end of the section's OWN text (before its first sub-heading). */
|
|
28
|
+
ownEnd: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Divergence gate (plans/ide-change-review.md §3): a short content hash the
|
|
32
|
+
* agent gets from `topic-read` and hands back as `expect` on a mutation. If
|
|
33
|
+
* the target changed in between (a human typing, another agent), the verb
|
|
34
|
+
* refuses with the current hash instead of writing over the newer text.
|
|
35
|
+
*/
|
|
36
|
+
export declare const contentHash: (text: string) => string;
|
|
37
|
+
export declare const topicReadCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
38
|
+
file: z.ZodString;
|
|
39
|
+
includeBody: z.ZodDefault<z.ZodBoolean>;
|
|
40
|
+
}, z.core.$strict>>;
|
|
41
|
+
export declare const topicSetTitleCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
42
|
+
file: z.ZodString;
|
|
43
|
+
title: z.ZodString;
|
|
44
|
+
expect: z.ZodOptional<z.ZodString>;
|
|
45
|
+
}, z.core.$strict>>;
|
|
46
|
+
export declare const sectionReplaceCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
47
|
+
file: z.ZodString;
|
|
48
|
+
section: z.ZodString;
|
|
49
|
+
body: z.ZodString;
|
|
50
|
+
keepHeading: z.ZodDefault<z.ZodBoolean>;
|
|
51
|
+
expect: z.ZodOptional<z.ZodString>;
|
|
52
|
+
}, z.core.$strict>>;
|
|
53
|
+
export declare const blockInsertCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
54
|
+
file: z.ZodString;
|
|
55
|
+
content: z.ZodString;
|
|
56
|
+
section: z.ZodOptional<z.ZodString>;
|
|
57
|
+
position: z.ZodDefault<z.ZodEnum<{
|
|
58
|
+
start: "start";
|
|
59
|
+
end: "end";
|
|
60
|
+
}>>;
|
|
61
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
62
|
+
expect: z.ZodOptional<z.ZodString>;
|
|
63
|
+
}, z.core.$strict>>;
|
|
64
|
+
export declare const blockDeleteCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
65
|
+
file: z.ZodString;
|
|
66
|
+
section: z.ZodString;
|
|
67
|
+
expect: z.ZodOptional<z.ZodString>;
|
|
68
|
+
}, z.core.$strict>>;
|
|
69
|
+
export declare const propsGetCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
70
|
+
file: z.ZodString;
|
|
71
|
+
}, z.core.$strict>>;
|
|
72
|
+
export declare const propsSetCommand: import("../engine.js").CommandDef<z.ZodObject<{
|
|
73
|
+
file: z.ZodString;
|
|
74
|
+
set: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
|
|
75
|
+
unset: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
76
|
+
expect: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, z.core.$strict>>;
|
|
78
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/commands/content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qGAAqG;IACrG,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;CAChB;AAiFD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,MAAsE,CAAC;AAqGlH,eAAO,MAAM,gBAAgB;;;mBA4D3B,CAAC;AAIH,eAAO,MAAM,oBAAoB;;;;mBAsC/B,CAAC;AAIH,eAAO,MAAM,qBAAqB;;;;;;mBA2ChC,CAAC;AAIH,eAAO,MAAM,kBAAkB;;;;;;;;;;mBA6D7B,CAAC;AAIH,eAAO,MAAM,kBAAkB;;;;mBA2C7B,CAAC;AAIH,eAAO,MAAM,eAAe;;mBAc1B,CAAC;AAKH,eAAO,MAAM,eAAe;;;;;mBAkG1B,CAAC"}
|