artshelf 0.15.0 → 0.16.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
@@ -136,6 +136,16 @@
136
136
  - Moved `artshelf put` registry-warning output from stdout to stderr in human
137
137
  mode; `--json` output is unchanged (NGX-429).
138
138
 
139
+ ## [0.16.0](https://github.com/calvinnwq/artshelf/compare/v0.15.0...v0.16.0) (2026-06-24)
140
+
141
+
142
+ ### Features
143
+
144
+ * **dispose:** add approval-gated artifact disposition ([ee2406e](https://github.com/calvinnwq/artshelf/commit/ee2406e1850f6f2775d5d581bb529fac295b5159))
145
+ * **dispose:** add disposition dry-run domain layer ([c7bdbb3](https://github.com/calvinnwq/artshelf/commit/c7bdbb3d8a40ffc3c9c14c7da6eb6acfedb42859))
146
+ * **dispose:** add disposition execute domain layer ([b9fda32](https://github.com/calvinnwq/artshelf/commit/b9fda3254576d99e5c222a6d1a04c25b1a08d8b0))
147
+ * **dispose:** wire disposition CLI command ([7fe7921](https://github.com/calvinnwq/artshelf/commit/7fe792123b2d4a05bef37d360ced573b82dcd322))
148
+
139
149
  ## [0.15.0](https://github.com/calvinnwq/artshelf/compare/v0.14.0...v0.15.0) (2026-06-20)
140
150
 
141
151
 
package/README.md CHANGED
@@ -83,7 +83,7 @@ Follow the instructions in https://github.com/calvinnwq/artshelf/blob/main/INSTA
83
83
  It will install the CLI, copy the portable skill (with its bundled review-report
84
84
  renderer), register any existing project ledgers, and — only with your approval —
85
85
  schedule a **read-only** daily review. Scheduled jobs review and report only;
86
- cleanup and purge execution always come back to you. See [INSTALL.md](INSTALL.md)
86
+ cleanup, dispose, and purge execution always come back to you. See [INSTALL.md](INSTALL.md)
87
87
  for the full steps.
88
88
 
89
89
  ## How it works
@@ -109,7 +109,7 @@ destructive deletion.
109
109
  - **Ledger-first**, not filesystem-scan-first — every artifact is a recorded decision.
110
110
  - **Dry-run before mutation**, and execute only from a reviewed plan id.
111
111
  - **No daemon, no auto-execute, no global execute** — `--all` is read-only or
112
- dry-run reporting; cleanup and purge refuse it.
112
+ dry-run reporting; cleanup, dispose, and purge refuse it.
113
113
  - **No fresh-plan-then-execute shortcut** — review the plan, then run that plan.
114
114
  - **Trash before delete** — `cleanup=delete` stays refused; physical deletion
115
115
  needs its own reviewed trash purge. No silent deletion, ever.
@@ -118,8 +118,8 @@ destructive deletion.
118
118
  registry mutations take a cross-process lock so overlapping commands never
119
119
  lose records or leave a half-written ledger.
120
120
  - **`--json` on every command**, so agents can act on structured output.
121
- - **`--agent` on `review`/`status`/`doctor`, `ledgers prune --dry-run`, and
122
- `get --inspect`**, a compact, token-efficient decision packet for agents,
121
+ - **`--agent` on `review`/`status`/`doctor`, `ledgers prune --dry-run`,
122
+ `dispose --dry-run`, and `get --inspect`**, a compact, token-efficient decision packet for agents,
123
123
  while the default render stays human-scannable.
124
124
 
125
125
  ## Reference
@@ -147,6 +147,8 @@ artshelf doctor
147
147
  artshelf update [--json]
148
148
  artshelf cleanup --dry-run [--all]
149
149
  artshelf cleanup --execute --plan-id <id> [--ledger <path>] [--json]
150
+ artshelf dispose --id <id> --action trash-resolve|resolve-only|snooze|keep --dry-run [--reason <text>] [--ttl <ttl>|--retain-until <date>] [--ledger <path>] [--json|--agent]
151
+ artshelf dispose --execute --plan-id <id> [--ledger <path>] [--json]
150
152
  artshelf reconcile --dry-run [--all] [--ledger <path>] [--json]
151
153
  artshelf reconcile --execute --plan-id <id> --ledger <path> [--json]
152
154
  artshelf trash list [--all] [--ledger <path>] [--json]
@@ -159,8 +161,9 @@ Use `artshelf help` for a grouped command list, then `artshelf <command> --help`
159
161
  or `artshelf help <command>` for focused details. Nested commands such as
160
162
  `artshelf trash purge --help`, `artshelf ledgers add --help`, and
161
163
  `artshelf ledgers prune --help` show only that subcommand. All core commands
162
- support `--json`; `review`, `status`, `doctor`, `ledgers prune --dry-run`, and
163
- `get --inspect` also take `--agent` for a compact decision packet; `--ledger`,
164
+ support `--json`; `review`, `status`, `doctor`, `ledgers prune --dry-run`,
165
+ `dispose --dry-run`, and `get --inspect` also take `--agent` for a compact
166
+ decision packet; `--ledger`,
164
167
  `--registry`, and `--all` are scope flags only on commands that list them.
165
168
  </details>
166
169
 
@@ -202,8 +205,9 @@ The package includes an agent-facing skill at `skills/artshelf`. Agents that
202
205
  support local skills can copy or reference this directory to learn when to call
203
206
  `artshelf put`, how to report deterministic footnotes after JSON registration,
204
207
  why `artshelf find` / `artshelf get` are the read-only idempotency lookup surface,
205
- why `cleanup --execute` requires an approved reviewed plan id, how to render
206
- dry-run cleanup and trash purge plans as review-report decision packets, and when
208
+ why `cleanup --execute` and `dispose --execute` require approved reviewed plan ids, how to render
209
+ dry-run cleanup and trash purge plans as review-report decision packets, how to use
210
+ `dispose --agent` for per-record approval packets, and when
207
211
  `artshelf resolve <id> --status resolved --reason <text>` may mark a record handled
208
212
  without moving or deleting files.
209
213
 
package/SPEC.md CHANGED
@@ -218,7 +218,8 @@ exact next-safe action. It never reads or previews arbitrary file contents:
218
218
  - `keep` — held for manual review, already resolved, already trashed, or due
219
219
  with `cleanup=review`; it needs your judgment but nothing auto-runs.
220
220
  - `snooze` — retention has not expired yet; re-inspect after it is due.
221
- - `trash-safe` — due with `cleanup=trash`; safe to plan a reviewed cleanup.
221
+ - `trash-safe` — due with `cleanup=trash`; safe to plan a reviewed dispose
222
+ `trash-resolve` decision.
222
223
  - `resolve-only` — the recorded path is gone; resolve the record (ledger-only)
223
224
  rather than cleaning a file.
224
225
  - `blocked` — needs a human decision first: `cleanup=delete` (refused at
@@ -246,7 +247,7 @@ path: <backup-path>
246
247
  status: active · cleanup: review · owner: agent · labels: registry-prune
247
248
  existence: present (directory, 49 B) · age: 14d · retention: manual-review · due: manual-review
248
249
  reason: rollback backup before registry prune
249
- next: Held for manual review — keep it, change its retention, or resolve it explicitly; no cleanup is scheduled.
250
+ next: Held for manual review — run `artshelf dispose --id <id> --action keep --dry-run --reason '<why>' --ledger <ledger-path>` to keep it quiet through a reviewed decision, or choose resolve-only/snooze deliberately.
250
251
  ledger: <ledger-path>
251
252
  ```
252
253
 
@@ -325,12 +326,13 @@ registry-prune dry-run for missing registered ledgers, dry-run reconcile for
325
326
  missing-path or reconcile drift, or nothing to do). Review never writes a plan,
326
327
  so the next action always points at an explicit follow-up command.
327
328
 
328
- `review`, `status`, `doctor`, `ledgers prune --dry-run`, and `get --inspect`
329
- expose agent-oriented render modes. For review/status/doctor, the default human render
329
+ `review`, `status`, `doctor`, `ledgers prune --dry-run`, `dispose --dry-run`,
330
+ and `get --inspect` expose agent-oriented render modes. For review/status/doctor, the default human render
330
331
  leads each ledger and summary line with a `✓`/`⚠` attention glyph. `--json` stays
331
332
  the full, backward-compatible public audit report; and `--agent` emits a compact,
332
333
  deterministic single-line JSON decision packet for agents, taking precedence over
333
- `--json` when both are passed. For `get --inspect`, `--agent` returns the
334
+ `--json` when both are passed. For `dispose --dry-run`, `--agent` returns the per-request plan or blocked packet.
335
+ For `get --inspect`, `--agent` returns the
334
336
  per-record decision packet and requires `--inspect`. For `review`, the packet sorts records into
335
337
  ready-for-approval, needs-review-first, and blocked groups. Because review is
336
338
  read-only and never mints a cleanup or registry-prune plan, the exact approval
@@ -687,6 +689,54 @@ Execute rules:
687
689
  JSON output is deterministic (findings preserve ledger order) so agents can render a
688
690
  decision packet and approve a specific plan id.
689
691
 
692
+ ### `artshelf dispose`
693
+
694
+ Approval-gated disposition for one reviewed record. `dispose` is the command
695
+ surface that follows `get --inspect`: inspect stays read-only, then dispose
696
+ creates or executes the exact reviewed plan for the chosen decision.
697
+
698
+ ```bash
699
+ artshelf dispose --id <id> --action trash-resolve --dry-run [--reason <text>] [--ledger <path>] [--json|--agent]
700
+ artshelf dispose --id <id> --action resolve-only --dry-run --reason <text> [--ledger <path>] [--json|--agent]
701
+ artshelf dispose --id <id> --action snooze --dry-run (--ttl <ttl>|--retain-until <date>) [--reason <text>] [--ledger <path>] [--json|--agent]
702
+ artshelf dispose --id <id> --action keep --dry-run [--reason <text>] [--ledger <path>] [--json|--agent]
703
+ artshelf dispose --execute --plan-id <id> --ledger <path> [--json]
704
+ ```
705
+
706
+ Actions:
707
+
708
+ - `trash-resolve`: move the recorded path into plan-scoped Artshelf trash,
709
+ mark the row `trashed`, and leave physical deletion to a separate trash purge.
710
+ - `resolve-only`: resolve the ledger row only; requires `--reason`.
711
+ - `snooze`: extend retention; requires `--ttl` or `--retain-until`.
712
+ - `keep`: stamp that the record was reviewed and kept.
713
+
714
+ Dry-run rules:
715
+
716
+ - Classifies exactly one record id and action. There is no `--all` path.
717
+ - Writes a reviewed plan only when the request is actionable, under
718
+ `<ledger-dir>/dispose-plans/<id>.json`.
719
+ - Registers the plan as an Artshelf-owned artifact (`owner=artshelf`,
720
+ `kind=run-artifact`, `ttl=14d`, `cleanup=trash`, labels including
721
+ `artshelf`, `dispose-plan`, and the plan id).
722
+ - Prints the exact approval target:
723
+ `approve artshelf dispose ledger <ledger-path> plan <plan-id>`.
724
+ - A blocked request reports `planId=not-created`, writes no plan, and exits
725
+ non-zero while still returning a JSON/agent packet when requested.
726
+
727
+ Execute rules:
728
+
729
+ - Requires one explicit reviewed `--plan-id` and the target `--ledger`.
730
+ - Refuses missing, unknown, id-mismatched, ledger-mismatched, malformed, stale,
731
+ drifted, or target-conflicting plans before mutating.
732
+ - Re-snapshots the subject before execution; stale entries are skipped rather
733
+ than applied.
734
+ - Writes a dispose receipt to `<ledger-dir>/dispose-receipts/<id>.json` and
735
+ registers it as an Artshelf-owned artifact (`ttl=30d`, `cleanup=review`,
736
+ labels including `artshelf`, `dispose-receipt`, and the plan id).
737
+ - There is no fresh-plan-then-execute, no global execute, no daemon, and no
738
+ physical deletion.
739
+
690
740
  ## Ledger Storage
691
741
 
692
742
  V1 supports two scopes:
@@ -738,9 +788,9 @@ V1 also supports a user-level registry of known ledgers:
738
788
  registry validation.
739
789
  - Registry-prune artifacts live next to the registry: `registry-prune-plans/`,
740
790
  `registry-prune-rollbacks/`, and `registry-prune-receipts/`.
741
- - `cleanup --execute --all`, `reconcile --execute --all`, and `trash purge --all`
742
- are refused; execution stays scoped to one explicit ledger or registry and one
743
- reviewed plan id.
791
+ - `cleanup --execute --all`, `dispose --all`, `reconcile --execute --all`, and
792
+ `trash purge --all` are refused; execution stays scoped to one explicit ledger
793
+ or registry and one reviewed plan id.
744
794
 
745
795
  ## Ledger Registry Schema
746
796
 
@@ -823,6 +873,23 @@ the purge provenance:
823
873
  }
824
874
  ```
825
875
 
876
+ Records touched by `artshelf dispose --execute` carry the dispose audit trail so
877
+ reviewed disposition stays traceable to the plan and receipt that produced it:
878
+
879
+ ```json
880
+ {
881
+ "disposePlanId": "dispose_20260601_063000_ab12",
882
+ "disposeReceiptPath": "/absolute/path/.artshelf/dispose-receipts/dispose_20260601_063000_ab12.json",
883
+ "disposedAt": "2026-06-01T06:30:00Z",
884
+ "disposeAction": "trash-resolve",
885
+ "disposeReason": "reviewed and no longer needed"
886
+ }
887
+ ```
888
+
889
+ `trash-resolve` also sets `previousPath` and `targetPath`; `resolve-only` sets
890
+ the resolve fields without moving the subject; `snooze` updates retention; and
891
+ `keep` leaves retention intact while making due classification quiet.
892
+
826
893
  Records touched by `artshelf reconcile --execute` carry the reconcile audit trail so a
827
894
  remap or resolve stays traceable to the reviewed plan that produced it:
828
895
 
@@ -895,15 +962,16 @@ hold, and every future feature (`status`, `doctor`, `review`, scheduled jobs,
895
962
  - **No daemon.** Artshelf never runs in the background or watches the clock. It
896
963
  only does work while you are running an `artshelf` command.
897
964
  - **No auto-execute.** No command cleans up as a side effect. The only commands
898
- that move, trash, or delete files are `artshelf cleanup --execute` and
899
- `artshelf trash purge --execute`, each run by a human against a separately
900
- reviewed plan id.
901
- - **No global execute.** `cleanup --execute --all` and `trash purge --all`
902
- are refused; `--all` is read-only or dry-run reporting only. Execution is
903
- always scoped to a single reviewed plan id.
904
- - **No fresh-plan-then-execute.** `cleanup --execute` refuses to compute a new
905
- live set. It acts only on a plan id that an earlier `cleanup --dry-run`
906
- produced and a human reviewed; it will not plan and execute in one step.
965
+ that move, trash, or delete files are `artshelf cleanup --execute`,
966
+ `artshelf dispose --execute`, and `artshelf trash purge --execute`, each run
967
+ by a human against a separately reviewed plan id.
968
+ - **No global execute.** `cleanup --execute --all`, `dispose --all`, and
969
+ `trash purge --all` are refused; `--all` is read-only or dry-run reporting
970
+ only where supported. Execution is always scoped to a single reviewed plan id.
971
+ - **No fresh-plan-then-execute.** `cleanup --execute` and `dispose --execute`
972
+ refuse to compute a new live set. They act only on plan ids that earlier
973
+ dry-runs produced and a human reviewed; they will not plan and execute in one
974
+ step.
907
975
  - **No silent deletion.** Cleanup trashes or flags for review and writes a
908
976
  receipt to the ledger. The `cleanup=delete` action stays refused in v1; the
909
977
  one sanctioned physical deletion is `artshelf trash purge --execute`, which only
@@ -940,9 +1008,9 @@ Agents should call `artshelf put` immediately after creating:
940
1008
  - one-off generated reports
941
1009
  - copied files kept for rollback
942
1010
 
943
- Agents should not run `artshelf cleanup --execute` or
944
- `artshelf trash purge --execute` without explicit approval naming the ledger path
945
- and reviewed plan id.
1011
+ Agents should not run `artshelf cleanup --execute`,
1012
+ `artshelf dispose --execute`, or `artshelf trash purge --execute` without
1013
+ explicit approval naming the ledger path and reviewed plan id.
946
1014
 
947
1015
  Agents may run `artshelf find` and `artshelf get` before `put` to avoid duplicate
948
1016
  registrations. `find`/`get` are read-only ledger queries; they must not be used
@@ -1004,11 +1072,14 @@ then render a compact decision report from it. The packet schema is
1004
1072
  docs/skills carry matching copies for browsable docs and portable agent
1005
1073
  installs. The report groups decisions into ready-for-approval,
1006
1074
  needs-review-first, and blocked sections, and must still include exact approval
1007
- targets in the message body.
1075
+ targets in the message body. `dispose --dry-run --agent` already emits its own
1076
+ compact per-record approval packet, so it does not require wrapping in this
1077
+ review-report schema.
1008
1078
 
1009
1079
  Scheduled jobs must never run `artshelf cleanup --execute`,
1010
- `artshelf ledgers prune --execute`, or `artshelf trash purge --execute`; they may
1011
- only dry-run and report plans for later human review.
1080
+ `artshelf ledgers prune --execute`, `artshelf dispose --execute`, or
1081
+ `artshelf trash purge --execute`; they may only dry-run and report plans
1082
+ for later human review.
1012
1083
 
1013
1084
  ## Dogfood Scenarios
1014
1085
 
@@ -1016,7 +1087,9 @@ only dry-run and report plans for later human review.
1016
1087
  2. Record a config backup with manual review retention.
1017
1088
  3. Generate a dry-run cleanup plan after TTL expiry using fixture data.
1018
1089
  4. Execute a cleanup plan in a temporary test fixture and verify receipt output.
1019
- 5. List trashed records, dry-run an old-trash purge, then execute the reviewed
1090
+ 5. Dispose one reviewed record through dry-run and exact execute approval, then
1091
+ verify the receipt, audit fields, and quiet review state.
1092
+ 6. List trashed records, dry-run an old-trash purge, then execute the reviewed
1020
1093
  purge plan in a fixture and verify receipt output plus resolved ledger state.
1021
1094
 
1022
1095
  ## V1 Acceptance Criteria
@@ -1054,6 +1127,11 @@ only dry-run and report plans for later human review.
1054
1127
  - Cleanup execute writes a started receipt before moving files, resumes or
1055
1128
  replays the same plan id from receipt/trash evidence, and completes the
1056
1129
  receipt idempotently.
1130
+ - CLI can dispose one reviewed record through `artshelf dispose`: `--dry-run`
1131
+ creates or reuses an exact reviewed plan for trash-resolve, resolve-only,
1132
+ snooze, or keep; `--execute` applies one plan id against one ledger, refuses
1133
+ `--all`, stale state, and target conflicts, writes a receipt, and never
1134
+ physically deletes.
1057
1135
  - CLI can list trashed records (single ledger or `--all`) and purge them through
1058
1136
  an approval-first, ledger-scoped dry-run/execute boundary that writes a purge
1059
1137
  receipt; purge refuses `--all` and never deletes without a reviewed plan id.
@@ -1071,14 +1149,14 @@ only dry-run and report plans for later human review.
1071
1149
  - Package includes the deterministic `ArtshelfReviewReport` schema, canonical
1072
1150
  example, and portable renderer script for agent-rendered review reports.
1073
1151
  - All core commands support `--json`.
1074
- - `review`, `status`, `doctor`, `ledgers prune --dry-run`, and `get --inspect`
1075
- also support `--agent`, a compact single-line JSON decision packet for agents
1076
- that takes precedence over `--json`.
1152
+ - `review`, `status`, `doctor`, `ledgers prune --dry-run`, `dispose --dry-run`,
1153
+ and `get --inspect` also support `--agent`, a compact single-line JSON decision
1154
+ packet for agents that takes precedence over `--json`.
1077
1155
  - Tests cover record/list/find/get/status-filter/due/validate/resolve/registry,
1078
1156
  `artshelf doctor`, the `artshelf status` dashboard, `--all` review, stale-registry,
1079
1157
  dry-run, global-dry-run, execute-plan, cleanup plan-id validation, concurrent
1080
1158
  ledger writes, trash list/purge, path provenance validation, registry-prune,
1081
- and reconcile dry-run/execute behavior.
1159
+ reconcile dry-run/execute, and dispose dry-run/execute behavior.
1082
1160
 
1083
1161
  ## Deferred
1084
1162
 
@@ -0,0 +1,88 @@
1
+ import { createDisposePlan, executeDisposePlan } from "../dispose.js";
2
+ import { printCompactJson, printJson } from "../renderers/json.js";
3
+ import { boolFlag, requiredStringFlag, stringFlag } from "../shared/flags.js";
4
+ export function handleDispose(parsed, ledgerPath, json) {
5
+ const dryRun = boolFlag(parsed, "dry-run");
6
+ const execute = boolFlag(parsed, "execute");
7
+ if (dryRun && execute)
8
+ throw new Error("dispose accepts either --dry-run or --execute, not both");
9
+ if (boolFlag(parsed, "all"))
10
+ throw new Error("dispose is scoped to one --ledger and one reviewed plan; --all is not supported");
11
+ if (execute) {
12
+ const planId = requiredStringFlag(parsed, "plan-id");
13
+ const execution = executeDisposePlan(ledgerPath, planId);
14
+ if (json) {
15
+ printJson({ ok: execution.result.status !== "skipped", ledgerPath, execution });
16
+ return execution.result.status === "skipped" ? 1 : 0;
17
+ }
18
+ printDisposeExecution(execution, ledgerPath);
19
+ return execution.result.status === "skipped" ? 1 : 0;
20
+ }
21
+ if (!dryRun)
22
+ throw new Error("dispose requires --dry-run or --execute");
23
+ const id = requiredStringFlag(parsed, "id");
24
+ const action = requiredStringFlag(parsed, "action");
25
+ const plan = createDisposePlan(ledgerPath, {
26
+ id,
27
+ action,
28
+ reason: stringFlag(parsed, "reason"),
29
+ ttl: stringFlag(parsed, "ttl"),
30
+ retainUntil: stringFlag(parsed, "retain-until")
31
+ });
32
+ const approve = disposeApprovalTarget(ledgerPath, plan.planId);
33
+ if (boolFlag(parsed, "agent")) {
34
+ printCompactJson({
35
+ ok: plan.entry !== null,
36
+ command: "dispose",
37
+ ledgerPath,
38
+ id,
39
+ action,
40
+ status: plan.entry ? "ready-for-approval" : "blocked",
41
+ planId: plan.entry ? plan.planId : null,
42
+ approve,
43
+ blocked: plan.blocked
44
+ });
45
+ return plan.entry ? 0 : 1;
46
+ }
47
+ if (json) {
48
+ printJson({ ok: plan.entry !== null, ledgerPath, plan, approve });
49
+ return plan.entry ? 0 : 1;
50
+ }
51
+ printDisposePlan(plan, ledgerPath, approve);
52
+ return plan.entry ? 0 : 1;
53
+ }
54
+ function disposeApprovalTarget(ledgerPath, planId) {
55
+ if (planId === "not-created")
56
+ return null;
57
+ return `approve artshelf dispose ledger ${ledgerPath} plan ${planId}`;
58
+ }
59
+ function printDisposePlan(plan, ledgerPath, approve) {
60
+ if (!plan.entry) {
61
+ process.stdout.write(`dispose plan not-created: blocked ${plan.request.id} ${plan.request.action}\n`);
62
+ if (plan.blocked)
63
+ process.stdout.write(`blocked: ${plan.blocked.reason} — ${plan.blocked.detail}\n`);
64
+ process.stdout.write(`ledger: ${ledgerPath}\n`);
65
+ return;
66
+ }
67
+ process.stdout.write(`dispose plan ${plan.planId}: ${plan.entry.action} ${plan.entry.id}\n`);
68
+ process.stdout.write(`reason: ${plan.entry.reason}\n`);
69
+ if (plan.entry.targetPath)
70
+ process.stdout.write(`target: ${plan.entry.targetPath}\n`);
71
+ if (plan.entry.retainUntil)
72
+ process.stdout.write(`retain-until: ${plan.entry.retainUntil}\n`);
73
+ process.stdout.write(`plan: ${plan.planPath ?? "not created"}\n`);
74
+ process.stdout.write(`ledger: ${ledgerPath}\n`);
75
+ if (approve)
76
+ process.stdout.write(`approve: ${approve}\n`);
77
+ }
78
+ function printDisposeExecution(execution, ledgerPath) {
79
+ process.stdout.write(`dispose receipt ${execution.planId}: ${execution.result.status} ${execution.result.action} ${execution.result.id}\n`);
80
+ if (execution.result.reason)
81
+ process.stdout.write(`reason: ${execution.result.reason}\n`);
82
+ if (execution.result.targetPath)
83
+ process.stdout.write(`target: ${execution.result.targetPath}\n`);
84
+ if (execution.result.retainUntil)
85
+ process.stdout.write(`retain-until: ${execution.result.retainUntil}\n`);
86
+ process.stdout.write(`receipt: ${execution.receiptPath}\n`);
87
+ process.stdout.write(`ledger: ${ledgerPath}\n`);
88
+ }
@@ -1,6 +1,7 @@
1
1
  import { normalizeLedgerPath } from "../ledger.js";
2
2
  import { boolFlag, stringFlag } from "../shared/flags.js";
3
3
  import { handleCleanup } from "./cleanup.js";
4
+ import { handleDispose } from "./dispose.js";
4
5
  import { handleDoctor } from "./doctor.js";
5
6
  import { handleDue } from "./due.js";
6
7
  import { handleFind } from "./find.js";
@@ -44,6 +45,9 @@ export async function runCommand(parsed) {
44
45
  case "cleanup":
45
46
  status = handleCleanup(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
46
47
  break;
48
+ case "dispose":
49
+ status = handleDispose(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
50
+ break;
47
51
  case "reconcile":
48
52
  status = handleReconcile(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
49
53
  break;