artshelf 0.9.0 → 0.10.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/CHANGELOG.md CHANGED
@@ -64,6 +64,30 @@
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
+
76
+ ## [0.10.0](https://github.com/calvinnwq/artshelf/compare/artshelf-v0.9.0...artshelf-v0.10.0) (2026-06-12)
77
+
78
+
79
+ ### Features
80
+
81
+ * **cli:** add --agent decision-packet render mode for review, status, and doctor ([4d2dee0](https://github.com/calvinnwq/artshelf/commit/4d2dee099569803b887ae49438b0747d1330ec5d))
82
+ * **cli:** add --agent render mode and implement status --agent ([36f8e78](https://github.com/calvinnwq/artshelf/commit/36f8e7839d535fcabddadfc616ba518a9b444114))
83
+ * **cli:** add ✓/⚠ attention glyphs to human renders of status/doctor/review ([6f6cbe8](https://github.com/calvinnwq/artshelf/commit/6f6cbe85d54886cfd137791863e1b3554ca908f0))
84
+ * **cli:** implement artshelf doctor --agent compact decision packet ([d9abd4e](https://github.com/calvinnwq/artshelf/commit/d9abd4e75a7f4b2898eeacc3b3404221f4456bd4))
85
+ * **cli:** implement artshelf review --agent compact decision packet ([6f5476c](https://github.com/calvinnwq/artshelf/commit/6f5476ca987de3190f7a8760c6bb9c1efa8b9fce))
86
+
87
+
88
+ ### Bug Fixes
89
+
90
+ * **cli:** preserve ledger scope in agent next actions ([a583683](https://github.com/calvinnwq/artshelf/commit/a583683064cdd16dd929766dc01f23fc31fa50e7))
67
91
 
68
92
  ## [0.9.0](https://github.com/calvinnwq/artshelf/compare/artshelf-v0.8.0...artshelf-v0.9.0) (2026-06-11)
69
93
 
package/README.md CHANGED
@@ -112,6 +112,8 @@ destructive deletion.
112
112
  - **Trash before delete** — `cleanup=delete` stays refused; physical deletion
113
113
  needs its own reviewed trash purge. No silent deletion, ever.
114
114
  - **`--json` on every command**, so agents can act on structured output.
115
+ - **`--agent` on `review`/`status`/`doctor`**, a compact, token-efficient
116
+ decision packet for agents, while the default render stays human-scannable.
115
117
 
116
118
  ## Reference
117
119
 
@@ -143,7 +145,8 @@ artshelf resolve <id> --status resolved --reason "inspected and no longer needed
143
145
  Use `artshelf help` for a grouped command list, then `artshelf <command> --help`
144
146
  or `artshelf help <command>` for focused details. Nested commands such as
145
147
  `artshelf trash purge --help` and `artshelf ledgers add --help` show only that
146
- subcommand. All core commands support `--json`; `--ledger`, `--registry`, and
148
+ subcommand. All core commands support `--json`; `review`, `status`, and `doctor`
149
+ also take `--agent` for a compact decision packet; `--ledger`, `--registry`, and
147
150
  `--all` are scope flags only on commands that list them.
148
151
  </details>
149
152
 
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
 
@@ -773,6 +794,8 @@ human review.
773
794
  - Package includes the deterministic `ArtshelfReviewReport` schema, canonical
774
795
  example, and portable renderer script for agent-rendered review reports.
775
796
  - All core commands support `--json`.
797
+ - `review`, `status`, and `doctor` also support `--agent`, a compact single-line
798
+ JSON decision packet for agents that takes precedence over `--json`.
776
799
  - Tests cover record/list/find/get/status-filter/due/validate/resolve/registry,
777
800
  `artshelf doctor`, the `artshelf status` dashboard, `--all` review, stale-registry,
778
801
  dry-run, global-dry-run, execute-plan, and trash list/purge behavior.
package/dist/src/cli.js CHANGED
@@ -9,7 +9,7 @@ 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 BOOLEAN_FLAGS = new Set(["all", "json", "manual-review", "dry-run", "execute", "help", "version", "plain"]);
12
+ const BOOLEAN_FLAGS = new Set(["all", "json", "agent", "manual-review", "dry-run", "execute", "help", "version", "plain"]);
13
13
  const VALUE_FLAGS = new Set([
14
14
  "cleanup",
15
15
  "kind",
@@ -535,12 +535,17 @@ function handleTrashPurge(parsed, ledgerPath, json) {
535
535
  return 0;
536
536
  }
537
537
  function handleReview(parsed, ledgerPath, json) {
538
+ const agent = boolFlag(parsed, "agent");
538
539
  if (boolFlag(parsed, "all")) {
539
540
  const registryPath = normalizeRegistryPath(stringFlag(parsed, "registry"));
540
541
  const results = registeredLedgersOrThrow(registryPath).map((ledger) => reviewLedger(ledger));
541
542
  const ok = results.every((entry) => entry.validate.ok);
542
543
  const summary = summarizeReview(results);
543
- const nextAction = reviewNextAction(summary);
544
+ if (agent) {
545
+ printCompactJson(buildReviewAgentPacketAll(results, summary, registryPath));
546
+ return ok ? 0 : 1;
547
+ }
548
+ const nextAction = reviewNextAction(summary, "all");
544
549
  if (json) {
545
550
  printJson({ ok, registryPath, summary, nextAction, ledgers: results });
546
551
  return ok ? 0 : 1;
@@ -549,6 +554,10 @@ function handleReview(parsed, ledgerPath, json) {
549
554
  return ok ? 0 : 1;
550
555
  }
551
556
  const result = reviewLedger({ name: "current", path: ledgerPath, scope: "other", createdAt: "", updatedAt: "" }, false);
557
+ if (agent) {
558
+ printCompactJson(buildReviewAgentPacketSingle(result, ledgerPath));
559
+ return result.validate.ok ? 0 : 1;
560
+ }
552
561
  if (json) {
553
562
  printJson({ ok: result.validate.ok, ledger: result });
554
563
  return result.validate.ok ? 0 : 1;
@@ -558,6 +567,10 @@ function handleReview(parsed, ledgerPath, json) {
558
567
  }
559
568
  function handleDoctor(parsed, ledgerPath, json) {
560
569
  const report = buildDoctorReport(ledgerPath, normalizeRegistryPath(stringFlag(parsed, "registry")));
570
+ if (boolFlag(parsed, "agent")) {
571
+ printCompactJson(buildDoctorAgentPacket(report));
572
+ return report.ok ? 0 : 1;
573
+ }
561
574
  if (json) {
562
575
  printJson(report);
563
576
  return report.ok ? 0 : 1;
@@ -624,16 +637,72 @@ function buildDoctorReport(ledgerPath, registryPath) {
624
637
  errors
625
638
  };
626
639
  }
640
+ // Actionable categories only — ok ledgers are healthy states, never attention.
641
+ // Order is fixed so the packet is byte-for-byte deterministic. Warnings surface
642
+ // even when health is ok (they never fail the machine), mirroring status attention.
643
+ const DOCTOR_ATTENTION_CATEGORIES = ["stale", "invalid", "warnings"];
644
+ function doctorAttention(summary) {
645
+ return DOCTOR_ATTENTION_CATEGORIES.filter((key) => summary[key] > 0);
646
+ }
647
+ function doctorNextAction(blockers, summary) {
648
+ if (blockers.length > 0) {
649
+ return `repair ${blockers.length} registry/ledger issue(s) above, then re-run \`artshelf doctor\``;
650
+ }
651
+ if (summary.warnings > 0) {
652
+ return `healthy, but ${summary.warnings} warning(s) noted — run \`artshelf validate --all\` to inspect; nothing is auto-executed`;
653
+ }
654
+ return "artshelf is healthy on this machine — cleanup safety enforced; no action needed";
655
+ }
656
+ function buildDoctorAgentPacket(report) {
657
+ const blockers = [];
658
+ if (report.registryError)
659
+ blockers.push(`registry unreadable: ${report.registryError}`);
660
+ for (const ledger of report.ledgers) {
661
+ if (ledger.status !== "ok") {
662
+ blockers.push(`${ledger.name} ${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`);
663
+ }
664
+ }
665
+ return {
666
+ schemaVersion: 1,
667
+ command: "doctor",
668
+ health: report.ok ? "ok" : "attention",
669
+ version: report.version,
670
+ node: report.node,
671
+ ledgerPath: report.ledgerPath,
672
+ registry: { path: report.registryPath, exists: report.registryExists, ok: report.registryOk, error: report.registryError },
673
+ ledgers: {
674
+ total: report.summary.ledgers,
675
+ ok: report.summary.ok,
676
+ stale: report.summary.stale,
677
+ invalid: report.summary.invalid,
678
+ warnings: report.summary.warnings
679
+ },
680
+ attention: doctorAttention(report.summary),
681
+ blockers,
682
+ cleanupSafety: report.cleanupSafety,
683
+ nextAction: doctorNextAction(blockers, report.summary),
684
+ verification: `artshelf doctor --agent --registry ${report.registryPath}`
685
+ };
686
+ }
687
+ // Human render (NGX-396): a scannable left-column glyph so attention state is
688
+ // obvious at a glance — ✓ clear, ⚠ needs attention. Plain Unicode (no ANSI
689
+ // color) keeps redirected/piped human output clean, and the `--agent`/`--json`
690
+ // renders never carry glyphs (those stay machine contracts).
691
+ const HUMAN_OK_GLYPH = "✓";
692
+ const HUMAN_ATTENTION_GLYPH = "⚠";
693
+ function attentionGlyph(needsAttention) {
694
+ return needsAttention ? HUMAN_ATTENTION_GLYPH : HUMAN_OK_GLYPH;
695
+ }
627
696
  function printDoctor(report) {
628
697
  process.stdout.write(`artshelf ${report.version} (node ${report.node})\n`);
629
- process.stdout.write(`health: ${report.ok ? "ok" : "needs attention"}\n`);
698
+ process.stdout.write(`${attentionGlyph(!report.ok)} health: ${report.ok ? "ok" : "needs attention"}\n`);
630
699
  process.stdout.write(`ledger: ${report.ledgerPath}${report.ledgerExists ? "" : " (absent)"}\n`);
631
700
  process.stdout.write(`registry: ${report.registryPath}${report.registryExists ? "" : " (absent)"}\n`);
632
701
  if (report.registryError)
633
702
  process.stdout.write(`registry error: ${report.registryError}\n`);
634
703
  process.stdout.write(`registered ledgers: ${report.summary.ledgers} (${report.summary.ok} ok, ${report.summary.stale} stale, ${report.summary.invalid} invalid)\n`);
635
704
  for (const ledger of report.ledgers) {
636
- process.stdout.write(` ${ledger.status} ${ledger.name} ${ledger.path}\n`);
705
+ process.stdout.write(` ${attentionGlyph(ledger.status !== "ok")} ${ledger.status} ${ledger.name} ${ledger.path}\n`);
637
706
  for (const message of ledger.errors)
638
707
  process.stdout.write(` error: ${message}\n`);
639
708
  }
@@ -644,8 +713,13 @@ function printDoctor(report) {
644
713
  }
645
714
  }
646
715
  function handleStatus(parsed, ledgerPath, json) {
716
+ const agent = boolFlag(parsed, "agent");
647
717
  if (boolFlag(parsed, "all")) {
648
718
  const report = buildStatusReport(normalizeRegistryPath(stringFlag(parsed, "registry")));
719
+ if (agent) {
720
+ printCompactJson(buildStatusAgentPacketAll(report));
721
+ return report.ok ? 0 : 1;
722
+ }
649
723
  if (json) {
650
724
  printJson(report);
651
725
  return report.ok ? 0 : 1;
@@ -654,6 +728,10 @@ function handleStatus(parsed, ledgerPath, json) {
654
728
  return report.ok ? 0 : 1;
655
729
  }
656
730
  const ledger = statusLedger({ name: "current", path: ledgerPath, scope: "other", createdAt: "", updatedAt: "" }, false);
731
+ if (agent) {
732
+ printCompactJson(buildStatusAgentPacketSingle(ledger, ledgerPath));
733
+ return ledger.ok ? 0 : 1;
734
+ }
657
735
  if (json) {
658
736
  printJson({ ok: ledger.ok, ledger });
659
737
  return ledger.ok ? 0 : 1;
@@ -737,23 +815,101 @@ function sumStatusCounts(ledgers, key) {
737
815
  function formatStatusCounts(counts) {
738
816
  return `active ${counts.active} · due ${counts.due} · manual-review ${counts.manualReview} · missing ${counts.missingPath} · kept ${counts.kept} · pending ${counts.pendingCleanup}`;
739
817
  }
818
+ // Actionable categories only — active and kept are healthy states, never
819
+ // attention. Order is fixed so the packet is byte-for-byte deterministic.
820
+ const STATUS_ATTENTION_CATEGORIES = ["due", "manualReview", "missingPath", "pendingCleanup"];
821
+ function statusAttention(counts) {
822
+ return STATUS_ATTENTION_CATEGORIES.filter((key) => counts[key] > 0);
823
+ }
824
+ function statusCommand(scope, command, ledgerPath) {
825
+ if (scope === "all")
826
+ return `artshelf ${command} --all`;
827
+ return ledgerPath ? `artshelf ${command} --ledger ${ledgerPath}` : `artshelf ${command}`;
828
+ }
829
+ function statusNextAction(blockers, counts, scope, ledgerPath) {
830
+ if (blockers.length > 0) {
831
+ const verify = statusCommand(scope, "status", ledgerPath);
832
+ return `repair ${blockers.length} broken ledger(s) above, then re-run \`${verify}\``;
833
+ }
834
+ const review = statusCommand(scope, "review", ledgerPath);
835
+ if (counts.pendingCleanup > 0 || counts.due > 0) {
836
+ return `run \`${review}\` to preview cleanup plans; nothing is auto-executed`;
837
+ }
838
+ if (counts.manualReview > 0) {
839
+ return `run \`${review}\` to inspect manual-review records; nothing is auto-executed`;
840
+ }
841
+ if (counts.missingPath > 0) {
842
+ return "inspect missing-path records and `artshelf resolve` the ones no longer needed; nothing is auto-executable";
843
+ }
844
+ return "nothing due — no broken ledgers and no due, manual-review, missing-path, or pending cleanup entries";
845
+ }
846
+ function buildStatusAgentPacketAll(report) {
847
+ const blockers = [];
848
+ if (report.registryError)
849
+ blockers.push(`registry unreadable: ${report.registryError}`);
850
+ for (const ledger of report.ledgers) {
851
+ if (ledger.status !== "ok") {
852
+ blockers.push(`${ledger.name} ${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`);
853
+ }
854
+ }
855
+ const counts = {
856
+ active: report.totals.active,
857
+ due: report.totals.due,
858
+ manualReview: report.totals.manualReview,
859
+ missingPath: report.totals.missingPath,
860
+ kept: report.totals.kept,
861
+ pendingCleanup: report.totals.pendingCleanup
862
+ };
863
+ return {
864
+ schemaVersion: 1,
865
+ command: "status",
866
+ scope: "all",
867
+ health: report.ok ? "ok" : "attention",
868
+ registry: { path: report.registryPath, exists: report.registryExists, ok: report.registryOk, error: report.registryError },
869
+ ledgers: { total: report.totals.ledgers, ok: report.totals.ok, stale: report.totals.stale, invalid: report.totals.invalid },
870
+ counts,
871
+ attention: statusAttention(counts),
872
+ blockers,
873
+ nextAction: statusNextAction(blockers, counts, "all"),
874
+ verification: `artshelf status --all --agent --registry ${report.registryPath}`
875
+ };
876
+ }
877
+ function buildStatusAgentPacketSingle(ledger, ledgerPath) {
878
+ const blockers = ledger.ok
879
+ ? []
880
+ : [`${ledger.status}${ledger.errors.length ? `: ${ledger.errors[0]}` : ""}`];
881
+ return {
882
+ schemaVersion: 1,
883
+ command: "status",
884
+ scope: "single",
885
+ health: ledger.ok ? "ok" : "attention",
886
+ ledgerPath,
887
+ counts: ledger.counts,
888
+ attention: statusAttention(ledger.counts),
889
+ blockers,
890
+ nextAction: statusNextAction(blockers, ledger.counts, "single", ledgerPath),
891
+ verification: `artshelf status --agent --ledger ${ledgerPath}`
892
+ };
893
+ }
740
894
  function printStatusAll(report) {
741
- process.stdout.write(`artshelf status: ${report.ok ? "ok" : "needs attention"}\n`);
895
+ const anyActionable = report.ledgers.some((ledger) => statusAttention(ledger.counts).length > 0);
896
+ process.stdout.write(`${attentionGlyph(!report.ok || anyActionable)} artshelf status: ${report.ok ? "ok" : "needs attention"}\n`);
742
897
  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
898
  if (report.registryError)
744
899
  process.stdout.write(`registry error: ${report.registryError}\n`);
745
900
  for (const ledger of report.ledgers) {
746
901
  if (ledger.status === "ok") {
747
- process.stdout.write(`[${ledger.name}] ${formatStatusCounts(ledger.counts)}\n`);
902
+ process.stdout.write(`${attentionGlyph(statusAttention(ledger.counts).length > 0)} [${ledger.name}] ${formatStatusCounts(ledger.counts)}\n`);
748
903
  }
749
904
  else {
750
- process.stdout.write(`[${ledger.name}] ${ledger.status}: ${ledger.errors.join("; ")}\n`);
905
+ process.stdout.write(`${HUMAN_ATTENTION_GLYPH} [${ledger.name}] ${ledger.status}: ${ledger.errors.join("; ")}\n`);
751
906
  }
752
907
  }
753
908
  process.stdout.write(`total: ${formatStatusCounts(report.totals)}\n`);
754
909
  }
755
910
  function printStatusSingle(ledger) {
756
- process.stdout.write(`artshelf status: ${ledger.ok ? "ok" : ledger.status}\n`);
911
+ const needsAttention = !ledger.ok || statusAttention(ledger.counts).length > 0;
912
+ process.stdout.write(`${attentionGlyph(needsAttention)} artshelf status: ${ledger.ok ? "ok" : ledger.status}\n`);
757
913
  process.stdout.write(`ledger: ${ledger.path}\n`);
758
914
  if (ledger.ok) {
759
915
  process.stdout.write(`${formatStatusCounts(ledger.counts)}\n`);
@@ -1008,6 +1164,12 @@ function printJson(value) {
1008
1164
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
1009
1165
  return 0;
1010
1166
  }
1167
+ // Agent/compact surface: a single minified JSON line. The default `--json`
1168
+ // stays pretty-printed for audit/debug; agent packets optimize for tokens.
1169
+ function printCompactJson(value) {
1170
+ process.stdout.write(`${JSON.stringify(value)}\n`);
1171
+ return 0;
1172
+ }
1011
1173
  function registeredLedgersOrThrow(registryPath) {
1012
1174
  const ledgers = listRegisteredLedgers(registryPath);
1013
1175
  if (ledgers.length === 0)
@@ -1157,13 +1319,16 @@ function summarizeReview(results) {
1157
1319
  }
1158
1320
  return summary;
1159
1321
  }
1160
- function reviewNextAction(summary) {
1322
+ function reviewNextAction(summary, scope, ledgerPath) {
1161
1323
  const broken = summary.invalid + summary.stale;
1324
+ const review = statusCommand(scope, "review", ledgerPath);
1162
1325
  if (broken > 0) {
1163
- return `repair ${broken} broken ledger(s) above (re-register or fix the file), then re-run \`artshelf review --all\``;
1326
+ const repair = scope === "all" ? "re-register or fix the file" : "fix the file";
1327
+ return `repair ${broken} broken ledger(s) above (${repair}), then re-run \`${review}\``;
1164
1328
  }
1165
1329
  if (summary.executable > 0) {
1166
- return "run `artshelf cleanup --dry-run --all` to generate plans, then `artshelf cleanup --execute --plan-id <id> --ledger <path>` for each reviewed plan";
1330
+ const dryRun = scope === "all" ? "artshelf cleanup --dry-run --all" : `artshelf cleanup --dry-run${ledgerPath ? ` --ledger ${ledgerPath}` : ""}`;
1331
+ return `run \`${dryRun}\` to generate plans, then \`artshelf cleanup --execute --plan-id <id> --ledger <path>\` for each reviewed plan`;
1167
1332
  }
1168
1333
  if (summary.missingPath > 0) {
1169
1334
  return "inspect missing-path entries and `artshelf resolve` the ones no longer needed; nothing is auto-executable";
@@ -1172,7 +1337,7 @@ function reviewNextAction(summary) {
1172
1337
  }
1173
1338
  function printReviewAll(results, summary, nextAction, registryPath) {
1174
1339
  const needsAttention = summary.invalid + summary.stale + summary.executable + summary.due + summary.manualReview + summary.missingPath > 0;
1175
- process.stdout.write(`artshelf review --all: ${needsAttention ? "needs attention" : "all clear"}\n`);
1340
+ process.stdout.write(`${attentionGlyph(needsAttention)} artshelf review --all: ${needsAttention ? "needs attention" : "all clear"}\n`);
1176
1341
  process.stdout.write(`registry: ${registryPath} — ${summary.ledgers} ledgers (${summary.ok} ok, ${summary.invalid} invalid, ${summary.stale} stale)\n`);
1177
1342
  printReview(results);
1178
1343
  process.stdout.write(`triage: due ${summary.due} · manual-review ${summary.manualReview} · missing ${summary.missingPath} · executable ${summary.executable} · skipped ${summary.skipped}\n`);
@@ -1181,11 +1346,149 @@ function printReviewAll(results, summary, nextAction, registryPath) {
1181
1346
  function printReview(results) {
1182
1347
  for (const result of results) {
1183
1348
  const visibleDue = result.due.filter((entry) => entry.dueStatus !== "kept");
1184
- process.stdout.write(`[${result.ledger.name}] ${result.validate.ok ? "ok" : "invalid"}: ${result.validate.entries} entries, ${result.validate.errors.length} errors, ${result.validate.warnings.length} warnings\n`);
1349
+ const needsAttention = !result.validate.ok || visibleDue.length > 0 || result.plan.entries.length > 0;
1350
+ 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
1351
  process.stdout.write(`due/manual/missing: ${visibleDue.length}; plan ${result.plan.planId}: ${result.plan.entries.length} entries, ${result.plan.skipped.length} skipped\n`);
1186
1352
  process.stdout.write(`ledger: ${result.ledger.path}\n`);
1187
1353
  }
1188
1354
  }
1355
+ // review is read-only, so every safety guarantee holds unconditionally.
1356
+ const REVIEW_SAFETY = {
1357
+ dryRunOnly: true,
1358
+ executeAllRefused: true,
1359
+ noExecuteRan: true,
1360
+ noResolveRan: true,
1361
+ noDeleteRan: true
1362
+ };
1363
+ // Classify each registered ledger's records into decision groups. Order is
1364
+ // fixed (registry order, then a stable per-ledger sub-order) so the packet is
1365
+ // byte-for-byte deterministic.
1366
+ function buildReviewDecisions(results, scope) {
1367
+ const readyForApproval = [];
1368
+ const needsReviewFirst = [];
1369
+ const blocked = [];
1370
+ const review = scope === "all" ? "artshelf review --all" : "artshelf review";
1371
+ for (const result of results) {
1372
+ const { ledger, validate, due } = result;
1373
+ if (!validate.ok) {
1374
+ const status = existsSync(ledger.path) ? "invalid" : "missing";
1375
+ const repair = scope === "all" ? `re-register or fix ${ledger.path}` : `fix ${ledger.path}`;
1376
+ blocked.push({
1377
+ label: `Repair ${ledger.name} ledger (${status})`,
1378
+ itemIds: [],
1379
+ actionType: "fix-registry",
1380
+ approvalTarget: null,
1381
+ reason: validate.errors[0] ?? `${scope === "all" ? "registered ledger" : "ledger"} is ${status}`,
1382
+ nextStep: `${repair}, then re-run \`${review}\``
1383
+ });
1384
+ continue;
1385
+ }
1386
+ const missingPath = due.filter((entry) => entry.dueStatus === "missing-path");
1387
+ const trashSafe = due.filter((entry) => entry.dueStatus === "due" && entry.cleanup === "trash");
1388
+ const inspectItems = due.filter((entry) => entry.dueStatus === "manual-review" ||
1389
+ (entry.dueStatus === "due" && (entry.cleanup === "review" || entry.cleanup === "delete")));
1390
+ // Ready for approval: missing-path records resolve ledger-only with an exact,
1391
+ // plan-less approval target. Resolution updates the ledger and never touches
1392
+ // files, so it is the one action review can hand an agent directly.
1393
+ if (missingPath.length > 0) {
1394
+ const ids = missingPath.map((entry) => entry.id).sort();
1395
+ readyForApproval.push({
1396
+ label: `Resolve ${ids.length} missing-path record(s) in ${ledger.name}`,
1397
+ itemIds: ids,
1398
+ actionType: "resolve-missing",
1399
+ approvalTarget: `approve artshelf resolve missing ledger ${ledger.path} ids ${ids.join(" ")}`,
1400
+ reason: "the recorded path is already missing",
1401
+ nextStep: "confirm the artifact is no longer needed, then approve the ledger-only resolve"
1402
+ });
1403
+ }
1404
+ // Trash-safe records are cleanup-eligible, but review never mints a plan, so
1405
+ // they carry no approval target: the next step is the dry-run that produces
1406
+ // the reviewed plan id to approve.
1407
+ if (trashSafe.length > 0) {
1408
+ const ids = trashSafe.map((entry) => entry.id).sort();
1409
+ needsReviewFirst.push({
1410
+ label: `Plan cleanup for ${ids.length} trash-eligible artifact(s) in ${ledger.name}`,
1411
+ itemIds: ids,
1412
+ actionType: "cleanup",
1413
+ approvalTarget: null,
1414
+ reason: "disposable artifacts are due but no reviewed cleanup plan exists yet",
1415
+ nextStep: `run \`artshelf cleanup --dry-run --ledger ${ledger.path} --json\`, then approve \`approve artshelf cleanup ledger ${ledger.path} plan <plan-id>\``
1416
+ });
1417
+ }
1418
+ // manual-review and cleanup=review records need a human decision before any
1419
+ // cleanup; cleanup=delete is refused outright. None carry an approval target.
1420
+ if (inspectItems.length > 0) {
1421
+ const ids = inspectItems.map((entry) => entry.id).sort();
1422
+ const hasDelete = inspectItems.some((entry) => entry.cleanup === "delete");
1423
+ needsReviewFirst.push({
1424
+ label: `Inspect ${ids.length} record(s) in ${ledger.name} before cleanup`,
1425
+ itemIds: ids,
1426
+ actionType: "inspect",
1427
+ approvalTarget: null,
1428
+ reason: hasDelete
1429
+ ? "records need manual review; cleanup=delete is refused and never deletes files"
1430
+ : "records are held for manual review before any cleanup",
1431
+ nextStep: "inspect each path, then keep, change retention, resolve, or set cleanup=trash and plan a cleanup"
1432
+ });
1433
+ }
1434
+ }
1435
+ return { readyForApproval, needsReviewFirst, blocked };
1436
+ }
1437
+ function reviewCounts(summary) {
1438
+ return {
1439
+ due: summary.due,
1440
+ manualReview: summary.manualReview,
1441
+ missingPath: summary.missingPath,
1442
+ executable: summary.executable,
1443
+ skipped: summary.skipped
1444
+ };
1445
+ }
1446
+ function buildReviewAgentPacketAll(results, summary, registryPath) {
1447
+ const groups = buildReviewDecisions(results, "all");
1448
+ return {
1449
+ schemaVersion: 1,
1450
+ command: "review",
1451
+ scope: "all",
1452
+ health: summary.invalid + summary.stale > 0 ? "attention" : "ok",
1453
+ registry: { path: registryPath, exists: existsSync(registryPath) },
1454
+ ledgers: { total: summary.ledgers, ok: summary.ok, stale: summary.stale, invalid: summary.invalid },
1455
+ counts: reviewCounts(summary),
1456
+ decisionSummary: {
1457
+ readyForApproval: groups.readyForApproval.length,
1458
+ needsReviewFirst: groups.needsReviewFirst.length,
1459
+ blocked: groups.blocked.length
1460
+ },
1461
+ readyForApproval: groups.readyForApproval,
1462
+ needsReviewFirst: groups.needsReviewFirst,
1463
+ blocked: groups.blocked,
1464
+ safety: REVIEW_SAFETY,
1465
+ nextAction: reviewNextAction(summary, "all"),
1466
+ verification: `artshelf review --all --agent --registry ${registryPath}`
1467
+ };
1468
+ }
1469
+ function buildReviewAgentPacketSingle(result, ledgerPath) {
1470
+ const summary = summarizeReview([result]);
1471
+ const groups = buildReviewDecisions([result], "single");
1472
+ return {
1473
+ schemaVersion: 1,
1474
+ command: "review",
1475
+ scope: "single",
1476
+ health: summary.invalid + summary.stale > 0 ? "attention" : "ok",
1477
+ ledgerPath,
1478
+ counts: reviewCounts(summary),
1479
+ decisionSummary: {
1480
+ readyForApproval: groups.readyForApproval.length,
1481
+ needsReviewFirst: groups.needsReviewFirst.length,
1482
+ blocked: groups.blocked.length
1483
+ },
1484
+ readyForApproval: groups.readyForApproval,
1485
+ needsReviewFirst: groups.needsReviewFirst,
1486
+ blocked: groups.blocked,
1487
+ safety: REVIEW_SAFETY,
1488
+ nextAction: reviewNextAction(summary, "single", ledgerPath),
1489
+ verification: `artshelf review --agent --ledger ${ledgerPath}`
1490
+ };
1491
+ }
1189
1492
  const COMMAND_GROUPS = [
1190
1493
  {
1191
1494
  group: "Create",
@@ -1387,17 +1690,28 @@ Resolved records stay in the audit trail but no longer participate in due or cle
1387
1690
  }
1388
1691
  if (command === "review") {
1389
1692
  process.stdout.write(`Usage:
1390
- artshelf review [--ledger <path>] [--json]
1391
- artshelf review --all [--registry <path>] [--json]
1693
+ artshelf review [--ledger <path>] [--json|--agent]
1694
+ artshelf review --all [--registry <path>] [--json|--agent]
1695
+
1696
+ Review runs validate, due, and cleanup plan preview without moving files or
1697
+ writing a plan. With --all, review adds aggregate triage counts and the next
1698
+ safe action.
1392
1699
 
1393
- Review runs validate, due, and cleanup plan preview without moving files or writing a plan.
1394
- With --all, review adds aggregate triage counts and the next safe action.
1700
+ Render modes:
1701
+ (default) Human summary of validation, triage counts, and the next safe action.
1702
+ --json Full read-only audit report (backward-compatible).
1703
+ --agent Compact single-line JSON decision packet for agents: health, triage
1704
+ counts, and classified decision groups (ready for approval, needs
1705
+ review first, blocked) with exact approval targets where they are
1706
+ safe. Review is read-only, so cleanup approval targets are minted by
1707
+ \`cleanup --dry-run\`, never leaked from a preview plan id.
1708
+ Token-efficient; --agent takes precedence over --json.
1395
1709
  `);
1396
1710
  return;
1397
1711
  }
1398
1712
  if (command === "doctor") {
1399
1713
  process.stdout.write(`Usage:
1400
- artshelf doctor [--registry <path>] [--ledger <path>] [--json]
1714
+ artshelf doctor [--registry <path>] [--ledger <path>] [--json|--agent]
1401
1715
 
1402
1716
  Doctor reports whether Artshelf is healthy on this machine: CLI version, selected
1403
1717
  or default ledger path, selected or global registry path, registered ledger health
@@ -1406,6 +1720,14 @@ selected or default ledger and still requires a reviewed plan id; --all execute
1406
1720
  and cleanup=delete are refused, while physical trash purge requires a separate
1407
1721
  reviewed purge plan.
1408
1722
 
1723
+ Render modes:
1724
+ (default) Human summary of machine health and cleanup safety.
1725
+ --json Full audit report (backward-compatible; suitable for cron/reporting).
1726
+ --agent Compact single-line JSON decision packet for agents: health, registry
1727
+ and registered-ledger health, blockers, cleanup-safety posture, next
1728
+ action, and a verify command. Token-efficient; --agent takes
1729
+ precedence over --json.
1730
+
1409
1731
  Run it after install, when --all commands behave unexpectedly, or on a schedule to
1410
1732
  catch stale registry entries. Doctor is read-only. A healthy machine exits 0; a
1411
1733
  broken registry or registered ledger exits non-zero with actionable errors.
@@ -1414,8 +1736,8 @@ broken registry or registered ledger exits non-zero with actionable errors.
1414
1736
  }
1415
1737
  if (command === "status") {
1416
1738
  process.stdout.write(`Usage:
1417
- artshelf status [--ledger <path>] [--json]
1418
- artshelf status --all [--registry <path>] [--json]
1739
+ artshelf status [--ledger <path>] [--json|--agent]
1740
+ artshelf status --all [--registry <path>] [--json|--agent]
1419
1741
 
1420
1742
  Status is the lightweight daily "what is going on?" view. Without --all, it
1421
1743
  reports counts for the selected or default ledger only. With --all, it adds
@@ -1423,10 +1745,16 @@ registry health, total ledgers, and aggregated counts across registered ledgers.
1423
1745
  Counts include active artifacts, kept, due, manual-review, missing-path, and
1424
1746
  pending cleanup entries.
1425
1747
 
1426
- Human output is short enough to paste into a chat; \`artshelf status --all --json\`
1427
- is suitable for cron and reporting. Status is read-only: it never creates plans
1428
- or receipts and never mutates records. A healthy selected ledger exits 0; with
1429
- --all, a broken registry or any stale or invalid registered ledger exits non-zero.
1748
+ Render modes:
1749
+ (default) Human summary, short enough to paste into a chat.
1750
+ --json Full audit report (backward-compatible; suitable for cron/reporting).
1751
+ --agent Compact single-line JSON decision packet for agents: health, counts,
1752
+ attention categories, blockers, next action, and a verify command.
1753
+ Token-efficient; --agent takes precedence over --json.
1754
+
1755
+ Status is read-only: it never creates plans or receipts and never mutates
1756
+ records. A healthy selected ledger exits 0; with --all, a broken registry or any
1757
+ stale or invalid registered ledger exits non-zero.
1430
1758
  `);
1431
1759
  return;
1432
1760
  }
@@ -73,7 +73,7 @@ artshelf ledgers add --ledger &lt;repo&gt;/.artshelf/ledger.jsonl --name &lt;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 --json
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</code></pre>
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>
@@ -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>Construct an <code>ArtshelfReviewReport</code> JSON packet first, then render a compact decision card.</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>.
@@ -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">
@@ -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
@@ -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
 
@@ -212,13 +213,29 @@ artshelf trash purge --execute --plan-id &lt;id&gt; [--ledger &lt;path&gt;] [--j
212
213
  <section>
213
214
  <h2>Output mode</h2>
214
215
  <p>
215
- <code>--json</code> is the agent contract: it switches commands that return data,
216
- records, plans, receipts, or health output to machine-readable JSON on stdout.
217
- Update notices and other diagnostics stay on stderr and never corrupt JSON output.
216
+ Every command has a default human render: compact, scannable terminal output with
217
+ grouped counts, a leading <code>✓</code>/<code>⚠</code> attention glyph on each ledger
218
+ and the summary line, and a short next action meant for a person reading the screen,
219
+ not a wall of raw JSON. The glyphs are plain Unicode (no ANSI color), so redirected
220
+ output stays clean.
221
+ </p>
222
+ <p>
223
+ <code>review</code>, <code>status</code>, and <code>doctor</code> add <code>--agent</code>:
224
+ a deterministic, token-efficient decision packet emitted as a single line of compact JSON.
225
+ It names health, counts, classifications, exact approval targets, blockers, and the command
226
+ to re-run for verification, so an agent can act without parsing decorative output.
227
+ </p>
228
+ <p>
229
+ <code>--json</code> stays the audit and API contract: the full, pretty-printed report for
230
+ debugging, audit trails, and existing integrations. It is unchanged and backward compatible,
231
+ and <code>--agent</code> takes precedence when both are passed. Update notices and other
232
+ diagnostics stay on stderr and never corrupt JSON or packet output.
218
233
  </p>
219
234
  <table class="opts">
220
235
  <tr><th>option</th><th>meaning</th></tr>
221
- <tr><td>--json</td><td>emit machine-readable JSON on commands that return data</td></tr>
236
+ <tr><td>(default)</td><td>human render: grouped counts, ✓/⚠ attention glyphs, and a short next action</td></tr>
237
+ <tr><td>--agent</td><td>token-efficient decision packet for agents on <code>review</code>, <code>status</code>, <code>doctor</code></td></tr>
238
+ <tr><td>--json</td><td>full machine-readable audit JSON on commands that return data</td></tr>
222
239
  </table>
223
240
  </section>
224
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artshelf",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Tiny CLI for accountable temporary artifact retention.",
5
5
  "type": "module",
6
6
  "author": "Calvin",
@@ -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
- evidence, and copied files kept for review. Skip source files, cheap regenerated
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
- `run-artifact` for logs/reports/evidence, `quarantine` for isolated questionable
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 --json
98
- artshelf review --all --json
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 --json`, and `artshelf trash list --all --json`.
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 `ArtshelfReviewReport` from
152
- `schemas/artshelf-review-report.schema.json`; use
153
- `examples/artshelf-review-report.json` as the canonical packet.
154
- 5. Render the compact decision card with `scripts/render-review-report.mjs`;
155
- keep `decisionSummary` in audit, while `decisionGroups` drive counts.
156
- Emojis are encouraged only in host-specific wrappers, not the renderer.
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