artshelf 0.9.0 → 0.10.1
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/CHANGELOG.md +39 -0
- package/README.md +12 -7
- package/SPEC.md +40 -8
- package/dist/src/cli.js +372 -28
- package/docs/agent-monitor.html +10 -2
- package/docs/agent-review.html +8 -2
- package/docs/agent-usage.html +14 -0
- package/docs/agent-usage.md +17 -0
- package/docs/reference.html +34 -11
- package/package.json +1 -1
- package/skills/artshelf/SKILL.md +18 -20
package/CHANGELOG.md
CHANGED
|
@@ -64,6 +64,45 @@
|
|
|
64
64
|
and `artshelf ledgers help` aliases, advertised short `-h`/`-v` flags, and
|
|
65
65
|
reclassified `--ledger`, `--registry`, and `--all` as command-specific scope
|
|
66
66
|
flags instead of global options.
|
|
67
|
+
- Added an `--agent` render mode to `artshelf review`, `status`, and `doctor`: a
|
|
68
|
+
compact, deterministic single-line JSON decision packet (health, counts,
|
|
69
|
+
attention categories or classified decision groups, blockers, the next safe
|
|
70
|
+
action, and a verification command) tuned for agents acting on results.
|
|
71
|
+
`--agent` takes precedence over `--json`, while `--json` stays the full,
|
|
72
|
+
backward-compatible audit report. The default human renders of these three
|
|
73
|
+
commands now lead each ledger and summary line with a `✓`/`⚠` attention glyph
|
|
74
|
+
(plain Unicode, no color) so redirected output stays clean.
|
|
75
|
+
- Shortened the automatic update-check cache so no-update, failed, missing, or
|
|
76
|
+
null results expire after 1 hour while update-available results keep the
|
|
77
|
+
24-hour TTL, letting newly published releases surface sooner. `artshelf update`
|
|
78
|
+
forces a fresh latest-version check instead of trusting a stale no-update
|
|
79
|
+
cache, `ARTSHELF_NO_UPDATE_CHECK_TTL_MS` overrides the no-update/failed TTL
|
|
80
|
+
(falling back to `ARTSHELF_UPDATE_CHECK_TTL_MS` for compatibility), and a
|
|
81
|
+
non-numeric TTL value falls back to the default instead of disabling expiry.
|
|
82
|
+
|
|
83
|
+
## [0.10.1](https://github.com/calvinnwq/artshelf/compare/artshelf-v0.10.0...artshelf-v0.10.1) (2026-06-12)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
### Bug Fixes
|
|
87
|
+
|
|
88
|
+
* **cli:** shorten no-update cache TTL for update checks ([d41e49e](https://github.com/calvinnwq/artshelf/commit/d41e49e7d5da02dfaa86fb70eaa7d5e7fb3d543e))
|
|
89
|
+
* **cli:** split update-check cache TTL so new releases surface sooner ([5afcfaa](https://github.com/calvinnwq/artshelf/commit/5afcfaafac4941b71f6a84c694139a64774a1d59))
|
|
90
|
+
|
|
91
|
+
## [0.10.0](https://github.com/calvinnwq/artshelf/compare/artshelf-v0.9.0...artshelf-v0.10.0) (2026-06-12)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
### Features
|
|
95
|
+
|
|
96
|
+
* **cli:** add --agent decision-packet render mode for review, status, and doctor ([4d2dee0](https://github.com/calvinnwq/artshelf/commit/4d2dee099569803b887ae49438b0747d1330ec5d))
|
|
97
|
+
* **cli:** add --agent render mode and implement status --agent ([36f8e78](https://github.com/calvinnwq/artshelf/commit/36f8e7839d535fcabddadfc616ba518a9b444114))
|
|
98
|
+
* **cli:** add ✓/⚠ attention glyphs to human renders of status/doctor/review ([6f6cbe8](https://github.com/calvinnwq/artshelf/commit/6f6cbe85d54886cfd137791863e1b3554ca908f0))
|
|
99
|
+
* **cli:** implement artshelf doctor --agent compact decision packet ([d9abd4e](https://github.com/calvinnwq/artshelf/commit/d9abd4e75a7f4b2898eeacc3b3404221f4456bd4))
|
|
100
|
+
* **cli:** implement artshelf review --agent compact decision packet ([6f5476c](https://github.com/calvinnwq/artshelf/commit/6f5476ca987de3190f7a8760c6bb9c1efa8b9fce))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### Bug Fixes
|
|
104
|
+
|
|
105
|
+
* **cli:** preserve ledger scope in agent next actions ([a583683](https://github.com/calvinnwq/artshelf/commit/a583683064cdd16dd929766dc01f23fc31fa50e7))
|
|
67
106
|
|
|
68
107
|
## [0.9.0](https://github.com/calvinnwq/artshelf/compare/artshelf-v0.8.0...artshelf-v0.9.0) (2026-06-11)
|
|
69
108
|
|
package/README.md
CHANGED
|
@@ -62,12 +62,14 @@ install with `npm unlink -g artshelf`.
|
|
|
62
62
|
</details>
|
|
63
63
|
|
|
64
64
|
Artshelf checks npm occasionally and prints a non-blocking notice to stderr when
|
|
65
|
-
a newer published version is available.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
|
|
65
|
+
a newer published version is available. Available-update results are cached for
|
|
66
|
+
24 hours by default; failed, missing, or no-update results are cached for 1 hour
|
|
67
|
+
so a newly published release is noticed sooner. Run `artshelf update` only for
|
|
68
|
+
npm global installs; it forces a fresh latest-version check before upgrading
|
|
69
|
+
with `npm install -g artshelf@latest`. pnpm global installs should update with
|
|
70
|
+
`pnpm add -g artshelf@latest`, and source installs still update by pulling,
|
|
71
|
+
rebuilding, and linking the checkout. Set `ARTSHELF_NO_UPDATE_CHECK=1` for
|
|
72
|
+
scheduled jobs that must avoid network and update-cache writes.
|
|
71
73
|
|
|
72
74
|
### Recommended agent setup
|
|
73
75
|
|
|
@@ -112,6 +114,8 @@ destructive deletion.
|
|
|
112
114
|
- **Trash before delete** — `cleanup=delete` stays refused; physical deletion
|
|
113
115
|
needs its own reviewed trash purge. No silent deletion, ever.
|
|
114
116
|
- **`--json` on every command**, so agents can act on structured output.
|
|
117
|
+
- **`--agent` on `review`/`status`/`doctor`**, a compact, token-efficient
|
|
118
|
+
decision packet for agents, while the default render stays human-scannable.
|
|
115
119
|
|
|
116
120
|
## Reference
|
|
117
121
|
|
|
@@ -143,7 +147,8 @@ artshelf resolve <id> --status resolved --reason "inspected and no longer needed
|
|
|
143
147
|
Use `artshelf help` for a grouped command list, then `artshelf <command> --help`
|
|
144
148
|
or `artshelf help <command>` for focused details. Nested commands such as
|
|
145
149
|
`artshelf trash purge --help` and `artshelf ledgers add --help` show only that
|
|
146
|
-
subcommand. All core commands support `--json`;
|
|
150
|
+
subcommand. All core commands support `--json`; `review`, `status`, and `doctor`
|
|
151
|
+
also take `--agent` for a compact decision packet; `--ledger`, `--registry`, and
|
|
147
152
|
`--all` are scope flags only on commands that list them.
|
|
148
153
|
</details>
|
|
149
154
|
|
package/SPEC.md
CHANGED
|
@@ -242,7 +242,9 @@ files or writing a plan.
|
|
|
242
242
|
|
|
243
243
|
```bash
|
|
244
244
|
artshelf review --json
|
|
245
|
+
artshelf review --agent
|
|
245
246
|
artshelf review --all --json
|
|
247
|
+
artshelf review --all --agent
|
|
246
248
|
```
|
|
247
249
|
|
|
248
250
|
`review` is the compact report surface for scheduled checks. `--all` reads every
|
|
@@ -257,6 +259,16 @@ triage count and states the same next safe action (repair broken ledgers, dry-ru
|
|
|
257
259
|
cleanup, inspect missing paths, or nothing to do). Review never writes a plan, so
|
|
258
260
|
the next action always points at an explicit follow-up command.
|
|
259
261
|
|
|
262
|
+
`review`, `status`, and `doctor` share three render modes. The default human
|
|
263
|
+
render leads each ledger and summary line with a `✓`/`⚠` attention glyph; `--json`
|
|
264
|
+
stays the full, backward-compatible audit report; and `--agent` emits a compact,
|
|
265
|
+
deterministic single-line JSON decision packet for agents, taking precedence over
|
|
266
|
+
`--json` when both are passed. For `review`, the packet sorts records into
|
|
267
|
+
ready-for-approval, needs-review-first, and blocked groups. Because review is
|
|
268
|
+
read-only and never mints a cleanup plan, the only exact approval target it emits
|
|
269
|
+
is `resolve missing`; cleanup-eligible records stay needs-review-first and point
|
|
270
|
+
at `cleanup --dry-run`, which mints the reviewed plan id to approve.
|
|
271
|
+
|
|
260
272
|
### `artshelf doctor`
|
|
261
273
|
|
|
262
274
|
Reports whether Artshelf is healthy on the current machine without mutating
|
|
@@ -265,6 +277,7 @@ anything.
|
|
|
265
277
|
```bash
|
|
266
278
|
artshelf doctor
|
|
267
279
|
artshelf doctor --json
|
|
280
|
+
artshelf doctor --agent
|
|
268
281
|
artshelf doctor --ledger <path>
|
|
269
282
|
artshelf doctor --registry <path>
|
|
270
283
|
```
|
|
@@ -284,7 +297,11 @@ A healthy machine exits 0. A broken registry file or any stale or invalid
|
|
|
284
297
|
registered ledger exits non-zero with actionable errors. Humans should run
|
|
285
298
|
`artshelf doctor` after install or when `--all` commands behave unexpectedly; agents
|
|
286
299
|
may run it on a schedule to catch stale registry entries before relying on
|
|
287
|
-
cleanup planning. Doctor never creates plans, receipts, or records.
|
|
300
|
+
cleanup planning. Doctor never creates plans, receipts, or records. Like `review`
|
|
301
|
+
and `status`, `doctor` accepts `--agent` for a compact single-line JSON decision
|
|
302
|
+
packet (health, registry and registered-ledger health, blockers, cleanup-safety
|
|
303
|
+
posture, next action, and a verify command); `--agent` takes precedence over
|
|
304
|
+
`--json`.
|
|
288
305
|
|
|
289
306
|
### `artshelf status`
|
|
290
307
|
|
|
@@ -293,7 +310,9 @@ The lightweight daily "what is going on?" view across ledgers.
|
|
|
293
310
|
```bash
|
|
294
311
|
artshelf status
|
|
295
312
|
artshelf status --json
|
|
313
|
+
artshelf status --agent
|
|
296
314
|
artshelf status --all --json
|
|
315
|
+
artshelf status --all --agent
|
|
297
316
|
artshelf status --all --registry <path> --json
|
|
298
317
|
```
|
|
299
318
|
|
|
@@ -312,7 +331,9 @@ never creates plans or receipts and never mutates records. A healthy machine
|
|
|
312
331
|
exits 0. In `--all` mode, a broken registry or any stale or invalid registered
|
|
313
332
|
ledger exits non-zero. Due entries are normal operational state and do not change
|
|
314
333
|
the exit code. With single `--ledger`, a not-yet-created ledger reports empty
|
|
315
|
-
counts.
|
|
334
|
+
counts. Like `review` and `doctor`, `status` accepts `--agent` for a compact
|
|
335
|
+
single-line JSON decision packet (health, counts, attention categories, blockers,
|
|
336
|
+
next action, and a verify command); `--agent` takes precedence over `--json`.
|
|
316
337
|
|
|
317
338
|
### `artshelf update`
|
|
318
339
|
|
|
@@ -332,12 +353,17 @@ Rules:
|
|
|
332
353
|
- Read-only command guarantees refer to ledger and artifact mutation; automatic
|
|
333
354
|
update-check cache writes are separate and can be disabled.
|
|
334
355
|
- Update notices must never pollute JSON stdout.
|
|
335
|
-
- Automatic checks cache
|
|
336
|
-
`~/.artshelf/update-check.json` by default
|
|
356
|
+
- Automatic checks cache latest-version lookups at
|
|
357
|
+
`~/.artshelf/update-check.json` by default. Cached update-available results
|
|
358
|
+
(`latest > current`) keep the long 24-hour TTL; cached no-update, failed,
|
|
359
|
+
missing, or null results use a shorter 1-hour TTL so newly published releases
|
|
360
|
+
are noticed sooner.
|
|
337
361
|
- `ARTSHELF_NO_UPDATE_CHECK=1` disables automatic checks for scheduled jobs,
|
|
338
362
|
tests, and no-network environments.
|
|
339
363
|
- `ARTSHELF_UPDATE_CACHE` overrides the update-cache path,
|
|
340
|
-
`ARTSHELF_UPDATE_CHECK_TTL_MS` overrides the cache TTL,
|
|
364
|
+
`ARTSHELF_UPDATE_CHECK_TTL_MS` overrides the update-available cache TTL,
|
|
365
|
+
`ARTSHELF_NO_UPDATE_CHECK_TTL_MS` overrides the no-update/failed cache TTL
|
|
366
|
+
(falling back to `ARTSHELF_UPDATE_CHECK_TTL_MS` for compatibility), and
|
|
341
367
|
`ARTSHELF_NPM_REGISTRY_URL` overrides the npm latest-version endpoint.
|
|
342
368
|
- `ARTSHELF_LATEST_VERSION` overrides the discovered latest version for tests.
|
|
343
369
|
- `ARTSHELF_UPDATE_DRY_RUN=1` makes `artshelf update` report the npm command it
|
|
@@ -520,9 +546,13 @@ V1 also supports a user-level registry of known ledgers:
|
|
|
520
546
|
overrides it for tests and controlled runs; legacy `SHELF_NOW` is read only
|
|
521
547
|
when `ARTSHELF_NOW` is unset.
|
|
522
548
|
- Automatic npm update checks cache their latest-version result at
|
|
523
|
-
`~/.artshelf/update-check.json` by default.
|
|
524
|
-
|
|
525
|
-
|
|
549
|
+
`~/.artshelf/update-check.json` by default. Cached update-available results
|
|
550
|
+
use the long 24-hour TTL; cached no-update, failed, missing, or null results
|
|
551
|
+
use a shorter 1-hour TTL. `ARTSHELF_NO_UPDATE_CHECK=1` disables automatic
|
|
552
|
+
checks, `ARTSHELF_UPDATE_CACHE` overrides the cache path,
|
|
553
|
+
`ARTSHELF_UPDATE_CHECK_TTL_MS` overrides the update-available TTL, and
|
|
554
|
+
`ARTSHELF_NO_UPDATE_CHECK_TTL_MS` overrides the no-update/failed TTL
|
|
555
|
+
(falling back to `ARTSHELF_UPDATE_CHECK_TTL_MS` for compatibility).
|
|
526
556
|
- `put` registers the ledger it writes to.
|
|
527
557
|
- `ledgers add` registers an existing ledger explicitly.
|
|
528
558
|
- `--all` reads registered ledgers as one review surface.
|
|
@@ -773,6 +803,8 @@ human review.
|
|
|
773
803
|
- Package includes the deterministic `ArtshelfReviewReport` schema, canonical
|
|
774
804
|
example, and portable renderer script for agent-rendered review reports.
|
|
775
805
|
- All core commands support `--json`.
|
|
806
|
+
- `review`, `status`, and `doctor` also support `--agent`, a compact single-line
|
|
807
|
+
JSON decision packet for agents that takes precedence over `--json`.
|
|
776
808
|
- Tests cover record/list/find/get/status-filter/due/validate/resolve/registry,
|
|
777
809
|
`artshelf doctor`, the `artshelf status` dashboard, `--all` review, stale-registry,
|
|
778
810
|
dry-run, global-dry-run, execute-plan, and trash list/purge behavior.
|
package/dist/src/cli.js
CHANGED
|
@@ -9,7 +9,8 @@ const VERSION = readPackageVersion();
|
|
|
9
9
|
const PACKAGE_NAME = "artshelf";
|
|
10
10
|
const NPM_REGISTRY_URL = process.env.ARTSHELF_NPM_REGISTRY_URL ?? `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
11
11
|
const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12
|
-
const
|
|
12
|
+
const NO_UPDATE_CHECK_TTL_MS = 60 * 60 * 1000;
|
|
13
|
+
const BOOLEAN_FLAGS = new Set(["all", "json", "agent", "manual-review", "dry-run", "execute", "help", "version", "plain"]);
|
|
13
14
|
const VALUE_FLAGS = new Set([
|
|
14
15
|
"cleanup",
|
|
15
16
|
"kind",
|
|
@@ -535,12 +536,17 @@ function handleTrashPurge(parsed, ledgerPath, json) {
|
|
|
535
536
|
return 0;
|
|
536
537
|
}
|
|
537
538
|
function handleReview(parsed, ledgerPath, json) {
|
|
539
|
+
const agent = boolFlag(parsed, "agent");
|
|
538
540
|
if (boolFlag(parsed, "all")) {
|
|
539
541
|
const registryPath = normalizeRegistryPath(stringFlag(parsed, "registry"));
|
|
540
542
|
const results = registeredLedgersOrThrow(registryPath).map((ledger) => reviewLedger(ledger));
|
|
541
543
|
const ok = results.every((entry) => entry.validate.ok);
|
|
542
544
|
const summary = summarizeReview(results);
|
|
543
|
-
|
|
545
|
+
if (agent) {
|
|
546
|
+
printCompactJson(buildReviewAgentPacketAll(results, summary, registryPath));
|
|
547
|
+
return ok ? 0 : 1;
|
|
548
|
+
}
|
|
549
|
+
const nextAction = reviewNextAction(summary, "all");
|
|
544
550
|
if (json) {
|
|
545
551
|
printJson({ ok, registryPath, summary, nextAction, ledgers: results });
|
|
546
552
|
return ok ? 0 : 1;
|
|
@@ -549,6 +555,10 @@ function handleReview(parsed, ledgerPath, json) {
|
|
|
549
555
|
return ok ? 0 : 1;
|
|
550
556
|
}
|
|
551
557
|
const result = reviewLedger({ name: "current", path: ledgerPath, scope: "other", createdAt: "", updatedAt: "" }, false);
|
|
558
|
+
if (agent) {
|
|
559
|
+
printCompactJson(buildReviewAgentPacketSingle(result, ledgerPath));
|
|
560
|
+
return result.validate.ok ? 0 : 1;
|
|
561
|
+
}
|
|
552
562
|
if (json) {
|
|
553
563
|
printJson({ ok: result.validate.ok, ledger: result });
|
|
554
564
|
return result.validate.ok ? 0 : 1;
|
|
@@ -558,6 +568,10 @@ function handleReview(parsed, ledgerPath, json) {
|
|
|
558
568
|
}
|
|
559
569
|
function handleDoctor(parsed, ledgerPath, json) {
|
|
560
570
|
const report = buildDoctorReport(ledgerPath, normalizeRegistryPath(stringFlag(parsed, "registry")));
|
|
571
|
+
if (boolFlag(parsed, "agent")) {
|
|
572
|
+
printCompactJson(buildDoctorAgentPacket(report));
|
|
573
|
+
return report.ok ? 0 : 1;
|
|
574
|
+
}
|
|
561
575
|
if (json) {
|
|
562
576
|
printJson(report);
|
|
563
577
|
return report.ok ? 0 : 1;
|
|
@@ -624,16 +638,72 @@ function buildDoctorReport(ledgerPath, registryPath) {
|
|
|
624
638
|
errors
|
|
625
639
|
};
|
|
626
640
|
}
|
|
641
|
+
// Actionable categories only — ok ledgers are healthy states, never attention.
|
|
642
|
+
// Order is fixed so the packet is byte-for-byte deterministic. Warnings surface
|
|
643
|
+
// even when health is ok (they never fail the machine), mirroring status attention.
|
|
644
|
+
const DOCTOR_ATTENTION_CATEGORIES = ["stale", "invalid", "warnings"];
|
|
645
|
+
function doctorAttention(summary) {
|
|
646
|
+
return DOCTOR_ATTENTION_CATEGORIES.filter((key) => summary[key] > 0);
|
|
647
|
+
}
|
|
648
|
+
function doctorNextAction(blockers, summary) {
|
|
649
|
+
if (blockers.length > 0) {
|
|
650
|
+
return `repair ${blockers.length} registry/ledger issue(s) above, then re-run \`artshelf doctor\``;
|
|
651
|
+
}
|
|
652
|
+
if (summary.warnings > 0) {
|
|
653
|
+
return `healthy, but ${summary.warnings} warning(s) noted — run \`artshelf validate --all\` to inspect; nothing is auto-executed`;
|
|
654
|
+
}
|
|
655
|
+
return "artshelf is healthy on this machine — cleanup safety enforced; no action needed";
|
|
656
|
+
}
|
|
657
|
+
function buildDoctorAgentPacket(report) {
|
|
658
|
+
const blockers = [];
|
|
659
|
+
if (report.registryError)
|
|
660
|
+
blockers.push(`registry unreadable: ${report.registryError}`);
|
|
661
|
+
for (const ledger of report.ledgers) {
|
|
662
|
+
if (ledger.status !== "ok") {
|
|
663
|
+
blockers.push(`${ledger.name} ${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return {
|
|
667
|
+
schemaVersion: 1,
|
|
668
|
+
command: "doctor",
|
|
669
|
+
health: report.ok ? "ok" : "attention",
|
|
670
|
+
version: report.version,
|
|
671
|
+
node: report.node,
|
|
672
|
+
ledgerPath: report.ledgerPath,
|
|
673
|
+
registry: { path: report.registryPath, exists: report.registryExists, ok: report.registryOk, error: report.registryError },
|
|
674
|
+
ledgers: {
|
|
675
|
+
total: report.summary.ledgers,
|
|
676
|
+
ok: report.summary.ok,
|
|
677
|
+
stale: report.summary.stale,
|
|
678
|
+
invalid: report.summary.invalid,
|
|
679
|
+
warnings: report.summary.warnings
|
|
680
|
+
},
|
|
681
|
+
attention: doctorAttention(report.summary),
|
|
682
|
+
blockers,
|
|
683
|
+
cleanupSafety: report.cleanupSafety,
|
|
684
|
+
nextAction: doctorNextAction(blockers, report.summary),
|
|
685
|
+
verification: `artshelf doctor --agent --registry ${report.registryPath}`
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
// Human render (NGX-396): a scannable left-column glyph so attention state is
|
|
689
|
+
// obvious at a glance — ✓ clear, ⚠ needs attention. Plain Unicode (no ANSI
|
|
690
|
+
// color) keeps redirected/piped human output clean, and the `--agent`/`--json`
|
|
691
|
+
// renders never carry glyphs (those stay machine contracts).
|
|
692
|
+
const HUMAN_OK_GLYPH = "✓";
|
|
693
|
+
const HUMAN_ATTENTION_GLYPH = "⚠";
|
|
694
|
+
function attentionGlyph(needsAttention) {
|
|
695
|
+
return needsAttention ? HUMAN_ATTENTION_GLYPH : HUMAN_OK_GLYPH;
|
|
696
|
+
}
|
|
627
697
|
function printDoctor(report) {
|
|
628
698
|
process.stdout.write(`artshelf ${report.version} (node ${report.node})\n`);
|
|
629
|
-
process.stdout.write(
|
|
699
|
+
process.stdout.write(`${attentionGlyph(!report.ok)} health: ${report.ok ? "ok" : "needs attention"}\n`);
|
|
630
700
|
process.stdout.write(`ledger: ${report.ledgerPath}${report.ledgerExists ? "" : " (absent)"}\n`);
|
|
631
701
|
process.stdout.write(`registry: ${report.registryPath}${report.registryExists ? "" : " (absent)"}\n`);
|
|
632
702
|
if (report.registryError)
|
|
633
703
|
process.stdout.write(`registry error: ${report.registryError}\n`);
|
|
634
704
|
process.stdout.write(`registered ledgers: ${report.summary.ledgers} (${report.summary.ok} ok, ${report.summary.stale} stale, ${report.summary.invalid} invalid)\n`);
|
|
635
705
|
for (const ledger of report.ledgers) {
|
|
636
|
-
process.stdout.write(` ${ledger.status} ${ledger.name} ${ledger.path}\n`);
|
|
706
|
+
process.stdout.write(` ${attentionGlyph(ledger.status !== "ok")} ${ledger.status} ${ledger.name} ${ledger.path}\n`);
|
|
637
707
|
for (const message of ledger.errors)
|
|
638
708
|
process.stdout.write(` error: ${message}\n`);
|
|
639
709
|
}
|
|
@@ -644,8 +714,13 @@ function printDoctor(report) {
|
|
|
644
714
|
}
|
|
645
715
|
}
|
|
646
716
|
function handleStatus(parsed, ledgerPath, json) {
|
|
717
|
+
const agent = boolFlag(parsed, "agent");
|
|
647
718
|
if (boolFlag(parsed, "all")) {
|
|
648
719
|
const report = buildStatusReport(normalizeRegistryPath(stringFlag(parsed, "registry")));
|
|
720
|
+
if (agent) {
|
|
721
|
+
printCompactJson(buildStatusAgentPacketAll(report));
|
|
722
|
+
return report.ok ? 0 : 1;
|
|
723
|
+
}
|
|
649
724
|
if (json) {
|
|
650
725
|
printJson(report);
|
|
651
726
|
return report.ok ? 0 : 1;
|
|
@@ -654,6 +729,10 @@ function handleStatus(parsed, ledgerPath, json) {
|
|
|
654
729
|
return report.ok ? 0 : 1;
|
|
655
730
|
}
|
|
656
731
|
const ledger = statusLedger({ name: "current", path: ledgerPath, scope: "other", createdAt: "", updatedAt: "" }, false);
|
|
732
|
+
if (agent) {
|
|
733
|
+
printCompactJson(buildStatusAgentPacketSingle(ledger, ledgerPath));
|
|
734
|
+
return ledger.ok ? 0 : 1;
|
|
735
|
+
}
|
|
657
736
|
if (json) {
|
|
658
737
|
printJson({ ok: ledger.ok, ledger });
|
|
659
738
|
return ledger.ok ? 0 : 1;
|
|
@@ -737,23 +816,101 @@ function sumStatusCounts(ledgers, key) {
|
|
|
737
816
|
function formatStatusCounts(counts) {
|
|
738
817
|
return `active ${counts.active} · due ${counts.due} · manual-review ${counts.manualReview} · missing ${counts.missingPath} · kept ${counts.kept} · pending ${counts.pendingCleanup}`;
|
|
739
818
|
}
|
|
819
|
+
// Actionable categories only — active and kept are healthy states, never
|
|
820
|
+
// attention. Order is fixed so the packet is byte-for-byte deterministic.
|
|
821
|
+
const STATUS_ATTENTION_CATEGORIES = ["due", "manualReview", "missingPath", "pendingCleanup"];
|
|
822
|
+
function statusAttention(counts) {
|
|
823
|
+
return STATUS_ATTENTION_CATEGORIES.filter((key) => counts[key] > 0);
|
|
824
|
+
}
|
|
825
|
+
function statusCommand(scope, command, ledgerPath) {
|
|
826
|
+
if (scope === "all")
|
|
827
|
+
return `artshelf ${command} --all`;
|
|
828
|
+
return ledgerPath ? `artshelf ${command} --ledger ${ledgerPath}` : `artshelf ${command}`;
|
|
829
|
+
}
|
|
830
|
+
function statusNextAction(blockers, counts, scope, ledgerPath) {
|
|
831
|
+
if (blockers.length > 0) {
|
|
832
|
+
const verify = statusCommand(scope, "status", ledgerPath);
|
|
833
|
+
return `repair ${blockers.length} broken ledger(s) above, then re-run \`${verify}\``;
|
|
834
|
+
}
|
|
835
|
+
const review = statusCommand(scope, "review", ledgerPath);
|
|
836
|
+
if (counts.pendingCleanup > 0 || counts.due > 0) {
|
|
837
|
+
return `run \`${review}\` to preview cleanup plans; nothing is auto-executed`;
|
|
838
|
+
}
|
|
839
|
+
if (counts.manualReview > 0) {
|
|
840
|
+
return `run \`${review}\` to inspect manual-review records; nothing is auto-executed`;
|
|
841
|
+
}
|
|
842
|
+
if (counts.missingPath > 0) {
|
|
843
|
+
return "inspect missing-path records and `artshelf resolve` the ones no longer needed; nothing is auto-executable";
|
|
844
|
+
}
|
|
845
|
+
return "nothing due — no broken ledgers and no due, manual-review, missing-path, or pending cleanup entries";
|
|
846
|
+
}
|
|
847
|
+
function buildStatusAgentPacketAll(report) {
|
|
848
|
+
const blockers = [];
|
|
849
|
+
if (report.registryError)
|
|
850
|
+
blockers.push(`registry unreadable: ${report.registryError}`);
|
|
851
|
+
for (const ledger of report.ledgers) {
|
|
852
|
+
if (ledger.status !== "ok") {
|
|
853
|
+
blockers.push(`${ledger.name} ${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const counts = {
|
|
857
|
+
active: report.totals.active,
|
|
858
|
+
due: report.totals.due,
|
|
859
|
+
manualReview: report.totals.manualReview,
|
|
860
|
+
missingPath: report.totals.missingPath,
|
|
861
|
+
kept: report.totals.kept,
|
|
862
|
+
pendingCleanup: report.totals.pendingCleanup
|
|
863
|
+
};
|
|
864
|
+
return {
|
|
865
|
+
schemaVersion: 1,
|
|
866
|
+
command: "status",
|
|
867
|
+
scope: "all",
|
|
868
|
+
health: report.ok ? "ok" : "attention",
|
|
869
|
+
registry: { path: report.registryPath, exists: report.registryExists, ok: report.registryOk, error: report.registryError },
|
|
870
|
+
ledgers: { total: report.totals.ledgers, ok: report.totals.ok, stale: report.totals.stale, invalid: report.totals.invalid },
|
|
871
|
+
counts,
|
|
872
|
+
attention: statusAttention(counts),
|
|
873
|
+
blockers,
|
|
874
|
+
nextAction: statusNextAction(blockers, counts, "all"),
|
|
875
|
+
verification: `artshelf status --all --agent --registry ${report.registryPath}`
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
function buildStatusAgentPacketSingle(ledger, ledgerPath) {
|
|
879
|
+
const blockers = ledger.ok
|
|
880
|
+
? []
|
|
881
|
+
: [`${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`];
|
|
882
|
+
return {
|
|
883
|
+
schemaVersion: 1,
|
|
884
|
+
command: "status",
|
|
885
|
+
scope: "single",
|
|
886
|
+
health: ledger.ok ? "ok" : "attention",
|
|
887
|
+
ledgerPath,
|
|
888
|
+
counts: ledger.counts,
|
|
889
|
+
attention: statusAttention(ledger.counts),
|
|
890
|
+
blockers,
|
|
891
|
+
nextAction: statusNextAction(blockers, ledger.counts, "single", ledgerPath),
|
|
892
|
+
verification: `artshelf status --agent --ledger ${ledgerPath}`
|
|
893
|
+
};
|
|
894
|
+
}
|
|
740
895
|
function printStatusAll(report) {
|
|
741
|
-
|
|
896
|
+
const anyActionable = report.ledgers.some((ledger) => statusAttention(ledger.counts).length > 0);
|
|
897
|
+
process.stdout.write(`${attentionGlyph(!report.ok || anyActionable)} artshelf status: ${report.ok ? "ok" : "needs attention"}\n`);
|
|
742
898
|
process.stdout.write(`registry: ${report.registryPath}${report.registryExists ? "" : " (absent)"} — ${report.totals.ledgers} ledgers (${report.totals.ok} ok, ${report.totals.stale} stale, ${report.totals.invalid} invalid)\n`);
|
|
743
899
|
if (report.registryError)
|
|
744
900
|
process.stdout.write(`registry error: ${report.registryError}\n`);
|
|
745
901
|
for (const ledger of report.ledgers) {
|
|
746
902
|
if (ledger.status === "ok") {
|
|
747
|
-
process.stdout.write(
|
|
903
|
+
process.stdout.write(`${attentionGlyph(statusAttention(ledger.counts).length > 0)} [${ledger.name}] ${formatStatusCounts(ledger.counts)}\n`);
|
|
748
904
|
}
|
|
749
905
|
else {
|
|
750
|
-
process.stdout.write(
|
|
906
|
+
process.stdout.write(`${HUMAN_ATTENTION_GLYPH} [${ledger.name}] ${ledger.status}: ${ledger.errors.join("; ")}\n`);
|
|
751
907
|
}
|
|
752
908
|
}
|
|
753
909
|
process.stdout.write(`total: ${formatStatusCounts(report.totals)}\n`);
|
|
754
910
|
}
|
|
755
911
|
function printStatusSingle(ledger) {
|
|
756
|
-
|
|
912
|
+
const needsAttention = !ledger.ok || statusAttention(ledger.counts).length > 0;
|
|
913
|
+
process.stdout.write(`${attentionGlyph(needsAttention)} artshelf status: ${ledger.ok ? "ok" : ledger.status}\n`);
|
|
757
914
|
process.stdout.write(`ledger: ${ledger.path}\n`);
|
|
758
915
|
if (ledger.ok) {
|
|
759
916
|
process.stdout.write(`${formatStatusCounts(ledger.counts)}\n`);
|
|
@@ -859,26 +1016,41 @@ async function getLatestVersion(options) {
|
|
|
859
1016
|
return latest;
|
|
860
1017
|
}
|
|
861
1018
|
function readUpdateCache() {
|
|
862
|
-
const ttl = Number(process.env.ARTSHELF_UPDATE_CHECK_TTL_MS ?? UPDATE_CHECK_TTL_MS);
|
|
863
|
-
if (ttl < 0)
|
|
864
|
-
return null;
|
|
865
1019
|
const cachePath = updateCachePath();
|
|
866
1020
|
if (!existsSync(cachePath))
|
|
867
1021
|
return null;
|
|
868
1022
|
try {
|
|
869
1023
|
const cache = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
1024
|
+
if (!("latest" in cache))
|
|
1025
|
+
cache.latest = null;
|
|
870
1026
|
if (cache.latest !== null && typeof cache.latest !== "string")
|
|
871
1027
|
return null;
|
|
872
1028
|
if (typeof cache.checkedAt !== "number")
|
|
873
1029
|
return null;
|
|
1030
|
+
const latest = cache.latest === null ? null : normalizeVersion(cache.latest);
|
|
1031
|
+
const ttl = updateCacheTtlFor(latest);
|
|
1032
|
+
if (ttl < 0)
|
|
1033
|
+
return null;
|
|
874
1034
|
if (Date.now() - cache.checkedAt > ttl)
|
|
875
1035
|
return null;
|
|
876
|
-
return { latest
|
|
1036
|
+
return { latest };
|
|
877
1037
|
}
|
|
878
1038
|
catch {
|
|
879
1039
|
return null;
|
|
880
1040
|
}
|
|
881
1041
|
}
|
|
1042
|
+
function updateCacheTtlFor(latest) {
|
|
1043
|
+
if (latest && compareVersions(latest, VERSION) > 0) {
|
|
1044
|
+
return resolveTtlMs(process.env.ARTSHELF_UPDATE_CHECK_TTL_MS, UPDATE_CHECK_TTL_MS);
|
|
1045
|
+
}
|
|
1046
|
+
return resolveTtlMs(process.env.ARTSHELF_NO_UPDATE_CHECK_TTL_MS ?? process.env.ARTSHELF_UPDATE_CHECK_TTL_MS, NO_UPDATE_CHECK_TTL_MS);
|
|
1047
|
+
}
|
|
1048
|
+
function resolveTtlMs(value, fallback) {
|
|
1049
|
+
if (value === undefined)
|
|
1050
|
+
return fallback;
|
|
1051
|
+
const parsed = Number(value);
|
|
1052
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
1053
|
+
}
|
|
882
1054
|
function writeUpdateCache(latest) {
|
|
883
1055
|
try {
|
|
884
1056
|
const cachePath = updateCachePath();
|
|
@@ -1008,6 +1180,12 @@ function printJson(value) {
|
|
|
1008
1180
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
1009
1181
|
return 0;
|
|
1010
1182
|
}
|
|
1183
|
+
// Agent/compact surface: a single minified JSON line. The default `--json`
|
|
1184
|
+
// stays pretty-printed for audit/debug; agent packets optimize for tokens.
|
|
1185
|
+
function printCompactJson(value) {
|
|
1186
|
+
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
1187
|
+
return 0;
|
|
1188
|
+
}
|
|
1011
1189
|
function registeredLedgersOrThrow(registryPath) {
|
|
1012
1190
|
const ledgers = listRegisteredLedgers(registryPath);
|
|
1013
1191
|
if (ledgers.length === 0)
|
|
@@ -1157,13 +1335,16 @@ function summarizeReview(results) {
|
|
|
1157
1335
|
}
|
|
1158
1336
|
return summary;
|
|
1159
1337
|
}
|
|
1160
|
-
function reviewNextAction(summary) {
|
|
1338
|
+
function reviewNextAction(summary, scope, ledgerPath) {
|
|
1161
1339
|
const broken = summary.invalid + summary.stale;
|
|
1340
|
+
const review = statusCommand(scope, "review", ledgerPath);
|
|
1162
1341
|
if (broken > 0) {
|
|
1163
|
-
|
|
1342
|
+
const repair = scope === "all" ? "re-register or fix the file" : "fix the file";
|
|
1343
|
+
return `repair ${broken} broken ledger(s) above (${repair}), then re-run \`${review}\``;
|
|
1164
1344
|
}
|
|
1165
1345
|
if (summary.executable > 0) {
|
|
1166
|
-
|
|
1346
|
+
const dryRun = scope === "all" ? "artshelf cleanup --dry-run --all" : `artshelf cleanup --dry-run${ledgerPath ? ` --ledger ${ledgerPath}` : ""}`;
|
|
1347
|
+
return `run \`${dryRun}\` to generate plans, then \`artshelf cleanup --execute --plan-id <id> --ledger <path>\` for each reviewed plan`;
|
|
1167
1348
|
}
|
|
1168
1349
|
if (summary.missingPath > 0) {
|
|
1169
1350
|
return "inspect missing-path entries and `artshelf resolve` the ones no longer needed; nothing is auto-executable";
|
|
@@ -1172,7 +1353,7 @@ function reviewNextAction(summary) {
|
|
|
1172
1353
|
}
|
|
1173
1354
|
function printReviewAll(results, summary, nextAction, registryPath) {
|
|
1174
1355
|
const needsAttention = summary.invalid + summary.stale + summary.executable + summary.due + summary.manualReview + summary.missingPath > 0;
|
|
1175
|
-
process.stdout.write(
|
|
1356
|
+
process.stdout.write(`${attentionGlyph(needsAttention)} artshelf review --all: ${needsAttention ? "needs attention" : "all clear"}\n`);
|
|
1176
1357
|
process.stdout.write(`registry: ${registryPath} — ${summary.ledgers} ledgers (${summary.ok} ok, ${summary.invalid} invalid, ${summary.stale} stale)\n`);
|
|
1177
1358
|
printReview(results);
|
|
1178
1359
|
process.stdout.write(`triage: due ${summary.due} · manual-review ${summary.manualReview} · missing ${summary.missingPath} · executable ${summary.executable} · skipped ${summary.skipped}\n`);
|
|
@@ -1181,11 +1362,149 @@ function printReviewAll(results, summary, nextAction, registryPath) {
|
|
|
1181
1362
|
function printReview(results) {
|
|
1182
1363
|
for (const result of results) {
|
|
1183
1364
|
const visibleDue = result.due.filter((entry) => entry.dueStatus !== "kept");
|
|
1184
|
-
|
|
1365
|
+
const needsAttention = !result.validate.ok || visibleDue.length > 0 || result.plan.entries.length > 0;
|
|
1366
|
+
process.stdout.write(`${attentionGlyph(needsAttention)} [${result.ledger.name}] ${result.validate.ok ? "ok" : "invalid"}: ${result.validate.entries} entries, ${result.validate.errors.length} errors, ${result.validate.warnings.length} warnings\n`);
|
|
1185
1367
|
process.stdout.write(`due/manual/missing: ${visibleDue.length}; plan ${result.plan.planId}: ${result.plan.entries.length} entries, ${result.plan.skipped.length} skipped\n`);
|
|
1186
1368
|
process.stdout.write(`ledger: ${result.ledger.path}\n`);
|
|
1187
1369
|
}
|
|
1188
1370
|
}
|
|
1371
|
+
// review is read-only, so every safety guarantee holds unconditionally.
|
|
1372
|
+
const REVIEW_SAFETY = {
|
|
1373
|
+
dryRunOnly: true,
|
|
1374
|
+
executeAllRefused: true,
|
|
1375
|
+
noExecuteRan: true,
|
|
1376
|
+
noResolveRan: true,
|
|
1377
|
+
noDeleteRan: true
|
|
1378
|
+
};
|
|
1379
|
+
// Classify each registered ledger's records into decision groups. Order is
|
|
1380
|
+
// fixed (registry order, then a stable per-ledger sub-order) so the packet is
|
|
1381
|
+
// byte-for-byte deterministic.
|
|
1382
|
+
function buildReviewDecisions(results, scope) {
|
|
1383
|
+
const readyForApproval = [];
|
|
1384
|
+
const needsReviewFirst = [];
|
|
1385
|
+
const blocked = [];
|
|
1386
|
+
const review = scope === "all" ? "artshelf review --all" : "artshelf review";
|
|
1387
|
+
for (const result of results) {
|
|
1388
|
+
const { ledger, validate, due } = result;
|
|
1389
|
+
if (!validate.ok) {
|
|
1390
|
+
const status = existsSync(ledger.path) ? "invalid" : "missing";
|
|
1391
|
+
const repair = scope === "all" ? `re-register or fix ${ledger.path}` : `fix ${ledger.path}`;
|
|
1392
|
+
blocked.push({
|
|
1393
|
+
label: `Repair ${ledger.name} ledger (${status})`,
|
|
1394
|
+
itemIds: [],
|
|
1395
|
+
actionType: "fix-registry",
|
|
1396
|
+
approvalTarget: null,
|
|
1397
|
+
reason: validate.errors[0] ?? `${scope === "all" ? "registered ledger" : "ledger"} is ${status}`,
|
|
1398
|
+
nextStep: `${repair}, then re-run \`${review}\``
|
|
1399
|
+
});
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
const missingPath = due.filter((entry) => entry.dueStatus === "missing-path");
|
|
1403
|
+
const trashSafe = due.filter((entry) => entry.dueStatus === "due" && entry.cleanup === "trash");
|
|
1404
|
+
const inspectItems = due.filter((entry) => entry.dueStatus === "manual-review" ||
|
|
1405
|
+
(entry.dueStatus === "due" && (entry.cleanup === "review" || entry.cleanup === "delete")));
|
|
1406
|
+
// Ready for approval: missing-path records resolve ledger-only with an exact,
|
|
1407
|
+
// plan-less approval target. Resolution updates the ledger and never touches
|
|
1408
|
+
// files, so it is the one action review can hand an agent directly.
|
|
1409
|
+
if (missingPath.length > 0) {
|
|
1410
|
+
const ids = missingPath.map((entry) => entry.id).sort();
|
|
1411
|
+
readyForApproval.push({
|
|
1412
|
+
label: `Resolve ${ids.length} missing-path record(s) in ${ledger.name}`,
|
|
1413
|
+
itemIds: ids,
|
|
1414
|
+
actionType: "resolve-missing",
|
|
1415
|
+
approvalTarget: `approve artshelf resolve missing ledger ${ledger.path} ids ${ids.join(" ")}`,
|
|
1416
|
+
reason: "the recorded path is already missing",
|
|
1417
|
+
nextStep: "confirm the artifact is no longer needed, then approve the ledger-only resolve"
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
// Trash-safe records are cleanup-eligible, but review never mints a plan, so
|
|
1421
|
+
// they carry no approval target: the next step is the dry-run that produces
|
|
1422
|
+
// the reviewed plan id to approve.
|
|
1423
|
+
if (trashSafe.length > 0) {
|
|
1424
|
+
const ids = trashSafe.map((entry) => entry.id).sort();
|
|
1425
|
+
needsReviewFirst.push({
|
|
1426
|
+
label: `Plan cleanup for ${ids.length} trash-eligible artifact(s) in ${ledger.name}`,
|
|
1427
|
+
itemIds: ids,
|
|
1428
|
+
actionType: "cleanup",
|
|
1429
|
+
approvalTarget: null,
|
|
1430
|
+
reason: "disposable artifacts are due but no reviewed cleanup plan exists yet",
|
|
1431
|
+
nextStep: `run \`artshelf cleanup --dry-run --ledger ${ledger.path} --json\`, then approve \`approve artshelf cleanup ledger ${ledger.path} plan <plan-id>\``
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
// manual-review and cleanup=review records need a human decision before any
|
|
1435
|
+
// cleanup; cleanup=delete is refused outright. None carry an approval target.
|
|
1436
|
+
if (inspectItems.length > 0) {
|
|
1437
|
+
const ids = inspectItems.map((entry) => entry.id).sort();
|
|
1438
|
+
const hasDelete = inspectItems.some((entry) => entry.cleanup === "delete");
|
|
1439
|
+
needsReviewFirst.push({
|
|
1440
|
+
label: `Inspect ${ids.length} record(s) in ${ledger.name} before cleanup`,
|
|
1441
|
+
itemIds: ids,
|
|
1442
|
+
actionType: "inspect",
|
|
1443
|
+
approvalTarget: null,
|
|
1444
|
+
reason: hasDelete
|
|
1445
|
+
? "records need manual review; cleanup=delete is refused and never deletes files"
|
|
1446
|
+
: "records are held for manual review before any cleanup",
|
|
1447
|
+
nextStep: "inspect each path, then keep, change retention, resolve, or set cleanup=trash and plan a cleanup"
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
return { readyForApproval, needsReviewFirst, blocked };
|
|
1452
|
+
}
|
|
1453
|
+
function reviewCounts(summary) {
|
|
1454
|
+
return {
|
|
1455
|
+
due: summary.due,
|
|
1456
|
+
manualReview: summary.manualReview,
|
|
1457
|
+
missingPath: summary.missingPath,
|
|
1458
|
+
executable: summary.executable,
|
|
1459
|
+
skipped: summary.skipped
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
function buildReviewAgentPacketAll(results, summary, registryPath) {
|
|
1463
|
+
const groups = buildReviewDecisions(results, "all");
|
|
1464
|
+
return {
|
|
1465
|
+
schemaVersion: 1,
|
|
1466
|
+
command: "review",
|
|
1467
|
+
scope: "all",
|
|
1468
|
+
health: summary.invalid + summary.stale > 0 ? "attention" : "ok",
|
|
1469
|
+
registry: { path: registryPath, exists: existsSync(registryPath) },
|
|
1470
|
+
ledgers: { total: summary.ledgers, ok: summary.ok, stale: summary.stale, invalid: summary.invalid },
|
|
1471
|
+
counts: reviewCounts(summary),
|
|
1472
|
+
decisionSummary: {
|
|
1473
|
+
readyForApproval: groups.readyForApproval.length,
|
|
1474
|
+
needsReviewFirst: groups.needsReviewFirst.length,
|
|
1475
|
+
blocked: groups.blocked.length
|
|
1476
|
+
},
|
|
1477
|
+
readyForApproval: groups.readyForApproval,
|
|
1478
|
+
needsReviewFirst: groups.needsReviewFirst,
|
|
1479
|
+
blocked: groups.blocked,
|
|
1480
|
+
safety: REVIEW_SAFETY,
|
|
1481
|
+
nextAction: reviewNextAction(summary, "all"),
|
|
1482
|
+
verification: `artshelf review --all --agent --registry ${registryPath}`
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function buildReviewAgentPacketSingle(result, ledgerPath) {
|
|
1486
|
+
const summary = summarizeReview([result]);
|
|
1487
|
+
const groups = buildReviewDecisions([result], "single");
|
|
1488
|
+
return {
|
|
1489
|
+
schemaVersion: 1,
|
|
1490
|
+
command: "review",
|
|
1491
|
+
scope: "single",
|
|
1492
|
+
health: summary.invalid + summary.stale > 0 ? "attention" : "ok",
|
|
1493
|
+
ledgerPath,
|
|
1494
|
+
counts: reviewCounts(summary),
|
|
1495
|
+
decisionSummary: {
|
|
1496
|
+
readyForApproval: groups.readyForApproval.length,
|
|
1497
|
+
needsReviewFirst: groups.needsReviewFirst.length,
|
|
1498
|
+
blocked: groups.blocked.length
|
|
1499
|
+
},
|
|
1500
|
+
readyForApproval: groups.readyForApproval,
|
|
1501
|
+
needsReviewFirst: groups.needsReviewFirst,
|
|
1502
|
+
blocked: groups.blocked,
|
|
1503
|
+
safety: REVIEW_SAFETY,
|
|
1504
|
+
nextAction: reviewNextAction(summary, "single", ledgerPath),
|
|
1505
|
+
verification: `artshelf review --agent --ledger ${ledgerPath}`
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1189
1508
|
const COMMAND_GROUPS = [
|
|
1190
1509
|
{
|
|
1191
1510
|
group: "Create",
|
|
@@ -1387,17 +1706,28 @@ Resolved records stay in the audit trail but no longer participate in due or cle
|
|
|
1387
1706
|
}
|
|
1388
1707
|
if (command === "review") {
|
|
1389
1708
|
process.stdout.write(`Usage:
|
|
1390
|
-
artshelf review [--ledger <path>] [--json]
|
|
1391
|
-
artshelf review --all [--registry <path>] [--json]
|
|
1709
|
+
artshelf review [--ledger <path>] [--json|--agent]
|
|
1710
|
+
artshelf review --all [--registry <path>] [--json|--agent]
|
|
1711
|
+
|
|
1712
|
+
Review runs validate, due, and cleanup plan preview without moving files or
|
|
1713
|
+
writing a plan. With --all, review adds aggregate triage counts and the next
|
|
1714
|
+
safe action.
|
|
1392
1715
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1716
|
+
Render modes:
|
|
1717
|
+
(default) Human summary of validation, triage counts, and the next safe action.
|
|
1718
|
+
--json Full read-only audit report (backward-compatible).
|
|
1719
|
+
--agent Compact single-line JSON decision packet for agents: health, triage
|
|
1720
|
+
counts, and classified decision groups (ready for approval, needs
|
|
1721
|
+
review first, blocked) with exact approval targets where they are
|
|
1722
|
+
safe. Review is read-only, so cleanup approval targets are minted by
|
|
1723
|
+
\`cleanup --dry-run\`, never leaked from a preview plan id.
|
|
1724
|
+
Token-efficient; --agent takes precedence over --json.
|
|
1395
1725
|
`);
|
|
1396
1726
|
return;
|
|
1397
1727
|
}
|
|
1398
1728
|
if (command === "doctor") {
|
|
1399
1729
|
process.stdout.write(`Usage:
|
|
1400
|
-
artshelf doctor [--registry <path>] [--ledger <path>] [--json]
|
|
1730
|
+
artshelf doctor [--registry <path>] [--ledger <path>] [--json|--agent]
|
|
1401
1731
|
|
|
1402
1732
|
Doctor reports whether Artshelf is healthy on this machine: CLI version, selected
|
|
1403
1733
|
or default ledger path, selected or global registry path, registered ledger health
|
|
@@ -1406,6 +1736,14 @@ selected or default ledger and still requires a reviewed plan id; --all execute
|
|
|
1406
1736
|
and cleanup=delete are refused, while physical trash purge requires a separate
|
|
1407
1737
|
reviewed purge plan.
|
|
1408
1738
|
|
|
1739
|
+
Render modes:
|
|
1740
|
+
(default) Human summary of machine health and cleanup safety.
|
|
1741
|
+
--json Full audit report (backward-compatible; suitable for cron/reporting).
|
|
1742
|
+
--agent Compact single-line JSON decision packet for agents: health, registry
|
|
1743
|
+
and registered-ledger health, blockers, cleanup-safety posture, next
|
|
1744
|
+
action, and a verify command. Token-efficient; --agent takes
|
|
1745
|
+
precedence over --json.
|
|
1746
|
+
|
|
1409
1747
|
Run it after install, when --all commands behave unexpectedly, or on a schedule to
|
|
1410
1748
|
catch stale registry entries. Doctor is read-only. A healthy machine exits 0; a
|
|
1411
1749
|
broken registry or registered ledger exits non-zero with actionable errors.
|
|
@@ -1414,8 +1752,8 @@ broken registry or registered ledger exits non-zero with actionable errors.
|
|
|
1414
1752
|
}
|
|
1415
1753
|
if (command === "status") {
|
|
1416
1754
|
process.stdout.write(`Usage:
|
|
1417
|
-
artshelf status [--ledger <path>] [--json]
|
|
1418
|
-
artshelf status --all [--registry <path>] [--json]
|
|
1755
|
+
artshelf status [--ledger <path>] [--json|--agent]
|
|
1756
|
+
artshelf status --all [--registry <path>] [--json|--agent]
|
|
1419
1757
|
|
|
1420
1758
|
Status is the lightweight daily "what is going on?" view. Without --all, it
|
|
1421
1759
|
reports counts for the selected or default ledger only. With --all, it adds
|
|
@@ -1423,10 +1761,16 @@ registry health, total ledgers, and aggregated counts across registered ledgers.
|
|
|
1423
1761
|
Counts include active artifacts, kept, due, manual-review, missing-path, and
|
|
1424
1762
|
pending cleanup entries.
|
|
1425
1763
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
--
|
|
1764
|
+
Render modes:
|
|
1765
|
+
(default) Human summary, short enough to paste into a chat.
|
|
1766
|
+
--json Full audit report (backward-compatible; suitable for cron/reporting).
|
|
1767
|
+
--agent Compact single-line JSON decision packet for agents: health, counts,
|
|
1768
|
+
attention categories, blockers, next action, and a verify command.
|
|
1769
|
+
Token-efficient; --agent takes precedence over --json.
|
|
1770
|
+
|
|
1771
|
+
Status is read-only: it never creates plans or receipts and never mutates
|
|
1772
|
+
records. A healthy selected ledger exits 0; with --all, a broken registry or any
|
|
1773
|
+
stale or invalid registered ledger exits non-zero.
|
|
1430
1774
|
`);
|
|
1431
1775
|
return;
|
|
1432
1776
|
}
|
package/docs/agent-monitor.html
CHANGED
|
@@ -73,7 +73,7 @@ artshelf ledgers add --ledger <repo>/.artshelf/ledger.jsonl --name <pro
|
|
|
73
73
|
artshelf ledgers list --json
|
|
74
74
|
|
|
75
75
|
<span class="c"># review and due-check every registered ledger at once</span>
|
|
76
|
-
artshelf review --all --
|
|
76
|
+
artshelf review --all --agent
|
|
77
77
|
artshelf due --all --json
|
|
78
78
|
|
|
79
79
|
<span class="c"># find records this agent owns, across ledgers</span>
|
|
@@ -102,12 +102,20 @@ artshelf due --all --json
|
|
|
102
102
|
|
|
103
103
|
<span class="c"># the full read-only pass: validate + due + plan preview</span>
|
|
104
104
|
artshelf review --all --json
|
|
105
|
+
artshelf review --all --agent
|
|
105
106
|
|
|
106
107
|
<span class="c"># CLI version, paths, registry health, safety posture</span>
|
|
107
108
|
artshelf doctor --json
|
|
109
|
+
artshelf doctor --agent
|
|
108
110
|
|
|
109
111
|
<span class="c"># lightweight counts, cron-friendly</span>
|
|
110
|
-
artshelf status --all --json
|
|
112
|
+
artshelf status --all --json
|
|
113
|
+
artshelf status --all --agent</code></pre>
|
|
114
|
+
<p>
|
|
115
|
+
Use <code>--agent</code> for concise monitor decisions and exact next
|
|
116
|
+
actions. Use <code>--json</code> instead when the job needs full
|
|
117
|
+
audit/API detail for storage, debugging, or a custom report.
|
|
118
|
+
</p>
|
|
111
119
|
</section>
|
|
112
120
|
|
|
113
121
|
<section>
|
package/docs/agent-review.html
CHANGED
|
@@ -44,16 +44,22 @@
|
|
|
44
44
|
<section>
|
|
45
45
|
<h2>Daily review workflow</h2>
|
|
46
46
|
<ol>
|
|
47
|
-
<li>Read <code>ledgers list</code>, <code>review --all</code>, and <code>trash list --all</code>.</li>
|
|
47
|
+
<li>Read <code>ledgers list --json</code>, <code>review --all --agent</code>, and <code>trash list --all --json</code>.</li>
|
|
48
48
|
<li>Run explicit-ledger purge dry-runs only when old trash needs review.</li>
|
|
49
49
|
<li>Classify each candidate: <code>trash-safe</code>, <code>needs-human-review</code>, <code>resolve-candidate</code>, or <code>registry-problem</code>.</li>
|
|
50
50
|
<li>Ask only with exact ledger path, reviewed plan id, or ids.</li>
|
|
51
51
|
</ol>
|
|
52
|
+
<p>
|
|
53
|
+
Prefer the built-in <code>--agent</code> packet when an acting agent
|
|
54
|
+
needs the compact decision surface. Use full <code>--json</code>
|
|
55
|
+
output when you need every ledger field for audit, debugging, or a
|
|
56
|
+
custom renderer.
|
|
57
|
+
</p>
|
|
52
58
|
</section>
|
|
53
59
|
|
|
54
60
|
<section>
|
|
55
61
|
<h2>Review plan report schema</h2>
|
|
56
|
-
<p>
|
|
62
|
+
<p>For richer host cards, attachments, or audit packets, construct an <code>ArtshelfReviewReport</code> JSON packet first, then render a compact decision card.</p>
|
|
57
63
|
<p>
|
|
58
64
|
Use <a href="schemas/artshelf-review-report.schema.json">schemas/artshelf-review-report.schema.json</a>
|
|
59
65
|
and <a href="examples/artshelf-review-report.json">examples/artshelf-review-report.json</a>.
|
package/docs/agent-usage.html
CHANGED
|
@@ -87,6 +87,20 @@
|
|
|
87
87
|
</dl>
|
|
88
88
|
</section>
|
|
89
89
|
|
|
90
|
+
<section>
|
|
91
|
+
<h2>Render modes</h2>
|
|
92
|
+
<p>
|
|
93
|
+
<code>review</code>, <code>status</code>, and <code>doctor</code> share three render modes
|
|
94
|
+
so the same data fits both people and agents. Reach for <code>--agent</code> when an agent
|
|
95
|
+
decides and acts; reach for <code>--json</code> for full record, plan, or health detail.
|
|
96
|
+
</p>
|
|
97
|
+
<dl class="def-rows">
|
|
98
|
+
<div><dt>default</dt><dd>human render: scannable grouped counts, attention states, and a short next action for a person at the terminal</dd></div>
|
|
99
|
+
<div><dt>--agent</dt><dd>a deterministic, token-efficient decision packet (single-line compact JSON) with health, counts, classifications, exact approval targets, blockers, and a verification command</dd></div>
|
|
100
|
+
<div><dt>--json</dt><dd>the full audit and API contract: complete machine-readable JSON for debugging and existing integrations, unchanged and backward compatible</dd></div>
|
|
101
|
+
</dl>
|
|
102
|
+
</section>
|
|
103
|
+
|
|
90
104
|
<section>
|
|
91
105
|
<h2>The mental model</h2>
|
|
92
106
|
<ul class="boundary-list">
|
package/docs/agent-usage.md
CHANGED
|
@@ -50,6 +50,23 @@ The browsable docs split the workflow into focused child pages:
|
|
|
50
50
|
- Approval names the exact ledger, plan id, or record ids.
|
|
51
51
|
- Every approved action ends with a read-only verification.
|
|
52
52
|
|
|
53
|
+
## Render modes
|
|
54
|
+
|
|
55
|
+
`review`, `status`, and `doctor` share three render modes so the same data fits
|
|
56
|
+
both people and agents:
|
|
57
|
+
|
|
58
|
+
- **default**: a human render — scannable grouped counts, attention states, and a
|
|
59
|
+
short next action for a person at the terminal.
|
|
60
|
+
- **`--agent`**: a deterministic, token-efficient decision packet (single-line
|
|
61
|
+
compact JSON) with health, counts, classifications, exact approval targets,
|
|
62
|
+
blockers, and a verification command. Use it when an agent acts on the result.
|
|
63
|
+
- **`--json`**: the full audit and API contract — complete machine-readable JSON
|
|
64
|
+
for debugging and existing integrations, unchanged and backward compatible.
|
|
65
|
+
|
|
66
|
+
Reach for `--agent` when an agent needs to decide and act cheaply; reach for
|
|
67
|
+
`--json` when you want the full record, plan, or health detail for audit or
|
|
68
|
+
debugging. `--agent` takes precedence if both flags are passed.
|
|
69
|
+
|
|
53
70
|
## Portable Skill
|
|
54
71
|
|
|
55
72
|
The repo ships a portable skill at
|
package/docs/reference.html
CHANGED
|
@@ -120,19 +120,20 @@ artshelf validate [--all] [--json]</code></pre>
|
|
|
120
120
|
<section class="cmd">
|
|
121
121
|
<div class="cmd-head"><h2>artshelf review / status / doctor</h2><span class="cmd-flag readonly">read-only</span></div>
|
|
122
122
|
<pre><code><span class="c"># validate + due + plan preview in one pass</span>
|
|
123
|
-
artshelf review [--all] [--json]
|
|
123
|
+
artshelf review [--all] [--agent] [--json]
|
|
124
124
|
|
|
125
125
|
<span class="c"># lightweight dashboard of counts</span>
|
|
126
|
-
artshelf status [--all] [--json]
|
|
126
|
+
artshelf status [--all] [--agent] [--json]
|
|
127
127
|
|
|
128
128
|
<span class="c"># CLI version, resolved paths, registry health</span>
|
|
129
|
-
artshelf doctor [--json]</code></pre>
|
|
129
|
+
artshelf doctor [--agent] [--json]</code></pre>
|
|
130
130
|
<p>
|
|
131
131
|
<code>review</code> runs validate, due, and a cleanup plan preview in one pass; no-op
|
|
132
132
|
previews report <code>not-created</code>, and <code>--all</code> adds an aggregate triage
|
|
133
133
|
summary plus the next safe action. <code>status</code> is the lightweight dashboard of
|
|
134
134
|
counts; <code>--all --json</code> is cron-friendly. <code>doctor</code> reports CLI version,
|
|
135
|
-
resolved paths, registry health, and the cleanup safety posture.
|
|
135
|
+
resolved paths, registry health, and the cleanup safety posture. All three also accept
|
|
136
|
+
<code>--agent</code> for a deterministic, token-efficient decision packet (see Output mode).
|
|
136
137
|
</p>
|
|
137
138
|
</section>
|
|
138
139
|
|
|
@@ -147,7 +148,10 @@ artshelf update [--json]</code></pre>
|
|
|
147
148
|
only. pnpm global installs should update with
|
|
148
149
|
<code>pnpm add -g artshelf@latest</code>. Source installs should update by
|
|
149
150
|
pulling, rebuilding, and linking the checkout. Notices are cached in
|
|
150
|
-
<code>~/.artshelf/update-check.json</code
|
|
151
|
+
<code>~/.artshelf/update-check.json</code>: update-available results use
|
|
152
|
+
the long 24-hour TTL, while no-update, failed, missing, or null results
|
|
153
|
+
use a shorter 1-hour TTL so newly published releases are noticed sooner.
|
|
154
|
+
<code>artshelf update</code> forces a fresh latest-version check. Set
|
|
151
155
|
<code>ARTSHELF_NO_UPDATE_CHECK=1</code> to disable automatic checks for
|
|
152
156
|
no-network scripts and scheduled jobs. Read-only command labels refer
|
|
153
157
|
to ledger and artifact mutation, not this optional update-check cache.
|
|
@@ -212,13 +216,29 @@ artshelf trash purge --execute --plan-id <id> [--ledger <path>] [--j
|
|
|
212
216
|
<section>
|
|
213
217
|
<h2>Output mode</h2>
|
|
214
218
|
<p>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
219
|
+
Every command has a default human render: compact, scannable terminal output with
|
|
220
|
+
grouped counts, a leading <code>✓</code>/<code>⚠</code> attention glyph on each ledger
|
|
221
|
+
and the summary line, and a short next action — meant for a person reading the screen,
|
|
222
|
+
not a wall of raw JSON. The glyphs are plain Unicode (no ANSI color), so redirected
|
|
223
|
+
output stays clean.
|
|
224
|
+
</p>
|
|
225
|
+
<p>
|
|
226
|
+
<code>review</code>, <code>status</code>, and <code>doctor</code> add <code>--agent</code>:
|
|
227
|
+
a deterministic, token-efficient decision packet emitted as a single line of compact JSON.
|
|
228
|
+
It names health, counts, classifications, exact approval targets, blockers, and the command
|
|
229
|
+
to re-run for verification, so an agent can act without parsing decorative output.
|
|
230
|
+
</p>
|
|
231
|
+
<p>
|
|
232
|
+
<code>--json</code> stays the audit and API contract: the full, pretty-printed report for
|
|
233
|
+
debugging, audit trails, and existing integrations. It is unchanged and backward compatible,
|
|
234
|
+
and <code>--agent</code> takes precedence when both are passed. Update notices and other
|
|
235
|
+
diagnostics stay on stderr and never corrupt JSON or packet output.
|
|
218
236
|
</p>
|
|
219
237
|
<table class="opts">
|
|
220
238
|
<tr><th>option</th><th>meaning</th></tr>
|
|
221
|
-
<tr><td
|
|
239
|
+
<tr><td>(default)</td><td>human render: grouped counts, ✓/⚠ attention glyphs, and a short next action</td></tr>
|
|
240
|
+
<tr><td>--agent</td><td>token-efficient decision packet for agents on <code>review</code>, <code>status</code>, <code>doctor</code></td></tr>
|
|
241
|
+
<tr><td>--json</td><td>full machine-readable audit JSON on commands that return data</td></tr>
|
|
222
242
|
</table>
|
|
223
243
|
</section>
|
|
224
244
|
|
|
@@ -244,7 +264,8 @@ artshelf trash purge --execute --plan-id <id> [--ledger <path>] [--j
|
|
|
244
264
|
<tr><td>ARTSHELF_NOW</td><td>override current time for retention and due calculations</td></tr>
|
|
245
265
|
<tr><td>ARTSHELF_NO_UPDATE_CHECK=1</td><td>disable automatic npm update checks</td></tr>
|
|
246
266
|
<tr><td>ARTSHELF_UPDATE_CACHE</td><td>override the update-check cache path</td></tr>
|
|
247
|
-
<tr><td>ARTSHELF_UPDATE_CHECK_TTL_MS</td><td>override the update-
|
|
267
|
+
<tr><td>ARTSHELF_UPDATE_CHECK_TTL_MS</td><td>override the update-available cache TTL; also acts as the no-update TTL fallback for compatibility</td></tr>
|
|
268
|
+
<tr><td>ARTSHELF_NO_UPDATE_CHECK_TTL_MS</td><td>override the no-update/failed cache TTL specifically</td></tr>
|
|
248
269
|
<tr><td>ARTSHELF_NPM_REGISTRY_URL</td><td>override the npm latest-version endpoint</td></tr>
|
|
249
270
|
<tr><td>ARTSHELF_UPDATE_DRY_RUN=1</td><td>print the npm update command without running it</td></tr>
|
|
250
271
|
<tr><td>ARTSHELF_LATEST_VERSION</td><td>override the latest-version value for tests</td></tr>
|
|
@@ -273,7 +294,9 @@ artshelf trash purge --execute --plan-id <id> [--ledger <path>] [--j
|
|
|
273
294
|
<code>~/.artshelf/ledgers.json</code> is the discovery index for <code>--all</code>
|
|
274
295
|
review, status, cleanup dry-run, and trash-list; project records stay in their own
|
|
275
296
|
repo-local ledgers. Automatic update checks cache their last npm result at
|
|
276
|
-
<code>~/.artshelf/update-check.json</code> by default
|
|
297
|
+
<code>~/.artshelf/update-check.json</code> by default, with a long TTL
|
|
298
|
+
for update-available results and a shorter TTL for no-update or failed
|
|
299
|
+
results.
|
|
277
300
|
</p>
|
|
278
301
|
<div class="callout" data-kind="boundary">
|
|
279
302
|
<span class="callout-label">Hard boundary</span>
|
package/package.json
CHANGED
package/skills/artshelf/SKILL.md
CHANGED
|
@@ -59,8 +59,7 @@ npm link
|
|
|
59
59
|
artshelf doctor
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Install, copy, or reference this portable skill only after the user chooses the
|
|
63
|
-
integration path. Offer to schedule read-only review job delivery in the host runtime.
|
|
62
|
+
Install, copy, or reference this portable skill only after the user chooses the integration path. Offer to schedule read-only review job delivery in the host runtime.
|
|
64
63
|
|
|
65
64
|
## Create
|
|
66
65
|
|
|
@@ -72,15 +71,11 @@ artshelf put <path> --reason "<why this exists>" --ttl 3d --kind run-artifact --
|
|
|
72
71
|
artshelf get <id> --json
|
|
73
72
|
```
|
|
74
73
|
|
|
75
|
-
Register backups, quarantine folders, debug output, generated reports, long-run
|
|
76
|
-
|
|
77
|
-
build output, dependency caches, secrets, credential dumps, and artifacts already
|
|
78
|
-
owned by another durable ledger.
|
|
74
|
+
Register backups, quarantine folders, debug output, generated reports, long-run evidence, and copied files kept for review. Skip source files, cheap regenerated
|
|
75
|
+
build output, dependency caches, secrets, credential dumps, and artifacts already owned by another durable ledger.
|
|
79
76
|
|
|
80
|
-
Defaults: `kind=scratch` for temp dirs, `backup` for rollback copies,
|
|
81
|
-
`
|
|
82
|
-
files. Use `cleanup=review` when judgment is needed and `cleanup=trash` only when
|
|
83
|
-
later disposal is clearly safe.
|
|
77
|
+
Defaults: `kind=scratch` for temp dirs, `backup` for rollback copies, `run-artifact` for logs/reports/evidence, `quarantine` for isolated questionable
|
|
78
|
+
files. Use `cleanup=review` when judgment is needed and `cleanup=trash` only when later disposal is clearly safe.
|
|
84
79
|
|
|
85
80
|
When JSON registration succeeds, include this deterministic Artshelf footnote:
|
|
86
81
|
|
|
@@ -94,13 +89,15 @@ Use the ledger registry for whole-machine review:
|
|
|
94
89
|
|
|
95
90
|
```bash
|
|
96
91
|
artshelf ledgers list --json
|
|
97
|
-
artshelf status --all --
|
|
98
|
-
artshelf review --all --
|
|
92
|
+
artshelf status --all --agent
|
|
93
|
+
artshelf review --all --agent
|
|
99
94
|
artshelf trash list --all --json
|
|
100
95
|
```
|
|
101
96
|
|
|
102
97
|
`artshelf ledgers list --json` reports per-ledger validation status. `--plain`
|
|
103
98
|
skips validation. `--all` is for discovery and review, not mutation permission.
|
|
99
|
+
Use `--agent` on `review`, `status`, and `doctor` for compact decisions; use
|
|
100
|
+
`--json` for full audit/API payloads, custom rendering, or debugging.
|
|
104
101
|
|
|
105
102
|
Register existing project ledgers explicitly:
|
|
106
103
|
|
|
@@ -144,22 +141,23 @@ Daily Review Workflow: turn raw Artshelf output into a decision packet, not a
|
|
|
144
141
|
count dump.
|
|
145
142
|
|
|
146
143
|
1. Run read-only review first: `artshelf ledgers list --json`,
|
|
147
|
-
`artshelf review --all --
|
|
144
|
+
`artshelf review --all --agent`, and `artshelf trash list --all --json`.
|
|
148
145
|
2. If cleanup attention exists, run `artshelf cleanup --dry-run --all --json`.
|
|
149
146
|
3. Classify candidates as `trash-safe`, `needs-human-review`,
|
|
150
147
|
`resolve-candidate`, or `registry-problem`.
|
|
151
|
-
4. Use `
|
|
152
|
-
|
|
153
|
-
`
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
4. Use the built-in `--agent` packet when the CLI output is enough to decide,
|
|
149
|
+
because it is deterministic and token-efficient. Use
|
|
150
|
+
`ArtshelfReviewReport` from `schemas/artshelf-review-report.schema.json` and
|
|
151
|
+
`examples/artshelf-review-report.json` when you need a host-specific card,
|
|
152
|
+
attachment, or richer audit record.
|
|
153
|
+
5. Render full packets with `scripts/render-review-report.mjs`; keep
|
|
154
|
+
`decisionSummary` in audit, while `decisionGroups` drive counts. Emojis are encouraged only in host-specific wrappers, not the renderer.
|
|
157
155
|
6. Always include the exact approval target in the message body as a fallback.
|
|
158
156
|
Do not paste the whole packet into chat unless the user asks for it.
|
|
159
157
|
|
|
160
158
|
### Review Plan Report Schema
|
|
161
159
|
|
|
162
|
-
Deterministic renderer:
|
|
160
|
+
Deterministic compact decision card renderer:
|
|
163
161
|
|
|
164
162
|
```bash
|
|
165
163
|
cd /path/to/skills/artshelf
|