@nikovirtala/adr 0.0.0 → 0.2.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/cli.js +1057 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/kind-BcHjCHvi.js +415 -0
  7. package/dist/kind-BcHjCHvi.js.map +1 -0
  8. package/dist/kind.js +1 -0
  9. package/dist/types/cli/accept.d.ts +3 -0
  10. package/dist/types/cli/commands.d.ts +3 -0
  11. package/dist/types/cli/deprecate.d.ts +3 -0
  12. package/dist/types/cli/editor.d.ts +12 -0
  13. package/dist/types/cli/for.d.ts +3 -0
  14. package/dist/types/cli/init.d.ts +3 -0
  15. package/dist/types/cli/link.d.ts +3 -0
  16. package/dist/types/cli/list.d.ts +3 -0
  17. package/dist/types/cli/new.d.ts +6 -0
  18. package/dist/types/cli/note.d.ts +3 -0
  19. package/dist/types/cli/open.d.ts +17 -0
  20. package/dist/types/cli/reject.d.ts +3 -0
  21. package/dist/types/cli/scope.d.ts +7 -0
  22. package/dist/types/cli/show.d.ts +3 -0
  23. package/dist/types/cli/supersede.d.ts +3 -0
  24. package/dist/types/cli/text.d.ts +46 -0
  25. package/dist/types/cli/withdraw.d.ts +3 -0
  26. package/dist/types/cli.d.ts +2 -0
  27. package/dist/types/config.d.ts +33 -0
  28. package/dist/types/index.d.ts +8 -0
  29. package/dist/types/init.d.ts +27 -0
  30. package/dist/types/kind/project.d.ts +50 -0
  31. package/dist/types/kind/reduce.d.ts +35 -0
  32. package/dist/types/kind/rules.d.ts +20 -0
  33. package/dist/types/kind/schemas.d.ts +37 -0
  34. package/dist/types/kind.d.ts +18 -0
  35. package/dist/types/log.d.ts +53 -0
  36. package/dist/types/scope.d.ts +51 -0
  37. package/dist/types/templates/builtin.d.ts +8 -0
  38. package/dist/types/verify.d.ts +10 -0
  39. package/dist/types/views.d.ts +65 -0
  40. package/dist/types/write.d.ts +333 -0
  41. package/dist/write-9IaqHRrY.js +1986 -0
  42. package/dist/write-9IaqHRrY.js.map +1 -0
  43. package/package.json +55 -1
package/dist/cli.js ADDED
@@ -0,0 +1,1057 @@
1
+ #!/usr/bin/env node
2
+ import { n as DECISION_STATUSES, t as adrKind } from "./kind-BcHjCHvi.js";
3
+ import { C as initDecisionLog, S as unavailableReason, a as noteDecision, c as scopeDecision, d as withdrawDecision, f as adrChecks, g as showDecision, h as listDecisions, i as linkDecision, m as decisionsFor, n as deprecateDecision, o as proposeDecision, r as followScopeRenames, s as rejectDecision, t as acceptDecision, u as supersedeDecision, v as openDecisionLog, x as resolveDecision, y as readDecisions } from "./write-9IaqHRrY.js";
4
+ import { createRequire } from "node:module";
5
+ import { UsageError, renderStateHeader, resolveAttribution, stagedRenames, templateHistoryRef } from "@nikovirtala/ledger";
6
+ import { COMMIT_OPTIONS, commitChoice, confirm, processIO, runCli, verifyCommand } from "@nikovirtala/ledger/cli";
7
+ import { spawn } from "node:child_process";
8
+ // src/cli/open.ts
9
+ /**
10
+ * The decision log a command works on: `--root` and `--ref` from the globals, git run with the
11
+ * CLI's environment.
12
+ */
13
+ function openForCli(io, flags, o = {}) {
14
+ return openDecisionLog({
15
+ cwd: io.cwd,
16
+ root: flags.root,
17
+ ref: flags.ref,
18
+ ...o.reader === void 0 ? {} : { reader: o.reader },
19
+ gitOptions: { env: io.env }
20
+ });
21
+ }
22
+ /**
23
+ * How other decisions display in a write's text, read again from the work tree, like the write
24
+ * result's own views: a commit on the main ref has numbered the decisions it accepted.
25
+ */
26
+ async function displayAfterWrite(log) {
27
+ const read = await readDecisions(log);
28
+ return { display: (id) => read.projection.views.get(id)?.display ?? id };
29
+ }
30
+ // src/cli/text.ts
31
+ /** A table: each column padded to its widest value, trailing spaces removed. */
32
+ function columns(rows) {
33
+ const widths = [];
34
+ for (const row of rows) row.forEach((cell, i) => {
35
+ widths[i] = Math.max(widths[i] ?? 0, cell.length);
36
+ });
37
+ return rows.map((row) => row.map((cell, i) => i === row.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ").trimEnd()).join("\n");
38
+ }
39
+ /** `adr list`: one row per decision, `→ <effective>` when it differs from the decision. */
40
+ function listText(doc, display) {
41
+ if (doc.decisions.length === 0) return "No decisions.";
42
+ return columns(doc.decisions.map((v) => {
43
+ const arrow = v.effective === null ? "→ (cycle)" : v.effective === v.id ? "" : `→ ${display(v.effective)}`;
44
+ return [
45
+ v.number === null ? "-" : v.display,
46
+ v.id,
47
+ v.status,
48
+ v.title,
49
+ arrow
50
+ ];
51
+ }));
52
+ }
53
+ /** `ADR-0003 (m2x7p4d2)`, or the id alone when it has no number. */
54
+ function named(id, display) {
55
+ const shown = display(id);
56
+ return shown === id ? id : `${shown} (${id})`;
57
+ }
58
+ const day = (at) => (at ?? "").slice(0, 10);
59
+ const list = (ids, display) => ids.length === 0 ? "none" : ids.map((id) => named(id, display)).join(", ");
60
+ function bodyLine(v) {
61
+ if (v.body_path === null) return "missing";
62
+ if (v.body_intact === null) return v.body_path;
63
+ return `${v.body_path} (${v.body_intact ? "intact" : "changed after acceptance"})`;
64
+ }
65
+ function actorText(e) {
66
+ const a = e.actor;
67
+ return a.kind === "agent" ? `${a.name} (agent ${a.agent} for ${a.on_behalf_of})` : a.name;
68
+ }
69
+ /** The second line of an event in `show --history`: its reason, else a summary of its data. */
70
+ function eventDetail(e) {
71
+ if (typeof e.reason === "string") return `reason: ${e.reason}`;
72
+ const entries = Object.entries(e.data);
73
+ if (entries.length === 0) return null;
74
+ return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
75
+ }
76
+ function historyLines(events) {
77
+ const width = Math.max(...events.map((e) => e.type.length));
78
+ return events.flatMap((e) => {
79
+ const detail = eventDetail(e);
80
+ return [` ${e.at} ${e.type.padEnd(width)} ${actorText(e)}`, ...detail === null ? [] : [` ${detail}`]];
81
+ });
82
+ }
83
+ const SECTION_FINDING = /^(?:required )?section (.+?) (is missing|is empty|still contains its guidance text)$/;
84
+ const REASONS = {
85
+ "is missing": "missing",
86
+ "is empty": "empty",
87
+ "still contains its guidance text": "guidance text left"
88
+ };
89
+ /** The section findings of a draft lint as headings and reasons, and the other findings. */
90
+ function sectionGaps(findings) {
91
+ const headings = [];
92
+ const reasons = [];
93
+ const other = [];
94
+ for (const f of findings) {
95
+ const m = SECTION_FINDING.exec(f.message);
96
+ if (m === null) {
97
+ other.push(f);
98
+ continue;
99
+ }
100
+ const [, heading = "", what = ""] = m;
101
+ const reason = REASONS[what] ?? what;
102
+ if (!headings.includes(heading)) headings.push(heading);
103
+ if (!reasons.includes(reason)) reasons.push(reason);
104
+ }
105
+ return {
106
+ headings,
107
+ reasons,
108
+ other
109
+ };
110
+ }
111
+ /**
112
+ * The draft lint of `new` as advice: `Fill in N sections before accepting: A, B (why)` for the
113
+ * section findings, and one line per other finding.
114
+ */
115
+ function draftAdvice(findings) {
116
+ const { headings, reasons, other } = sectionGaps(findings);
117
+ const n = headings.length;
118
+ return [...n === 0 ? [] : [`Fill in ${n === 1 ? "1 section" : `${n} sections`} before accepting: ${headings.join(", ")} (${reasons.join(", ")})`], ...other.map((f) => `Draft: ${f.message} [${f.rule}]`)];
119
+ }
120
+ /**
121
+ * The stderr warning of `new` when the draft it wrote has unfilled sections (design §7.2), or
122
+ * `null` when there are none.
123
+ */
124
+ function unfilledWarning(doc) {
125
+ if (doc.command !== "new" || !doc.ok || doc.written.length === 0) return null;
126
+ const { headings, reasons } = sectionGaps(doc.findings);
127
+ const n = headings.length;
128
+ const path = doc.findings.find((f) => f.file !== null)?.file ?? doc.decision?.body_path ?? null;
129
+ if (n === 0 || path === null) return null;
130
+ return `${path}: fill in ${n === 1 ? "1 required section" : `${n} required sections`} before committing or accepting: ${headings.join(", ")} (${reasons.join(", ")}); adr verify reports this until then`;
131
+ }
132
+ function findingLines(findings) {
133
+ return findings.map((f) => ` ${f.file === null ? "" : `${f.file}: `}${f.message} [${f.rule}]`);
134
+ }
135
+ const short = (sha) => sha.slice(0, 7);
136
+ /**
137
+ * `adr scope --follow-renames`: one `<named>: <old globs> → <new globs>` line per changed
138
+ * decision (`affected`), whether or not a decision was given.
139
+ */
140
+ function followRenamesText(doc, display, o) {
141
+ if (!o.renamed) return "No staged renames.";
142
+ if (doc.affected.length === 0) return "No scope globs follow the staged renames.";
143
+ return [...doc.affected.map((v) => {
144
+ const before = o.before.get(v.id) ?? [];
145
+ const old = before.filter((g) => !v.applies_to.includes(g));
146
+ const now = v.applies_to.filter((g) => !before.includes(g));
147
+ const change = old.length === 0 ? `+ ${now.join(", ")}` : `${old.join(", ")} → ${now.join(", ")}`;
148
+ return `${named(v.id, display)}: ${change}`;
149
+ }), "Staged; commit the scoped events with the renames."].join("\n");
150
+ }
151
+ /** The past participle of each write command, for `Not <verb>: …`. */
152
+ const VERBS = {
153
+ new: "proposed",
154
+ accept: "accepted",
155
+ supersede: "superseded",
156
+ reject: "rejected",
157
+ withdraw: "withdrawn",
158
+ deprecate: "deprecated",
159
+ note: "noted",
160
+ link: "linked",
161
+ scope: "scoped"
162
+ };
163
+ /**
164
+ * The text of a write command (design §5.6, §6.1). `subject` names what a refused command was
165
+ * about; `number` is the decision's number in the write result, if it has one now; `reason` is
166
+ * the reason a lifecycle exit recorded.
167
+ */
168
+ function writeText(doc, display, o) {
169
+ const verb = VERBS[doc.command];
170
+ if (!doc.ok) {
171
+ const unfilled = doc.command === "new" && sectionGaps(doc.findings).headings.length > 0;
172
+ return [
173
+ `Not ${verb}: ${o.subject}`,
174
+ ...findingLines(doc.findings),
175
+ ...unfilled ? ["Fill them in with --section <id>=<markdown>, or run without --commit and edit the draft."] : []
176
+ ].join("\n");
177
+ }
178
+ if (o.followRenames !== void 0) return followRenamesText(doc, display, o.followRenames);
179
+ const v = doc.decision;
180
+ if (v === null) return "";
181
+ const committed = doc.commit === null ? null : `Committed ${short(doc.commit)}`;
182
+ const recorded = committed === null ? "Staged; commit to record it." : `${committed}.`;
183
+ switch (doc.command) {
184
+ case "new": return [
185
+ `Proposed ${v.id}: ${v.title}`,
186
+ ...doc.written.map((p) => ` ${p}`),
187
+ ...draftAdvice(doc.findings),
188
+ ...committed === null ? [] : [`${committed}.`],
189
+ o.actor?.kind === "agent" && o.acceptRequiresHuman === true ? `Next: ask a human to review and accept it (adr accept ${v.id})` : `Next: adr accept ${v.id}`
190
+ ].join("\n");
191
+ case "accept": return [
192
+ `Accepted ${v.id}: ${v.title}`,
193
+ ...doc.affected.map((a) => `Superseded ${named(a.id, display)}: ${a.title}`),
194
+ ...o.copied === void 0 || o.copied === null ? [] : [`Copied scope from ${o.copied.displays.join(", ")}: ${o.copied.globs.join(", ")}`],
195
+ committed === null ? "Staged; commit to record it." : o.number === null ? `${committed}. The number is assigned when this commit lands on main.` : `${committed} as ${o.number}.`
196
+ ].join("\n");
197
+ case "supersede": return [
198
+ `Superseded ${named(v.id, display)}: ${v.title}`,
199
+ ` by ${named(v.superseded_by ?? "", display)}`,
200
+ recorded
201
+ ].join("\n");
202
+ case "reject":
203
+ case "withdraw":
204
+ case "deprecate": return [
205
+ `${verb.charAt(0).toUpperCase()}${verb.slice(1)} ${named(v.id, display)}: ${v.title}`,
206
+ ...o.reason === void 0 || o.reason === null ? [] : [` reason: ${o.reason}`],
207
+ recorded
208
+ ].join("\n");
209
+ case "note": return [`Noted on ${named(v.id, display)}: ${v.title}`, recorded].join("\n");
210
+ case "link": {
211
+ const link = doc.written.length === 0 ? void 0 : v.links.at(-1);
212
+ if (link === void 0) return `Links of ${named(v.id, display)} unchanged`;
213
+ return [`Linked ${named(v.id, display)} ${link.rel} ${named(link.to, display)}`, recorded].join("\n");
214
+ }
215
+ case "scope": {
216
+ const before = new Set(o.scopeBefore ?? []);
217
+ const after = new Set(v.applies_to);
218
+ const globs = v.applies_to.length === 0 ? "no globs" : v.applies_to.join(", ");
219
+ if (doc.written.length === 0) return `Scope of ${named(v.id, display)} unchanged: ${globs}`;
220
+ return [
221
+ `Scope of ${named(v.id, display)}: ${v.title}`,
222
+ ...v.applies_to.filter((g) => !before.has(g)).map((g) => ` + ${g}`),
223
+ ...[...before].filter((g) => !after.has(g)).map((g) => ` - ${g}`),
224
+ ` = ${globs}`,
225
+ recorded
226
+ ].join("\n");
227
+ }
228
+ }
229
+ }
230
+ /**
231
+ * "Applies to" without globs: nothing is recorded, which isn't "governs nothing" (design §7.3),
232
+ * with the command that adds globs while the decision can still take them.
233
+ */
234
+ function noGlobs(v, display) {
235
+ return adrKind.machine.allowed(v.status, "scoped") ? `no globs recorded; add them with adr scope ${display(v.id)} --add <glob>` : "no globs recorded";
236
+ }
237
+ /** `adr show`: the state header, the decision's fields and its body. */
238
+ function showText(doc, display) {
239
+ const v = doc.decision;
240
+ const supersedes = v.supersedes.length === 0 && v.declared_supersedes.length > 0 ? `none (declared: ${list(v.declared_supersedes, display)})` : list(v.supersedes, display);
241
+ const fields = [
242
+ ["Title", v.title],
243
+ ["Status", `${v.status} (${day(v.status_at)})`],
244
+ ["Effective", v.effective === null ? "none (supersession cycle)" : named(v.effective, display)],
245
+ ["Chain", v.chain.map(display).join(" → ")],
246
+ ["Supersedes", supersedes],
247
+ ["Applies to", v.applies_to.length === 0 ? noGlobs(v, display) : v.applies_to.join(", ")],
248
+ ["Body", bodyLine(v)]
249
+ ];
250
+ if (doc.events === null) fields.push(["History", v.history.join(" · ")]);
251
+ const width = Math.max(...fields.map(([k]) => k.length)) + 2;
252
+ const lines = fields.map(([k, value]) => `${`${k}:`.padEnd(width)}${value}`);
253
+ if (doc.events !== null) lines.push("History:", ...historyLines(doc.events));
254
+ const body = doc.body === null ? "" : `\n${doc.body.endsWith("\n") ? doc.body : `${doc.body}\n`}`;
255
+ return `${renderStateHeader(doc.header)}\n${lines.join("\n")}\n${body}`;
256
+ }
257
+ /**
258
+ * `adr for`: per path, one row per decision (named, status, title, how it matched), its warning
259
+ * below it when it isn't in force, then the anchors that name no decision of this log.
260
+ */
261
+ function forText(doc, display, keyword) {
262
+ const lines = [];
263
+ for (const p of doc.paths) {
264
+ lines.push(p.tracked ? p.path : `${p.path} (not tracked)`);
265
+ if (p.decisions.length === 0 && p.unresolved.length === 0) lines.push(" no decisions");
266
+ const rows = columns(p.decisions.map((d) => [
267
+ named(d.id, display),
268
+ d.status,
269
+ d.title,
270
+ d.via.map((v) => v.type === "glob" ? `glob ${v.glob}` : `anchor line ${v.line}`).join(", ")
271
+ ])).split("\n");
272
+ p.decisions.forEach((d, i) => {
273
+ lines.push(` ${rows[i] ?? ""}`);
274
+ if (d.warning !== null) lines.push(` ${d.warning}`);
275
+ });
276
+ for (const u of p.unresolved) {
277
+ const why = u.reason === "other-log" ? "in another log" : "not in this log";
278
+ lines.push(` unresolved: ${keyword}:${u.ref} (line ${u.line}, ${why})`);
279
+ }
280
+ }
281
+ return lines.join("\n");
282
+ }
283
+ /** `adr init`: what was written and kept, then the next step; or that nothing was needed. */
284
+ function initText(doc) {
285
+ if (doc.written.length === 0) return `Already initialized: ${doc.root} (kept ${doc.kept.join(", ")})`;
286
+ return [
287
+ `Initialized ${doc.root}`,
288
+ ...doc.written.map((p) => ` ${p}`),
289
+ ...doc.kept.map((p) => `Kept ${p}`),
290
+ ...doc.commit === null ? [] : [`Committed ${short(doc.commit)}.`],
291
+ "Next: adr new \"<title>\""
292
+ ].join("\n");
293
+ }
294
+ // src/cli/accept.ts
295
+ const MAX_LISTED = 5;
296
+ /** `Copy the scope of ADR-0002 (src/a/**, src/b/**) to k3f9q8a1? [y/N] ` (design §6.10). */
297
+ function question(id, offer) {
298
+ const shown = offer.globs.slice(0, MAX_LISTED).join(", ");
299
+ const more = offer.globs.length - MAX_LISTED;
300
+ const globs = more > 0 ? `${shown} and ${more} more` : shown;
301
+ const ds = offer.displays;
302
+ return `Copy the scope of ${ds.length === 1 ? ds[0] : `${ds.slice(0, -1).join(", ")} and ${ds.at(-1)}`} (${ds.length === 1 ? globs : `${offer.globs.length} globs: ${globs}`}) to ${id}? [y/N] `;
303
+ }
304
+ /**
305
+ * The flags decide; without them a person at a terminal is asked, and everyone else (scripts,
306
+ * hooks, CI, `--json`) gets the note with the `adr scope` command (design §6.10, D-e). `seen`
307
+ * receives the offer that was copied, for the text.
308
+ */
309
+ function copyChoice(io, json, value, seen) {
310
+ const copy = async (offer) => {
311
+ seen(offer);
312
+ return true;
313
+ };
314
+ if (value === true) return copy;
315
+ if (value === false) return "skip";
316
+ if (json || !io.interactive) return "suggest";
317
+ return async (offer) => {
318
+ const yes = await confirm(io, question(offer.id, offer), { json });
319
+ if (yes) seen(offer);
320
+ return yes;
321
+ };
322
+ }
323
+ /** `adr accept <decision> [--copy-scope | --no-copy-scope] …` (design §5.6, §6.10). */
324
+ const acceptCommand = {
325
+ name: "accept",
326
+ summary: "accept a proposed decision, superseding the decisions it declares",
327
+ usage: "accept <decision> [--copy-scope | --no-copy-scope] [--commit | --no-commit] [-m <message>]",
328
+ options: {
329
+ "copy-scope": { type: "boolean" },
330
+ ...COMMIT_OPTIONS
331
+ },
332
+ positionals: {
333
+ min: 1,
334
+ max: 1,
335
+ names: ["decision"]
336
+ },
337
+ ref: "reject",
338
+ async run({ io, flags, values, positionals }) {
339
+ const input = positionals[0] ?? "";
340
+ const log = await openForCli(io, flags);
341
+ const { actor, notes: attribution } = await resolveAttribution(log.git, { env: io.env });
342
+ const seen = { offer: null };
343
+ const copyScope = copyChoice(io, flags.json, values["copy-scope"], (offer) => {
344
+ seen.offer = offer;
345
+ });
346
+ const doc = await acceptDecision(log, {
347
+ id: input,
348
+ actor
349
+ }, {
350
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
351
+ notes: attribution,
352
+ copyScope
353
+ });
354
+ const { display } = await displayAfterWrite(log);
355
+ const v = doc.decision;
356
+ return {
357
+ exit: doc.ok ? 0 : 1,
358
+ json: doc,
359
+ text: writeText(doc, display, {
360
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
361
+ number: v === null || doc.commit === null || v.number === null ? null : v.display,
362
+ copied: doc.ok ? seen.offer : null
363
+ }),
364
+ notes: doc.notes
365
+ };
366
+ }
367
+ };
368
+ // src/cli/deprecate.ts
369
+ const USAGE$4 = "deprecate <decision> --reason <text> [--commit | --no-commit] [-m <message>]";
370
+ /** `adr deprecate` (design §6.5): an accepted decision no longer in force. */
371
+ const deprecateCommand = {
372
+ name: "deprecate",
373
+ summary: "deprecate an accepted decision",
374
+ usage: USAGE$4,
375
+ options: {
376
+ reason: { type: "string" },
377
+ ...COMMIT_OPTIONS
378
+ },
379
+ positionals: {
380
+ min: 1,
381
+ max: 1,
382
+ names: ["decision"]
383
+ },
384
+ ref: "reject",
385
+ async run({ io, flags, values, positionals }) {
386
+ const input = positionals[0] ?? "";
387
+ if (typeof values.reason !== "string" || values.reason.trim() === "") throw new UsageError(`missing --reason <text>; usage: adr ${USAGE$4}`);
388
+ const reason = typeof values.reason === "string" ? values.reason.trim() : null;
389
+ const log = await openForCli(io, flags);
390
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
391
+ const doc = await deprecateDecision(log, {
392
+ id: input,
393
+ reason,
394
+ actor
395
+ }, {
396
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
397
+ notes
398
+ });
399
+ const { display } = await displayAfterWrite(log);
400
+ const v = doc.decision;
401
+ return {
402
+ exit: doc.ok ? 0 : 1,
403
+ json: doc,
404
+ text: writeText(doc, display, {
405
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
406
+ number: null,
407
+ reason
408
+ }),
409
+ notes: doc.notes
410
+ };
411
+ }
412
+ };
413
+ // src/cli/for.ts
414
+ /** `adr for <path>… [--ref <ref>]` (design §6.9): the decisions that govern each file. */
415
+ const forCommand = {
416
+ name: "for",
417
+ summary: "list the decisions that govern files, by scope glob and code anchor",
418
+ usage: "for <path>…",
419
+ options: {},
420
+ positionals: {
421
+ min: 1,
422
+ max: 100,
423
+ names: ["path"]
424
+ },
425
+ ref: "read",
426
+ async run({ io, flags, positionals }) {
427
+ const log = await openForCli(io, flags);
428
+ const read = await readDecisions(log);
429
+ const doc = await decisionsFor(read, log, {
430
+ paths: positionals,
431
+ cwd: io.cwd
432
+ });
433
+ const display = (id) => read.projection.views.get(id)?.display ?? id;
434
+ return {
435
+ exit: 0,
436
+ json: doc,
437
+ text: forText(doc, display, read.adr.anchorKeyword),
438
+ notes: doc.notes
439
+ };
440
+ }
441
+ };
442
+ // src/cli/init.ts
443
+ /** `adr init [--template <name>]… [--commit | --no-commit] [-m <message>]` (design §6.2). */
444
+ const initCommand = {
445
+ name: "init",
446
+ summary: "set up a decision log: a config file and starter templates",
447
+ usage: "init [--template <name>]… [--commit | --no-commit] [-m <message>]",
448
+ options: {
449
+ template: {
450
+ type: "string",
451
+ multiple: true
452
+ },
453
+ ...COMMIT_OPTIONS
454
+ },
455
+ positionals: {
456
+ min: 0,
457
+ max: 0,
458
+ names: []
459
+ },
460
+ ref: "reject",
461
+ async run({ io, flags, values }) {
462
+ const templates = Array.isArray(values.template) ? values.template : void 0;
463
+ const log = await openForCli(io, flags);
464
+ const doc = await initDecisionLog(log, {
465
+ ...templates === void 0 ? {} : { templates },
466
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit })
467
+ });
468
+ return {
469
+ exit: 0,
470
+ json: doc,
471
+ text: initText(doc),
472
+ notes: doc.notes
473
+ };
474
+ }
475
+ };
476
+ // src/cli/link.ts
477
+ /** `adr link <decision> <target> [--rel <rel>]` (design §6.7): a typed link, in any state. */
478
+ const linkCommand = {
479
+ name: "link",
480
+ summary: "link a decision to another one, here or in a configured source",
481
+ usage: "link <decision> <target> [--rel relates-to|refines|depends-on] [--commit | --no-commit] [-m <message>]",
482
+ options: {
483
+ rel: { type: "string" },
484
+ ...COMMIT_OPTIONS
485
+ },
486
+ positionals: {
487
+ min: 2,
488
+ max: 2,
489
+ names: ["decision", "target"]
490
+ },
491
+ ref: "reject",
492
+ async run({ io, flags, values, positionals }) {
493
+ const [input = "", to = ""] = positionals;
494
+ const rel = typeof values.rel === "string" ? values.rel : "relates-to";
495
+ const log = await openForCli(io, flags);
496
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
497
+ const doc = await linkDecision(log, {
498
+ id: input,
499
+ to,
500
+ rel,
501
+ actor
502
+ }, {
503
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
504
+ notes
505
+ });
506
+ const { display } = await displayAfterWrite(log);
507
+ const v = doc.decision;
508
+ return {
509
+ exit: doc.ok ? 0 : 1,
510
+ json: doc,
511
+ text: writeText(doc, display, {
512
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
513
+ number: null
514
+ }),
515
+ notes: doc.notes
516
+ };
517
+ }
518
+ };
519
+ // src/cli/list.ts
520
+ const plural = (n, one, many) => n === 1 ? `1 ${one}` : `${n} ${many}`;
521
+ function statuses(values) {
522
+ return (Array.isArray(values.status) ? values.status : []).map((s) => {
523
+ if (!DECISION_STATUSES.includes(s)) throw new UsageError(`unknown status ${JSON.stringify(s)}; use one of ${DECISION_STATUSES.join(", ")}`);
524
+ return s;
525
+ });
526
+ }
527
+ /** `adr list [--status <status>]…` (design §5.6). */
528
+ const listCommand = {
529
+ name: "list",
530
+ summary: "list decisions with their computed status",
531
+ usage: "list [--status <status>]…",
532
+ options: { status: {
533
+ type: "string",
534
+ multiple: true
535
+ } },
536
+ positionals: {
537
+ min: 0,
538
+ max: 0,
539
+ names: []
540
+ },
541
+ ref: "read",
542
+ async run({ io, flags, values }) {
543
+ const status = statuses(values);
544
+ const read = await readDecisions(await openForCli(io, flags));
545
+ const doc = listDecisions(read, { status });
546
+ const notes = [...doc.notes];
547
+ if (!read.numbering.available && doc.decisions.length > 0) notes.push(`numbers are unavailable (${unavailableReason(read.numbering)}); decisions display by id`);
548
+ if (doc.findings.length > 0) notes.push(`the decision log has ${plural(doc.findings.length, "problem", "problems")}; run adr verify`);
549
+ if (doc.unreadable.length > 0) notes.push(`${plural(doc.unreadable.length, "decision uses", "decisions use")} a newer format; upgrade adr`);
550
+ if (doc.unfoldable.length > 0) notes.push(`${plural(doc.unfoldable.length, "decision", "decisions")} can't be read; run adr verify`);
551
+ const display = (id) => read.projection.views.get(id)?.display ?? id;
552
+ return {
553
+ exit: 0,
554
+ json: doc,
555
+ text: listText(doc, display),
556
+ notes
557
+ };
558
+ }
559
+ };
560
+ // src/cli/editor.ts
561
+ /**
562
+ * The user's editor as an `edit` callback (design §5.7), or `null` when neither `VISUAL` nor
563
+ * `EDITOR` is set. The editor string is run by `sh -c` the way git runs `core.editor`, with the
564
+ * path as the positional parameter `$1`, never interpolated. When stdout isn't a TTY (piped
565
+ * `--json`, say), the editor's stdout goes to stderr so the CLI's stdout stays one document.
566
+ * The callback never throws: a spawn error or a non-zero exit becomes the note.
567
+ */
568
+ function editorFor(env, o) {
569
+ const editor = [env.VISUAL, env.EDITOR].map((v) => v?.trim() ?? "").find((v) => v !== "");
570
+ if (editor === void 0) return null;
571
+ return (absPath) => new Promise((resolve) => {
572
+ const fail = (e) => resolve(`could not start the editor: ${e instanceof Error ? e.message : String(e)}`);
573
+ try {
574
+ const child = spawn("sh", [
575
+ "-c",
576
+ `${editor} "$1"`,
577
+ "sh",
578
+ absPath
579
+ ], {
580
+ env,
581
+ stdio: o.stdoutIsTTY ? "inherit" : [
582
+ "inherit",
583
+ 2,
584
+ "inherit"
585
+ ]
586
+ });
587
+ child.on("error", fail);
588
+ child.on("close", (code, signal) => {
589
+ if (code === 0) resolve(null);
590
+ else resolve(`the editor ${code === null ? `was killed by ${signal ?? "a signal"}` : `exited with status ${code}`}; the draft is staged as rendered`);
591
+ });
592
+ } catch (e) {
593
+ fail(e);
594
+ }
595
+ });
596
+ }
597
+ // src/cli/new.ts
598
+ const strings$1 = (v) => Array.isArray(v) ? v : [];
599
+ /** `--section <id>=<markdown>` values, split at the first `=`; each id at most once. */
600
+ function sections(values) {
601
+ const out = {};
602
+ for (const value of values) {
603
+ const at = value.indexOf("=");
604
+ const id = at < 0 ? "" : value.slice(0, at).trim();
605
+ if (id === "") throw new UsageError(`--section needs <id>=<markdown>, not ${JSON.stringify(value)}`);
606
+ if (Object.hasOwn(out, id)) throw new UsageError(`--section ${id} is given twice`);
607
+ out[id] = value.slice(at + 1);
608
+ }
609
+ return out;
610
+ }
611
+ /**
612
+ * `adr new <title> …` (design §5.6). The editor runs with `--edit`, or without `--no-edit` when
613
+ * the terminal is interactive and an editor is set.
614
+ */
615
+ const newCommand = {
616
+ name: "new",
617
+ summary: "propose a decision from a template and stage it",
618
+ usage: "new <title> [--template <name>] [--supersedes <decision>]… [--section <id>=<markdown>]… [--edit | --no-edit] [--commit | --no-commit] [-m <message>]",
619
+ options: {
620
+ template: { type: "string" },
621
+ supersedes: {
622
+ type: "string",
623
+ multiple: true
624
+ },
625
+ section: {
626
+ type: "string",
627
+ multiple: true
628
+ },
629
+ edit: { type: "boolean" },
630
+ ...COMMIT_OPTIONS
631
+ },
632
+ positionals: {
633
+ min: 1,
634
+ max: 1,
635
+ names: ["title"]
636
+ },
637
+ ref: "reject",
638
+ async run({ io, flags, values, positionals }) {
639
+ const title = positionals[0] ?? "";
640
+ const given = sections(strings$1(values.section));
641
+ const log = await openForCli(io, flags);
642
+ const { actor, notes: attribution } = await resolveAttribution(log.git, { env: io.env });
643
+ const { commit: requested, message } = commitChoice(values, { autoCommit: log.adr.autoCommit });
644
+ const notes = [];
645
+ const editor = editorFor(io.env, { stdoutIsTTY: io.stdoutIsTTY });
646
+ const wantsEdit = values.edit === true || values.edit === void 0 && io.interactive;
647
+ if (values.edit === true && editor === null) notes.push("no editor: set VISUAL or EDITOR");
648
+ const edit = wantsEdit && editor !== null ? async (abs) => {
649
+ const note = await editor(abs);
650
+ if (note !== null) notes.push(note);
651
+ return note;
652
+ } : void 0;
653
+ const doc = await proposeDecision(log, {
654
+ title,
655
+ sections: given,
656
+ supersedes: strings$1(values.supersedes),
657
+ actor,
658
+ ...typeof values.template === "string" ? { template: values.template } : {}
659
+ }, {
660
+ commit: requested,
661
+ message,
662
+ notes: attribution,
663
+ ...edit === void 0 ? {} : { edit },
664
+ onNote: (note) => notes.push(requested ? `not committed: ${note}` : note)
665
+ });
666
+ const { display } = await displayAfterWrite(log);
667
+ const v = doc.decision;
668
+ const unfilled = unfilledWarning(doc);
669
+ return {
670
+ exit: doc.ok ? 0 : 1,
671
+ json: doc,
672
+ text: writeText(doc, display, {
673
+ subject: title.trim(),
674
+ number: v === null || v.number === null ? null : v.display,
675
+ actor,
676
+ acceptRequiresHuman: log.adr.acceptRequiresHuman
677
+ }),
678
+ notes: [
679
+ ...doc.notes,
680
+ ...notes,
681
+ ...unfilled === null ? [] : [unfilled]
682
+ ]
683
+ };
684
+ }
685
+ };
686
+ // src/cli/note.ts
687
+ /** `adr note <decision> <text>` (design §6.6): a note on a decision in any state. */
688
+ const noteCommand = {
689
+ name: "note",
690
+ summary: "add a note to a decision, in any state",
691
+ usage: "note <decision> <text> [--commit | --no-commit] [-m <message>]",
692
+ options: { ...COMMIT_OPTIONS },
693
+ positionals: {
694
+ min: 2,
695
+ max: 2,
696
+ names: ["decision", "text"]
697
+ },
698
+ ref: "reject",
699
+ async run({ io, flags, values, positionals }) {
700
+ const [input = "", text = ""] = positionals;
701
+ const log = await openForCli(io, flags);
702
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
703
+ const doc = await noteDecision(log, {
704
+ id: input,
705
+ text,
706
+ actor
707
+ }, {
708
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
709
+ notes
710
+ });
711
+ const { display } = await displayAfterWrite(log);
712
+ const v = doc.decision;
713
+ return {
714
+ exit: doc.ok ? 0 : 1,
715
+ json: doc,
716
+ text: writeText(doc, display, {
717
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
718
+ number: null
719
+ }),
720
+ notes: doc.notes
721
+ };
722
+ }
723
+ };
724
+ // src/cli/reject.ts
725
+ const USAGE$3 = "reject <decision> --reason <text> [--commit | --no-commit] [-m <message>]";
726
+ /** `adr reject` (design §6.3): a proposal that won't be adopted. */
727
+ const rejectCommand = {
728
+ name: "reject",
729
+ summary: "reject a proposed decision",
730
+ usage: USAGE$3,
731
+ options: {
732
+ reason: { type: "string" },
733
+ ...COMMIT_OPTIONS
734
+ },
735
+ positionals: {
736
+ min: 1,
737
+ max: 1,
738
+ names: ["decision"]
739
+ },
740
+ ref: "reject",
741
+ async run({ io, flags, values, positionals }) {
742
+ const input = positionals[0] ?? "";
743
+ if (typeof values.reason !== "string" || values.reason.trim() === "") throw new UsageError(`missing --reason <text>; usage: adr ${USAGE$3}`);
744
+ const reason = typeof values.reason === "string" ? values.reason.trim() : null;
745
+ const log = await openForCli(io, flags);
746
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
747
+ const doc = await rejectDecision(log, {
748
+ id: input,
749
+ reason,
750
+ actor
751
+ }, {
752
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
753
+ notes
754
+ });
755
+ const { display } = await displayAfterWrite(log);
756
+ const v = doc.decision;
757
+ return {
758
+ exit: doc.ok ? 0 : 1,
759
+ json: doc,
760
+ text: writeText(doc, display, {
761
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
762
+ number: null,
763
+ reason
764
+ }),
765
+ notes: doc.notes
766
+ };
767
+ }
768
+ };
769
+ // src/cli/scope.ts
770
+ const USAGE$2 = "scope <decision> ([--set <glob>]… | --clear | [--add <glob>]… [--remove <glob>]…) [--commit | --no-commit] [-m <message>] | scope [<decision>] --follow-renames";
771
+ /** The flag names `applySetOp` uses in its messages (design §3.2). */
772
+ const NAMES = {
773
+ set: "--set",
774
+ add: "--add",
775
+ remove: "--remove",
776
+ clear: "--clear"
777
+ };
778
+ const strings = (v) => Array.isArray(v) ? v : [];
779
+ /** `--set`/`--clear`/`--add`/`--remove` as a set operation; `--clear` is `{ set: [] }`. */
780
+ function setOp(values) {
781
+ const set = strings(values.set);
782
+ const add = strings(values.add);
783
+ const remove = strings(values.remove);
784
+ if (values.clear === true) {
785
+ if (set.length > 0 || add.length > 0 || remove.length > 0) throw new UsageError("--clear can't be combined with --set, --add or --remove");
786
+ return { set: [] };
787
+ }
788
+ return {
789
+ ...set.length > 0 ? { set } : {},
790
+ add,
791
+ remove
792
+ };
793
+ }
794
+ /**
795
+ * `--follow-renames` alone: no set operation, no commit and no message, all refused before
796
+ * `commitChoice` so the answer doesn't depend on `auto_commit` (design §6.8).
797
+ */
798
+ function checkFollowRenames(values) {
799
+ if ([
800
+ "set",
801
+ "add",
802
+ "remove"
803
+ ].some((k) => strings(values[k]).length > 0) || values.clear === true) throw new UsageError("--follow-renames can't be combined with --set, --clear, --add or --remove");
804
+ if (values.commit === true) throw new UsageError("--follow-renames stages its events for the commit that contains the renames; commit them together");
805
+ if (typeof values.message === "string") throw new UsageError("--follow-renames never commits, so it takes no --message");
806
+ }
807
+ /**
808
+ * `adr scope <decision> --set|--clear|--add|--remove …` (design §6.8): the globs of the code a
809
+ * decision governs, written as one whole-set `scoped` event. `adr scope [<decision>]
810
+ * --follow-renames` rewrites them after staged renames and stages the events, never committing.
811
+ */
812
+ const scopeCommand = {
813
+ name: "scope",
814
+ summary: "set, add or remove the globs of the code a decision governs",
815
+ usage: USAGE$2,
816
+ options: {
817
+ set: {
818
+ type: "string",
819
+ multiple: true
820
+ },
821
+ add: {
822
+ type: "string",
823
+ multiple: true
824
+ },
825
+ remove: {
826
+ type: "string",
827
+ multiple: true
828
+ },
829
+ clear: { type: "boolean" },
830
+ "follow-renames": { type: "boolean" },
831
+ ...COMMIT_OPTIONS
832
+ },
833
+ positionals: {
834
+ min: 0,
835
+ max: 1,
836
+ names: ["decision"]
837
+ },
838
+ ref: "reject",
839
+ async run({ io, flags, values, positionals }) {
840
+ const input = positionals[0];
841
+ if (values["follow-renames"] === true) {
842
+ checkFollowRenames(values);
843
+ const log = await openForCli(io, flags);
844
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
845
+ const read = await readDecisions(log);
846
+ const before = new Map([...read.projection.views].map(([id, v]) => [id, v.applies_to]));
847
+ const renamed = (await stagedRenames(log.git)).length > 0;
848
+ const doc = await followScopeRenames(log, {
849
+ ...input === void 0 ? {} : { id: input },
850
+ actor
851
+ }, { notes });
852
+ const { display } = await displayAfterWrite(log);
853
+ const v = doc.decision;
854
+ return {
855
+ exit: doc.ok ? 0 : 1,
856
+ json: doc,
857
+ text: writeText(doc, display, {
858
+ subject: v === null ? (input ?? "").trim() : `${display(v.id)}: ${v.title}`,
859
+ number: null,
860
+ followRenames: {
861
+ before,
862
+ renamed
863
+ }
864
+ }),
865
+ notes: doc.notes
866
+ };
867
+ }
868
+ const op = setOp(values);
869
+ if (input === void 0) throw new UsageError(`missing argument <decision>; usage: adr ${USAGE$2}`);
870
+ const log = await openForCli(io, flags);
871
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
872
+ const choice = commitChoice(values, { autoCommit: log.adr.autoCommit });
873
+ const read = await readDecisions(log);
874
+ const before = read.projection.views.get(resolveDecision(read, input))?.applies_to ?? [];
875
+ const doc = await scopeDecision(log, {
876
+ id: input,
877
+ op,
878
+ actor,
879
+ names: NAMES
880
+ }, {
881
+ ...choice,
882
+ notes
883
+ });
884
+ const { display } = await displayAfterWrite(log);
885
+ const v = doc.decision;
886
+ return {
887
+ exit: doc.ok ? 0 : 1,
888
+ json: doc,
889
+ text: writeText(doc, display, {
890
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
891
+ number: null,
892
+ scopeBefore: before
893
+ }),
894
+ notes: doc.notes
895
+ };
896
+ }
897
+ };
898
+ // src/cli/show.ts
899
+ /** `adr show <decision> [--history]` (design §5.6). */
900
+ const showCommand = {
901
+ name: "show",
902
+ summary: "show a decision with its state header, chain, history and body",
903
+ usage: "show <decision> [--history]",
904
+ options: { history: { type: "boolean" } },
905
+ positionals: {
906
+ min: 1,
907
+ max: 1,
908
+ names: ["decision"]
909
+ },
910
+ ref: "read",
911
+ async run({ io, flags, values, positionals }) {
912
+ const read = await readDecisions(await openForCli(io, flags));
913
+ const id = resolveDecision(read, positionals[0] ?? "");
914
+ const doc = await showDecision(read, id, { history: values.history === true });
915
+ const display = (s) => read.projection.views.get(s)?.display ?? s;
916
+ return {
917
+ exit: 0,
918
+ json: doc,
919
+ text: showText(doc, display),
920
+ notes: doc.notes
921
+ };
922
+ }
923
+ };
924
+ // src/cli/supersede.ts
925
+ const USAGE$1 = "supersede <decision> --by <decision> [--reason <text>] [--commit | --no-commit] [-m <message>]";
926
+ /** `adr supersede <decision> --by <decision> …` (design §5.6). */
927
+ const supersedeCommand = {
928
+ name: "supersede",
929
+ summary: "mark an accepted decision as superseded by another accepted one",
930
+ usage: USAGE$1,
931
+ options: {
932
+ by: { type: "string" },
933
+ reason: { type: "string" },
934
+ ...COMMIT_OPTIONS
935
+ },
936
+ positionals: {
937
+ min: 1,
938
+ max: 1,
939
+ names: ["decision"]
940
+ },
941
+ ref: "reject",
942
+ async run({ io, flags, values, positionals }) {
943
+ const input = positionals[0] ?? "";
944
+ if (typeof values.by !== "string") throw new UsageError(`missing --by <decision>; usage: adr ${USAGE$1}`);
945
+ const log = await openForCli(io, flags);
946
+ const { actor, notes: attribution } = await resolveAttribution(log.git, { env: io.env });
947
+ const doc = await supersedeDecision(log, {
948
+ id: input,
949
+ by: values.by,
950
+ actor,
951
+ ...typeof values.reason === "string" ? { reason: values.reason } : {}
952
+ }, {
953
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
954
+ notes: attribution
955
+ });
956
+ const { display } = await displayAfterWrite(log);
957
+ const v = doc.decision;
958
+ return {
959
+ exit: doc.ok ? 0 : 1,
960
+ json: doc,
961
+ text: writeText(doc, display, {
962
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
963
+ number: null
964
+ }),
965
+ notes: doc.notes
966
+ };
967
+ }
968
+ };
969
+ /** `adr withdraw` (design §6.4): a proposal its author takes back. */
970
+ const withdrawCommand = {
971
+ name: "withdraw",
972
+ summary: "withdraw a proposed decision",
973
+ usage: "withdraw <decision> [--reason <text>] [--commit | --no-commit] [-m <message>]",
974
+ options: {
975
+ reason: { type: "string" },
976
+ ...COMMIT_OPTIONS
977
+ },
978
+ positionals: {
979
+ min: 1,
980
+ max: 1,
981
+ names: ["decision"]
982
+ },
983
+ ref: "reject",
984
+ async run({ io, flags, values, positionals }) {
985
+ const input = positionals[0] ?? "";
986
+ if (typeof values.reason === "string" && values.reason.trim() === "") throw new UsageError("--reason must not be empty");
987
+ const reason = typeof values.reason === "string" ? values.reason.trim() : null;
988
+ const log = await openForCli(io, flags);
989
+ const { actor, notes } = await resolveAttribution(log.git, { env: io.env });
990
+ const doc = await withdrawDecision(log, {
991
+ id: input,
992
+ reason,
993
+ actor
994
+ }, {
995
+ ...commitChoice(values, { autoCommit: log.adr.autoCommit }),
996
+ notes
997
+ });
998
+ const { display } = await displayAfterWrite(log);
999
+ const v = doc.decision;
1000
+ return {
1001
+ exit: doc.ok ? 0 : 1,
1002
+ json: doc,
1003
+ text: writeText(doc, display, {
1004
+ subject: v === null ? input.trim() : `${display(v.id)}: ${v.title}`,
1005
+ number: null,
1006
+ reason
1007
+ }),
1008
+ notes: doc.notes
1009
+ };
1010
+ }
1011
+ };
1012
+ // src/cli/commands.ts
1013
+ /**
1014
+ * ledger's shared `verify`: the log as the mode reads it (the index for `--staged`, else the
1015
+ * `--ref` tree or the work tree), every phase-1 check, and `[verify] fail_on`.
1016
+ */
1017
+ const verify = verifyCommand({ async open({ io, flags, mode }) {
1018
+ const log = await openForCli(io, flags, { reader: mode.type === "staged" ? "index" : "worktree" });
1019
+ return {
1020
+ git: log.git,
1021
+ kind: adrKind,
1022
+ layout: log.layout,
1023
+ checks: adrChecks(log, await templateHistoryRef(log.git, mode)),
1024
+ failOn: log.adr.failOn,
1025
+ notes: log.notes
1026
+ };
1027
+ } });
1028
+ /** The `adr` tool: its commands, for ledger's `runCli`. */
1029
+ function adrCli(version) {
1030
+ return {
1031
+ name: "adr",
1032
+ version,
1033
+ summary: "Architecture decision records kept as event logs in git.",
1034
+ commands: [
1035
+ initCommand,
1036
+ newCommand,
1037
+ acceptCommand,
1038
+ rejectCommand,
1039
+ withdrawCommand,
1040
+ deprecateCommand,
1041
+ supersedeCommand,
1042
+ noteCommand,
1043
+ linkCommand,
1044
+ scopeCommand,
1045
+ forCommand,
1046
+ listCommand,
1047
+ showCommand,
1048
+ verify
1049
+ ]
1050
+ };
1051
+ }
1052
+ // src/cli.ts
1053
+ const { version } = createRequire(import.meta.url)("../package.json");
1054
+ process.exitCode = await runCli(adrCli(version), processIO());
1055
+ export {};
1056
+
1057
+ //# sourceMappingURL=cli.js.map