arcane-cli 1.16.1 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -244,6 +244,35 @@ spell report # writes docs/plans/<slug>/show-report.{json,html}
244
244
 
245
245
  Requires **Node.js 18+**.
246
246
 
247
+ ### State export
248
+
249
+ One command answers "what is the state of this repository" for a program -- a cockpit, a mobile companion, another agent -- so nothing has to parse `.arcane.json`, `DECISIONS.md` or `TODO.md` itself:
250
+
251
+ ```bash
252
+ spell state --json # one JSON document on stdout, nothing else; no network
253
+ spell state --json --online # also the latest published arcane-cli and this branch's open pull request (gh / az)
254
+ spell state --json --include-local-paths # hub only: add this machine's clone path per registered repository
255
+ ```
256
+
257
+ The document is derived, never authoritative, and deterministic: two runs differ only in `generated_at`. It carries versions and drift, git topology, the decision log, the TODO/IDEAS/DONE books (first lines and content-hash locators, never bodies), the journal index, the persisted configuration, a hub's venture registry, every feature PRD's status and the effective agent roster; a source that cannot be read becomes `null` plus a line in `warnings[]`. Trimmed:
258
+
259
+ ```json
260
+ {
261
+ "schema_version": "1.0.0",
262
+ "generated_at": "2026-11-04T12:00:00.000Z",
263
+ "generator": { "name": "arcane-cli", "version": "1.17.0" },
264
+ "repository": { "root": "/home/dev/ordovica", "scope": "repo", "role": "consumer", "profile": "full" },
265
+ "versions": { "files": "1.17.0", "cli": "1.17.0", "latest": null, "drift": { "files_behind_cli": false, "cli_behind_latest": null }, "components": [] },
266
+ "git": { "branch": "sessions/2026-11-04-survey", "detached": false, "trunk": "main", "ahead": 2, "behind": 0, "uncommitted": 0,
267
+ "remote": { "provider": "github", "url": "https://github.com/ordovica/survey.git" }, "worktrees": [{ "path": ".", "branch": "sessions/2026-11-04-survey" }], "pull_request": null },
268
+ "indexes": { "decisions": [{ "id": "ADR-001", "title": "Geothermal survey cadence", "status": "Accepted", "date": "2026-10-01", "anchor": "adr-001--geothermal-survey-cadence" }], "todos": { "open": 1, "done": 0, "items": [] }, "ideas": null, "done": null, "journal": [] },
269
+ "config": { "tracking_mode": "internal", "external_provider": null, "push_policy": "open", "role": null, "business_root": null, "spell_scope": null, "content_sensitivity": null },
270
+ "registry": null, "features": [], "agents": null, "warnings": []
271
+ }
272
+ ```
273
+
274
+ The shape is the JSON Schema at [`src/assets/schemas/state.schema.json`](src/assets/schemas/state.schema.json) (`$id` `https://arcaneai.net/schemas/state/v1`), shipped in the package as `dist/assets/schemas/state.schema.json`. Schema `1.0.0` is additive-only: a field is never removed or retyped; a breaking change is a new major version and a new decision record ([ARC-044](DECISIONS.md#arc-044--client-architecture-files-first-state-contract-and-a-local-presence-channel)).
275
+
247
276
  ### Profiles
248
277
 
249
278
  Install everything, or just the slice you want:
@@ -0,0 +1 @@
1
+ - **State export first (ARC-044):** when the installed CLI has `spell state --json`, run `spell state --json --online` once and read the `versions` and `git` sections from its document instead of running the individual version and git commands below: `versions.files`, `versions.cli` and `versions.latest` are the two-axis version check's three readings, `versions.drift` says which axis drifts, and `git.branch`, `git.worktrees`, `git.uncommitted`, `git.ahead`/`git.behind` and `git.pull_request` answer the branch, worktree, uncommitted-changes and open-pull-request questions. The document is derived, never authoritative (the files stay the source of truth), and its `warnings[]` name every source that could not be read. When the command is absent, exits non-zero or prints no document, proceed exactly as the bullets below describe; nothing else in this check changes.
@@ -115,6 +115,9 @@ Then produce output in this exact structure:
115
115
 
116
116
  Before anything else, check:
117
117
 
118
+ <!-- fragment:state-export:start -->
119
+ - **State export first (ARC-044):** when the installed CLI has `spell state --json`, run `spell state --json --online` once and read the `versions` and `git` sections from its document instead of running the individual version and git commands below: `versions.files`, `versions.cli` and `versions.latest` are the two-axis version check's three readings, `versions.drift` says which axis drifts, and `git.branch`, `git.worktrees`, `git.uncommitted`, `git.ahead`/`git.behind` and `git.pull_request` answer the branch, worktree, uncommitted-changes and open-pull-request questions. The document is derived, never authoritative (the files stay the source of truth), and its `warnings[]` name every source that could not be read. When the command is absent, exits non-zero or prints no document, proceed exactly as the bullets below describe; nothing else in this check changes.
120
+ <!-- fragment:state-export:end -->
118
121
  - **Current branch:** Run `git branch --show-current` and `git worktree list`. Report the current state; do not switch during this read-only check. Branch creation/switching is owned by the lazy Mutation Guard. **Caveat (EF-33):** if this repository or its linked worktrees might be reached through more than one filesystem view (a bridged/remote mount, a container bind-mount), `git worktree list` can truthfully report a live, healthy worktree as `prunable` when read from a vantage point that can't resolve its registered path — this is a read-only report, not a reason to act; never run a destructive worktree/branch command on its output alone (see git-conventions.md's Same-Vantage-Point Check section before any such command elsewhere in this or later sessions).
119
122
  - **Isolation primitive (ARC-028 R1–R5):** name where this session will run *before* the Mutation Guard creates anything, and state it in the session output. Every unit of work lives in exactly one of three primitives — the **primary checkout**, a **linked worktree**, or a **full clone**. Select from what the previous bullet's `git worktree list` and `git status` already showed, in this order:
120
123
  - **Managing repository state** — reconciling uncommitted files, pruning branches, fast-forwarding `main`, cutting a release, or any `git worktree` lifecycle operation → **primary checkout** (R1). These are the only operations that require holding `main`, and only one working tree can.
@@ -0,0 +1,386 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://arcaneai.net/schemas/state/v1",
4
+ "title": "Arcane state export",
5
+ "description": "One JSON document per repository, printed by `spell state --json` (ARC-044 decision 1). Derived from the repository's Arcane files, never authoritative, and deterministic: two runs on an unchanged repository differ only in `generated_at` (and `versions.latest` under `--online`). After 1.0.0 the schema is additive-only: a field is never removed or retyped; a breaking change is a new major version and a new decision record.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schema_version",
10
+ "generated_at",
11
+ "generator",
12
+ "repository",
13
+ "versions",
14
+ "git",
15
+ "indexes",
16
+ "config",
17
+ "registry",
18
+ "features",
19
+ "agents",
20
+ "warnings"
21
+ ],
22
+ "properties": {
23
+ "schema_version": {
24
+ "type": "string",
25
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
26
+ "description": "Semantic version of this document's shape. 1.0.0 at first release."
27
+ },
28
+ "generated_at": {
29
+ "type": "string",
30
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z$",
31
+ "description": "When the document was generated, ISO 8601 in UTC. The only field that changes between two runs on an unchanged repository."
32
+ },
33
+ "generator": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": ["name", "version"],
37
+ "properties": {
38
+ "name": { "type": "string", "const": "arcane-cli" },
39
+ "version": { "type": "string", "description": "The CLI version that produced the document." }
40
+ }
41
+ },
42
+ "repository": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["root", "scope", "role", "profile"],
46
+ "properties": {
47
+ "root": { "type": "string", "description": "Absolute real path of the repository the document describes. Every other path in the document is POSIX-relative to it." },
48
+ "scope": { "type": "string", "enum": ["repo", "user"], "description": "Which tier the manifest describes (ARC-045)." },
49
+ "role": { "type": "string", "enum": ["hub", "consumer"], "description": "Resolved hub role (ARC-030): consumer when the manifest does not declare one." },
50
+ "profile": { "type": "string", "description": "The install profile recorded in the manifest." }
51
+ }
52
+ },
53
+ "versions": { "$ref": "#/$defs/versions" },
54
+ "git": {
55
+ "oneOf": [{ "$ref": "#/$defs/git" }, { "type": "null" }],
56
+ "description": "Git topology, or null when the directory is not a git repository."
57
+ },
58
+ "indexes": {
59
+ "type": "object",
60
+ "additionalProperties": false,
61
+ "required": ["decisions", "todos", "ideas", "done", "journal"],
62
+ "properties": {
63
+ "decisions": {
64
+ "oneOf": [{ "type": "array", "items": { "$ref": "#/$defs/decision" } }, { "type": "null" }],
65
+ "description": "DECISIONS.md entries sorted by id, or null when the file is absent."
66
+ },
67
+ "todos": { "oneOf": [{ "$ref": "#/$defs/book" }, { "type": "null" }], "description": "TODO.md, or null when absent." },
68
+ "ideas": { "oneOf": [{ "$ref": "#/$defs/book" }, { "type": "null" }], "description": "IDEAS.md, or null when absent." },
69
+ "done": { "oneOf": [{ "$ref": "#/$defs/book" }, { "type": "null" }], "description": "The append-only done book (DONE.md), or null when absent." },
70
+ "journal": {
71
+ "oneOf": [{ "type": "array", "items": { "$ref": "#/$defs/journalEntry" } }, { "type": "null" }],
72
+ "description": "journal/YYYY-MM-DD-<slug>.md files sorted by date then slug, or null when the directory is absent."
73
+ }
74
+ }
75
+ },
76
+ "config": { "$ref": "#/$defs/config" },
77
+ "registry": {
78
+ "oneOf": [{ "$ref": "#/$defs/registry" }, { "type": "null" }],
79
+ "description": "The hub's venture registry (ARC-030 decision 2), or null on a consumer."
80
+ },
81
+ "features": {
82
+ "oneOf": [{ "type": "array", "items": { "$ref": "#/$defs/feature" } }, { "type": "null" }],
83
+ "description": "Every features/*/PRD.md sorted by slug, or null when features/ is absent."
84
+ },
85
+ "agents": {
86
+ "oneOf": [{ "$ref": "#/$defs/agents" }, { "type": "null" }],
87
+ "description": "The effective agent roster after precedence resolution (ARC-047), or null when no roster applies."
88
+ },
89
+ "warnings": {
90
+ "type": "array",
91
+ "items": { "type": "string" },
92
+ "description": "One line per source that degraded to null. Part of the document, never stderr noise."
93
+ }
94
+ },
95
+ "$defs": {
96
+ "versions": {
97
+ "type": "object",
98
+ "additionalProperties": false,
99
+ "required": ["files", "cli", "latest", "drift", "components"],
100
+ "properties": {
101
+ "files": { "type": "string", "description": "The version the managed files were written at (manifest `version`)." },
102
+ "cli": { "type": "string", "description": "The installed arcane-cli version." },
103
+ "latest": { "type": ["string", "null"], "description": "The latest published arcane-cli version; null unless --online, and null with a warning when the lookup fails." },
104
+ "drift": {
105
+ "type": "object",
106
+ "additionalProperties": false,
107
+ "required": ["files_behind_cli", "cli_behind_latest"],
108
+ "properties": {
109
+ "files_behind_cli": { "type": "boolean" },
110
+ "cli_behind_latest": { "type": ["boolean", "null"], "description": "null when `latest` is null." }
111
+ }
112
+ },
113
+ "components": {
114
+ "type": "array",
115
+ "items": {
116
+ "type": "object",
117
+ "additionalProperties": false,
118
+ "required": ["name", "installedVersion"],
119
+ "properties": {
120
+ "name": { "type": "string" },
121
+ "installedVersion": { "type": "string" }
122
+ }
123
+ },
124
+ "description": "Installed components sorted by name."
125
+ }
126
+ }
127
+ },
128
+ "git": {
129
+ "type": "object",
130
+ "additionalProperties": false,
131
+ "required": ["branch", "detached", "trunk", "ahead", "behind", "uncommitted", "remote", "worktrees", "pull_request"],
132
+ "properties": {
133
+ "branch": { "type": ["string", "null"], "description": "The checked-out branch, or the short commit id when HEAD is detached; null when unreadable." },
134
+ "detached": { "type": "boolean" },
135
+ "trunk": { "type": ["string", "null"], "description": "origin/HEAD's branch, else the first of main/master that exists; null when neither does." },
136
+ "ahead": { "type": ["integer", "null"], "description": "Commits ahead of the upstream; null without an upstream." },
137
+ "behind": { "type": ["integer", "null"], "description": "Commits behind the upstream; null without an upstream." },
138
+ "uncommitted": { "type": ["integer", "null"], "description": "Count of uncommitted changes (status --porcelain lines)." },
139
+ "remote": {
140
+ "oneOf": [
141
+ {
142
+ "type": "object",
143
+ "additionalProperties": false,
144
+ "required": ["provider", "url"],
145
+ "properties": {
146
+ "provider": { "type": "string", "enum": ["github", "ado", "other"] },
147
+ "url": { "type": "string", "description": "origin's URL with any userinfo (credentials) stripped." }
148
+ }
149
+ },
150
+ { "type": "null" }
151
+ ],
152
+ "description": "The origin remote, or null when there is none."
153
+ },
154
+ "worktrees": {
155
+ "oneOf": [
156
+ {
157
+ "type": "array",
158
+ "items": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "required": ["path", "branch"],
162
+ "properties": {
163
+ "path": { "type": "string", "description": "POSIX path relative to repository.root ('.' for the main worktree)." },
164
+ "branch": { "type": ["string", "null"], "description": "null when that worktree's HEAD is detached." }
165
+ }
166
+ }
167
+ },
168
+ { "type": "null" }
169
+ ],
170
+ "description": "Every worktree of the repository sorted by path, or null when the list could not be read."
171
+ },
172
+ "pull_request": {
173
+ "oneOf": [
174
+ {
175
+ "type": "object",
176
+ "additionalProperties": false,
177
+ "required": ["id", "url", "state"],
178
+ "properties": {
179
+ "id": { "type": "integer" },
180
+ "url": { "type": "string" },
181
+ "state": { "type": "string" }
182
+ }
183
+ },
184
+ { "type": "null" }
185
+ ],
186
+ "description": "The open pull request for the current branch. Looked up only under --online; null otherwise, and null with a warning when the lookup fails."
187
+ }
188
+ }
189
+ },
190
+ "decision": {
191
+ "type": "object",
192
+ "additionalProperties": false,
193
+ "required": ["id", "title", "status", "date", "anchor"],
194
+ "properties": {
195
+ "id": { "type": "string", "pattern": "^[A-Z]{2,5}-[0-9]{3,}$" },
196
+ "title": { "type": "string" },
197
+ "status": { "type": ["string", "null"], "description": "First word of the first **Status:** line." },
198
+ "date": { "type": ["string", "null"], "description": "First **Date:** line, YYYY-MM-DD." },
199
+ "anchor": { "type": "string", "description": "GitHub-style slug of the heading." }
200
+ }
201
+ },
202
+ "book": {
203
+ "type": "object",
204
+ "additionalProperties": false,
205
+ "required": ["open", "done", "items"],
206
+ "properties": {
207
+ "open": { "type": "integer" },
208
+ "done": { "type": "integer" },
209
+ "items": {
210
+ "type": "array",
211
+ "items": {
212
+ "type": "object",
213
+ "additionalProperties": false,
214
+ "required": ["text", "section", "locator", "checked"],
215
+ "properties": {
216
+ "text": { "type": "string", "maxLength": 200, "description": "The item's first line without its checkbox." },
217
+ "section": { "type": ["string", "null"], "description": "The nearest preceding heading." },
218
+ "locator": { "type": "string", "description": "<section-slug>/<first 12 hex of sha256(normalized text)>; survives moves, changes only when the text does." },
219
+ "checked": { "type": "boolean" }
220
+ }
221
+ },
222
+ "description": "Items in file order (order is meaning in a book)."
223
+ }
224
+ }
225
+ },
226
+ "journalEntry": {
227
+ "type": "object",
228
+ "additionalProperties": false,
229
+ "required": ["date", "slug", "title", "path"],
230
+ "properties": {
231
+ "date": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
232
+ "slug": { "type": "string" },
233
+ "title": { "type": ["string", "null"] },
234
+ "path": { "type": "string" }
235
+ }
236
+ },
237
+ "config": {
238
+ "type": "object",
239
+ "additionalProperties": false,
240
+ "required": ["tracking_mode", "external_provider", "push_policy", "role", "business_root", "spell_scope", "content_sensitivity"],
241
+ "description": "The persisted manifest answers. null means the question was never asked.",
242
+ "properties": {
243
+ "tracking_mode": { "type": ["string", "null"], "enum": ["internal", "external", null] },
244
+ "external_provider": { "type": ["string", "null"], "enum": ["ado", "github", "jira", "other", null] },
245
+ "push_policy": { "type": ["string", "null"], "enum": ["open", "guarded", "blocked", null] },
246
+ "role": { "type": ["string", "null"], "enum": ["hub", "consumer", null] },
247
+ "business_root": { "type": ["string", "null"] },
248
+ "spell_scope": { "type": ["string", "null"], "enum": ["repo", "user", null] },
249
+ "content_sensitivity": { "type": ["string", "null"], "enum": ["standard", "sensitive", null] }
250
+ }
251
+ },
252
+ "registry": {
253
+ "type": "object",
254
+ "additionalProperties": false,
255
+ "required": ["updated", "ventures"],
256
+ "properties": {
257
+ "updated": { "type": ["string", "null"] },
258
+ "ventures": {
259
+ "type": "array",
260
+ "items": {
261
+ "type": "object",
262
+ "additionalProperties": false,
263
+ "required": ["slug", "name", "aliases", "status", "visibility", "ownership", "tracking", "repos"],
264
+ "properties": {
265
+ "slug": { "type": "string" },
266
+ "name": { "type": ["string", "null"] },
267
+ "aliases": { "type": "array", "items": { "type": "string" } },
268
+ "status": { "type": ["string", "null"] },
269
+ "visibility": { "type": ["string", "null"] },
270
+ "ownership": { "type": ["string", "null"] },
271
+ "tracking": { "description": "The registry's tracking value as written: a provider name, an object with coordinates, or null." },
272
+ "repos": {
273
+ "type": "array",
274
+ "items": {
275
+ "type": "object",
276
+ "additionalProperties": false,
277
+ "required": ["name", "remote", "default"],
278
+ "properties": {
279
+ "name": { "type": ["string", "null"] },
280
+ "remote": { "type": ["string", "null"] },
281
+ "default": { "type": "boolean" },
282
+ "local_path": { "type": ["string", "null"], "description": "Present only with --include-local-paths: the clone path on the machine running the command. Other machines' paths are never exported." }
283
+ }
284
+ }
285
+ }
286
+ }
287
+ },
288
+ "description": "Ventures sorted by slug."
289
+ }
290
+ }
291
+ },
292
+ "feature": {
293
+ "type": "object",
294
+ "additionalProperties": false,
295
+ "required": ["slug", "title", "status", "implementation_status", "tracking", "path"],
296
+ "properties": {
297
+ "slug": { "type": "string", "description": "The features/<slug>/ folder name." },
298
+ "title": { "type": ["string", "null"] },
299
+ "status": { "type": ["string", "null"] },
300
+ "implementation_status": { "type": ["string", "null"] },
301
+ "tracking": {
302
+ "oneOf": [
303
+ {
304
+ "type": "object",
305
+ "additionalProperties": false,
306
+ "required": ["tracking_mode", "external_provider", "adoWorkItemId", "githubIssueId"],
307
+ "properties": {
308
+ "tracking_mode": { "type": ["string", "null"] },
309
+ "external_provider": { "type": ["string", "null"] },
310
+ "adoWorkItemId": { "type": ["integer", "string", "null"] },
311
+ "githubIssueId": { "type": ["integer", "string", "null"] }
312
+ }
313
+ },
314
+ { "type": "null" }
315
+ ]
316
+ },
317
+ "path": { "type": "string" }
318
+ }
319
+ },
320
+ "agents": {
321
+ "type": "object",
322
+ "additionalProperties": false,
323
+ "required": ["schema", "naming_strategy", "agent_profile", "entries"],
324
+ "properties": {
325
+ "schema": { "type": "string", "const": "roster-v2", "description": "The roster shape exported. roster-v3 fields arrive additively with the agent-identity decision record." },
326
+ "naming_strategy": { "type": "string" },
327
+ "agent_profile": { "type": "string" },
328
+ "entries": {
329
+ "type": "array",
330
+ "items": {
331
+ "type": "object",
332
+ "additionalProperties": false,
333
+ "required": ["definition", "name", "id", "epithet", "origin", "definition_revision"],
334
+ "properties": {
335
+ "definition": { "type": "string" },
336
+ "name": { "type": ["string", "null"] },
337
+ "id": { "type": "string" },
338
+ "epithet": { "type": ["string", "null"] },
339
+ "origin": { "type": "string", "enum": ["repo", "user"] },
340
+ "definition_revision": { "type": ["string", "null"], "pattern": "^sha256:[0-9a-f]{64}$", "description": "Hash of the role definition file; null with a warning when the file is missing." }
341
+ }
342
+ },
343
+ "description": "Entries in roster order."
344
+ }
345
+ }
346
+ }
347
+ },
348
+ "examples": [
349
+ {
350
+ "schema_version": "1.0.0",
351
+ "generated_at": "2026-11-04T12:00:00.000Z",
352
+ "generator": { "name": "arcane-cli", "version": "1.17.0" },
353
+ "repository": { "root": "/home/dev/ordovica", "scope": "repo", "role": "consumer", "profile": "full" },
354
+ "versions": {
355
+ "files": "1.17.0",
356
+ "cli": "1.17.0",
357
+ "latest": null,
358
+ "drift": { "files_behind_cli": false, "cli_behind_latest": null },
359
+ "components": [{ "name": "git-conventions", "installedVersion": "1.17.0" }]
360
+ },
361
+ "git": {
362
+ "branch": "sessions/2026-11-04-geothermal-survey",
363
+ "detached": false,
364
+ "trunk": "main",
365
+ "ahead": 2,
366
+ "behind": 0,
367
+ "uncommitted": 0,
368
+ "remote": { "provider": "github", "url": "https://github.com/ordovica/survey.git" },
369
+ "worktrees": [{ "path": ".", "branch": "sessions/2026-11-04-geothermal-survey" }],
370
+ "pull_request": null
371
+ },
372
+ "indexes": {
373
+ "decisions": [{ "id": "ADR-001", "title": "Geothermal survey cadence", "status": "Accepted", "date": "2026-10-01", "anchor": "adr-001--geothermal-survey-cadence" }],
374
+ "todos": { "open": 1, "done": 0, "items": [{ "text": "**Survey the Tidewright coastal site.**", "section": "Open Items", "locator": "open-items/0123456789ab", "checked": false }] },
375
+ "ideas": null,
376
+ "done": null,
377
+ "journal": [{ "date": "2026-11-03", "slug": "survey-kickoff", "title": "Survey kickoff", "path": "journal/2026-11-03-survey-kickoff.md" }]
378
+ },
379
+ "config": { "tracking_mode": "internal", "external_provider": null, "push_policy": "open", "role": null, "business_root": null, "spell_scope": null, "content_sensitivity": "standard" },
380
+ "registry": null,
381
+ "features": [{ "slug": "survey", "title": "PRD: Site survey", "status": "accepted", "implementation_status": "in-progress", "tracking": { "tracking_mode": "internal", "external_provider": null, "adoWorkItemId": null, "githubIssueId": null }, "path": "features/survey/PRD.md" }],
382
+ "agents": null,
383
+ "warnings": []
384
+ }
385
+ ]
386
+ }