@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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/cli.js +1057 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/kind-BcHjCHvi.js +415 -0
- package/dist/kind-BcHjCHvi.js.map +1 -0
- package/dist/kind.js +1 -0
- package/dist/types/cli/accept.d.ts +3 -0
- package/dist/types/cli/commands.d.ts +3 -0
- package/dist/types/cli/deprecate.d.ts +3 -0
- package/dist/types/cli/editor.d.ts +12 -0
- package/dist/types/cli/for.d.ts +3 -0
- package/dist/types/cli/init.d.ts +3 -0
- package/dist/types/cli/link.d.ts +3 -0
- package/dist/types/cli/list.d.ts +3 -0
- package/dist/types/cli/new.d.ts +6 -0
- package/dist/types/cli/note.d.ts +3 -0
- package/dist/types/cli/open.d.ts +17 -0
- package/dist/types/cli/reject.d.ts +3 -0
- package/dist/types/cli/scope.d.ts +7 -0
- package/dist/types/cli/show.d.ts +3 -0
- package/dist/types/cli/supersede.d.ts +3 -0
- package/dist/types/cli/text.d.ts +46 -0
- package/dist/types/cli/withdraw.d.ts +3 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/config.d.ts +33 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/init.d.ts +27 -0
- package/dist/types/kind/project.d.ts +50 -0
- package/dist/types/kind/reduce.d.ts +35 -0
- package/dist/types/kind/rules.d.ts +20 -0
- package/dist/types/kind/schemas.d.ts +37 -0
- package/dist/types/kind.d.ts +18 -0
- package/dist/types/log.d.ts +53 -0
- package/dist/types/scope.d.ts +51 -0
- package/dist/types/templates/builtin.d.ts +8 -0
- package/dist/types/verify.d.ts +10 -0
- package/dist/types/views.d.ts +65 -0
- package/dist/types/write.d.ts +333 -0
- package/dist/write-9IaqHRrY.js +1986 -0
- package/dist/write-9IaqHRrY.js.map +1 -0
- package/package.json +55 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kind-BcHjCHvi.js","names":[],"sources":["../src/kind/project.ts","../src/kind/reduce.ts","../src/kind/rules.ts","../src/kind/schemas.ts","../src/kind.ts"],"sourcesContent":["import type { FoldedSubject, ProjectContext } from \"@nikovirtala/ledger\";\nimport type { DecisionState } from \"./reduce.js\";\nimport type { DecisionEvents, DecisionStatus } from \"./schemas.js\";\n\n/** One decision as the CLI, the library and MCP show it (01:173–192, plus the added fields). */\nexport interface DecisionView {\n readonly id: string;\n readonly number: number | null;\n /** `ADR-0007`, or the id when unnumbered. */\n readonly display: string;\n readonly title: string;\n readonly status: DecisionStatus;\n readonly superseded_by: string | null;\n /** Ids whose applied `superseded.by` is this id, sorted. */\n readonly supersedes: readonly string[];\n /** The proposal's intent (01:164). */\n readonly declared_supersedes: readonly string[];\n /** Head of the `superseded_by` chain; `null` only on a cycle. */\n readonly effective: string | null;\n /** `[id, …, effective]`, following `superseded_by` forward. */\n readonly chain: readonly string[];\n readonly template: { readonly name: string; readonly version: number };\n readonly body_path: string | null;\n /** Recorded at acceptance. */\n readonly body_sha256: string | null;\n /** `null` until accepted; whether the current body hash equals the recorded one. */\n readonly body_intact: boolean | null;\n readonly links: readonly { readonly to: string; readonly rel: string }[];\n readonly applies_to: readonly string[];\n readonly notes: number;\n readonly proposed_at: string;\n readonly decided_at: string | null;\n readonly status_at: string;\n /** `proposed 2026-03-12`: state-changing events, in fold order. */\n readonly history: readonly string[];\n}\n\ntype Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;\ntype Context = ProjectContext<DecisionStatus, DecisionState, DecisionEvents>;\n\nconst NUMBER = /^(?:adr-?)?0*([1-9][0-9]{0,8})$/i;\n\n/** `ADR-0007`: at least 4 digits, growing past 9999. */\nexport function formatDecisionNumber(n: number): string {\n return `ADR-${String(n).padStart(4, \"0\")}`;\n}\n\n/** `7`, `0007`, `ADR-7`, `adr-0007` → 7; `null` for anything else. */\nexport function parseDecisionNumber(s: string): number | null {\n const m = NUMBER.exec(s.trim());\n return m === null ? null : Number(m[1]);\n}\n\n/**\n * Follows `superseded_by` from `id`. A visited set stops on a cycle (`effective: null`); a\n * reference to a subject that isn't folded ends the chain there.\n */\nfunction chainOf(id: string, ctx: Context): { chain: string[]; effective: string | null } {\n const chain = [id];\n let cur = id;\n for (;;) {\n const next = ctx.log.subjects.get(cur)?.state?.supersededBy ?? null;\n if (next === null) return { chain, effective: cur };\n if (chain.includes(next)) return { chain, effective: null };\n chain.push(next);\n cur = next;\n }\n}\n\n/** Pure: the view of one folded decision. */\nexport function project(subject: Subject, ctx: Context): DecisionView {\n const { id } = subject;\n const s = subject.state;\n if (s === null || subject.status === null) {\n throw new Error(`decision ${id} is not folded`);\n }\n const supersedes = [...ctx.log.subjects.values()]\n .filter((o) => o.id !== id && o.state?.supersededBy === id)\n .map((o) => o.id)\n .sort();\n const { chain, effective } = chainOf(id, ctx);\n const current = ctx.bodySha256(id);\n return {\n id,\n number: ctx.number(id),\n display: ctx.display(id),\n title: s.title,\n status: subject.status,\n superseded_by: s.supersededBy,\n supersedes,\n declared_supersedes: s.declaredSupersedes,\n effective,\n chain,\n template: s.template,\n body_path: subject.loaded.body?.path ?? subject.loaded.bodyPaths[0] ?? null,\n body_sha256: s.bodySha256,\n body_intact: s.bodySha256 === null ? null : current === s.bodySha256,\n links: s.links,\n applies_to: s.appliesTo,\n notes: s.notes,\n proposed_at: s.proposedAt,\n decided_at: s.decidedAt,\n status_at: s.statusAt,\n history: s.history.map((h) => `${h.type} ${h.at.slice(0, 10)}`),\n };\n}\n","import type { Actor, FoldEvent } from \"@nikovirtala/ledger\";\nimport type { DecisionEvents } from \"./schemas.js\";\n\n/** A decision's own state, maintained by `reduce` over its applied events. */\nexport interface DecisionState {\n readonly title: string;\n readonly template: { readonly name: string; readonly version: number };\n readonly declaredSupersedes: readonly string[];\n /** From the applied `accepted` event. */\n readonly bodySha256: string | null;\n readonly acceptedBy: Actor | null;\n readonly supersededBy: string | null;\n /** In fold order, duplicates kept once. */\n readonly links: readonly { readonly to: string; readonly rel: string }[];\n /** The latest `scoped` set (latest wins). */\n readonly appliesTo: readonly string[];\n readonly notes: number;\n /** State-changing events only, in fold order. */\n readonly history: readonly { readonly type: string; readonly at: string }[];\n readonly proposedAt: string;\n /** `at` of `accepted`, `rejected` or `withdrawn`. */\n readonly decidedAt: string | null;\n /** `at` of the latest state-changing event. */\n readonly statusAt: string;\n}\n\nconst EMPTY: DecisionState = {\n title: \"\",\n template: { name: \"\", version: 0 },\n declaredSupersedes: [],\n bodySha256: null,\n acceptedBy: null,\n supersededBy: null,\n links: [],\n appliesTo: [],\n notes: 0,\n history: [],\n proposedAt: \"\",\n decidedAt: null,\n statusAt: \"\",\n};\n\n/** Pure: the state after one applied event. The fold only passes events the machine allows. */\nexport function reduce(state: DecisionState | null, e: FoldEvent<DecisionEvents>): DecisionState {\n const s = state ?? EMPTY;\n const changed = (extra: Partial<DecisionState> = {}): DecisionState => ({\n ...s,\n ...extra,\n history: [...s.history, { type: e.type, at: e.at }],\n statusAt: e.at,\n });\n switch (e.type) {\n case \"proposed\":\n return changed({\n title: e.data.title,\n template: { name: e.data.template, version: e.data.template_version },\n declaredSupersedes: [...(e.data.supersedes ?? [])],\n proposedAt: e.at,\n });\n case \"accepted\":\n return changed({ bodySha256: e.data.body_sha256, acceptedBy: e.actor, decidedAt: e.at });\n case \"rejected\":\n case \"withdrawn\":\n return changed({ decidedAt: e.at });\n case \"deprecated\":\n return changed();\n case \"superseded\":\n return changed({ supersededBy: e.data.by });\n case \"noted\":\n return { ...s, notes: s.notes + 1 };\n case \"linked\": {\n const { to, rel } = e.data;\n const known = s.links.some((l) => l.to === to && l.rel === rel);\n return known ? s : { ...s, links: [...s.links, { to, rel }] };\n }\n case \"scoped\":\n return { ...s, appliesTo: [...e.data.set] };\n }\n}\n","import type { FoldedLog, FoldedSubject, KindFinding } from \"@nikovirtala/ledger\";\nimport type { DecisionState } from \"./reduce.js\";\nimport type { DecisionEvents, DecisionStatus } from \"./schemas.js\";\n\n/** Options of the decision kind's `invariants` and `relate`. */\nexport interface AdrOptions {\n /** Default `true`: an agent may propose, but only a human may accept (01:98). */\n readonly acceptRequiresHuman: boolean;\n}\n\ntype Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;\ntype Log = FoldedLog<DecisionStatus, DecisionState, DecisionEvents>;\n\n/** `decision/agent-accepted`: an applied `accepted` event written by an agent under the human policy. */\nexport function invariants(subject: Subject, options: AdrOptions): readonly KindFinding[] {\n if (!options.acceptRequiresHuman) return [];\n return subject.applied.flatMap(({ event }) =>\n event.type === \"accepted\" && event.actor.kind === \"agent\"\n ? [\n {\n severity: \"error\" as const,\n rule: \"decision/agent-accepted\",\n message: `accepted by agent ${event.actor.name}; accept_requires_human needs a human`,\n subject: subject.id,\n event: event.id,\n },\n ]\n : [],\n );\n}\n\n/**\n * Qualified ids (`<source>:<id>`) name decisions in other logs. Phase 1 checks local references\n * only and skips qualified ones, which only hand-written events can hold; CI checks them at the\n * pinned ref in phase 2 (design §E).\n */\nconst isQualified = (ref: string) => ref.includes(\":\");\n\n/**\n * Cross-subject rules: `decision/reference-missing` for a local `supersedes`, `superseded.by` or\n * `linked.to` naming no subject in the log; `decision/superseded-by-unaccepted` when the target of\n * an applied `superseded.by` has no applied `accepted` event in this tree; and\n * `decision/supersession-cycle`, once per cycle on its smallest id.\n */\nexport function relate(log: Log): readonly KindFinding[] {\n const out: KindFinding[] = [];\n const isAccepted = (id: string) =>\n log.subjects.get(id)?.applied.some((a) => a.event.type === \"accepted\") ?? false;\n const missing = (subject: string, event: string, ref: string, what: string) => {\n if (isQualified(ref) || log.subjects.has(ref)) return false;\n out.push({\n severity: \"error\",\n rule: \"decision/reference-missing\",\n message: `${what} ${ref}, which is not in the log`,\n subject,\n event,\n related: [ref],\n });\n return true;\n };\n\n for (const [id, s] of log.subjects) {\n for (const { event } of s.applied) {\n switch (event.type) {\n case \"proposed\":\n for (const ref of event.data.supersedes ?? []) missing(id, event.id, ref, \"supersedes\");\n break;\n case \"superseded\": {\n const by = event.data.by;\n if (missing(id, event.id, by, \"superseded by\") || isQualified(by)) break;\n if (!isAccepted(by)) {\n out.push({\n severity: \"error\",\n rule: \"decision/superseded-by-unaccepted\",\n message: `superseded by ${by}, which is not accepted`,\n subject: id,\n event: event.id,\n related: [by],\n });\n }\n break;\n }\n case \"linked\":\n missing(id, event.id, event.data.to, \"links to\");\n break;\n default:\n break;\n }\n }\n }\n\n const next = (id: string) => log.subjects.get(id)?.state?.supersededBy ?? null;\n const reported = new Set<string>();\n for (const start of log.subjects.keys()) {\n const path: string[] = [];\n let cur: string | null = start;\n while (cur !== null && !path.includes(cur)) {\n path.push(cur);\n cur = next(cur);\n }\n if (cur === null) continue;\n const cycle = path.slice(path.indexOf(cur));\n const smallest = [...cycle].sort()[0];\n if (smallest === undefined || reported.has(smallest)) continue;\n reported.add(smallest);\n const from = cycle.indexOf(smallest);\n const ordered = [...cycle.slice(from), ...cycle.slice(0, from), smallest];\n const event = log.subjects.get(smallest)?.applied.find((a) => a.event.type === \"superseded\")\n ?.event.id;\n out.push({\n severity: \"error\",\n rule: \"decision/supersession-cycle\",\n message: `supersession cycle: ${ordered.join(\" → \")}`,\n subject: smallest,\n ...(event === undefined ? {} : { event }),\n related: [...cycle].sort(),\n });\n }\n return out;\n}\n","import { eventType, setEventType } from \"@nikovirtala/ledger\";\n\n/** A decision's computed status. */\nexport type DecisionStatus =\n | \"proposed\"\n | \"accepted\"\n | \"rejected\"\n | \"withdrawn\"\n | \"deprecated\"\n | \"superseded\";\n\n/** Every status, in lifecycle order. */\nexport const DECISION_STATUSES: readonly DecisionStatus[] = Object.freeze([\n \"proposed\",\n \"accepted\",\n \"rejected\",\n \"withdrawn\",\n \"deprecated\",\n \"superseded\",\n]);\n\n/** A local subject id, or a qualified `<source>:<id>` one. */\nexport const REF = \"^(?:[a-z][a-z0-9-]*:)?[0-9a-hjkmnp-tv-z]{8}$\";\n\nconst ref = { type: \"string\", pattern: REF } as const;\nconst SINGLE_LINE = \"^[^\\\\r\\\\n]*$\";\n\n/** How a `linked` decision relates to its target. */\nexport type LinkRel = \"relates-to\" | \"refines\" | \"depends-on\";\n\n/** The decision kind's event types (01:128–136): data schemas, allowed states and targets. */\nexport const decisionEvents = {\n proposed: eventType<{\n title: string;\n template: string;\n template_version: number;\n supersedes?: string[];\n }>()({\n data: {\n type: \"object\",\n required: [\"title\", \"template\", \"template_version\"],\n properties: {\n title: { type: \"string\", minLength: 1, maxLength: 200, pattern: SINGLE_LINE },\n template: { type: \"string\", pattern: \"^[a-z][a-z0-9-]*$\" },\n template_version: { type: \"integer\", minimum: 1 },\n supersedes: { type: \"array\", items: ref, uniqueItems: true, maxItems: 20 },\n },\n },\n allowedIn: [],\n to: \"proposed\",\n }),\n accepted: eventType<{ body_sha256: string }>()({\n data: {\n type: \"object\",\n required: [\"body_sha256\"],\n properties: { body_sha256: { type: \"string\", pattern: \"^[0-9a-f]{64}$\" } },\n },\n allowedIn: [\"proposed\"],\n to: \"accepted\",\n }),\n rejected: eventType()({\n data: { type: \"object\" },\n allowedIn: [\"proposed\"],\n to: \"rejected\",\n reason: \"required\",\n }),\n withdrawn: eventType()({ data: { type: \"object\" }, allowedIn: [\"proposed\"], to: \"withdrawn\" }),\n deprecated: eventType()({\n data: { type: \"object\" },\n allowedIn: [\"accepted\"],\n to: \"deprecated\",\n reason: \"required\",\n }),\n superseded: eventType<{ by: string }>()({\n data: { type: \"object\", required: [\"by\"], properties: { by: ref } },\n allowedIn: [\"accepted\", \"deprecated\"],\n to: \"superseded\",\n }),\n noted: eventType<{ text: string }>()({\n data: {\n type: \"object\",\n required: [\"text\"],\n properties: { text: { type: \"string\", minLength: 1 } },\n },\n allowedIn: \"any\",\n to: null,\n }),\n linked: eventType<{ to: string; rel: LinkRel }>()({\n data: {\n type: \"object\",\n required: [\"to\", \"rel\"],\n properties: { to: ref, rel: { enum: [\"relates-to\", \"refines\", \"depends-on\"] } },\n },\n allowedIn: \"any\",\n to: null,\n }),\n // ledger's whole-set event (G11): the same schema as before, so decision/v1 is unchanged.\n scoped: setEventType({\n item: { type: \"string\", minLength: 1, maxLength: 500 },\n maxItems: 200,\n allowedIn: [\"proposed\", \"accepted\", \"deprecated\"],\n }),\n} as const;\n\nexport type DecisionEvents = typeof decisionEvents;\n","import { defineKind, type Kind } from \"@nikovirtala/ledger\";\nimport {\n type DecisionView,\n formatDecisionNumber,\n parseDecisionNumber,\n project,\n} from \"./kind/project.js\";\nimport { type DecisionState, reduce } from \"./kind/reduce.js\";\nimport { type AdrOptions, invariants, relate } from \"./kind/rules.js\";\nimport { type DecisionEvents, type DecisionStatus, decisionEvents } from \"./kind/schemas.js\";\n\nexport type { DecisionView } from \"./kind/project.js\";\nexport { formatDecisionNumber, parseDecisionNumber } from \"./kind/project.js\";\nexport type { DecisionState } from \"./kind/reduce.js\";\nexport type { AdrOptions } from \"./kind/rules.js\";\nexport {\n DECISION_STATUSES,\n type DecisionEvents,\n type DecisionStatus,\n type LinkRel,\n REF,\n} from \"./kind/schemas.js\";\n\n/**\n * The decision kind (01:128–194), as imported by other tools through `@nikovirtala/adr/kind`.\n *\n * Decisions are proposed, then accepted (which freezes the body and earns a number on main),\n * rejected or withdrawn; accepted ones can be deprecated or superseded. This module imports only\n * `@nikovirtala/ledger` and `./kind/*`, so importing the kind never loads CLI or MCP code.\n */\nexport const adrKind: Kind<\n DecisionStatus,\n DecisionEvents,\n DecisionState,\n DecisionView,\n AdrOptions\n> = defineKind<DecisionStatus, DecisionEvents, DecisionState, DecisionView, AdrOptions>({\n name: \"decision\",\n schema: \"decision/v1\",\n states: [\"proposed\", \"accepted\", \"rejected\", \"withdrawn\", \"deprecated\", \"superseded\"],\n events: decisionEvents,\n initial: \"proposed\",\n reduce,\n invariants,\n relate,\n project,\n body: { freezeOn: \"accepted\" },\n layout: { bodies: \"decisions\", events: \"events\" },\n defaultRoot: \"docs/adr\",\n sourceName: \"adr\",\n numbering: { on: \"accepted\", format: formatDecisionNumber, parse: parseDecisionNumber },\n templates: {\n dir: \"templates\",\n nameField: \"template\",\n versionField: \"template_version\",\n lintIn: [\"proposed\"],\n lintOn: \"accepted\",\n requireSections: [\"scope\"],\n },\n scope: { event: \"scoped\", activeIn: [\"proposed\", \"accepted\", \"deprecated\"] },\n options: Object.freeze({ acceptRequiresHuman: true }),\n});\n"],"mappings":"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"}
|
package/dist/kind.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { n as DECISION_STATUSES, r as REF, t as adrKind, i as formatDecisionNumber, a as parseDecisionNumber } from "./kind-BcHjCHvi.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The `edit` callback of `proposeDecision`: `null` when the editor exited 0, else a note. */
|
|
2
|
+
export type Edit = (absPath: string) => Promise<string | null>;
|
|
3
|
+
/**
|
|
4
|
+
* The user's editor as an `edit` callback (design §5.7), or `null` when neither `VISUAL` nor
|
|
5
|
+
* `EDITOR` is set. The editor string is run by `sh -c` the way git runs `core.editor`, with the
|
|
6
|
+
* path as the positional parameter `$1`, never interpolated. When stdout isn't a TTY (piped
|
|
7
|
+
* `--json`, say), the editor's stdout goes to stderr so the CLI's stdout stays one document.
|
|
8
|
+
* The callback never throws: a spawn error or a non-zero exit becomes the note.
|
|
9
|
+
*/
|
|
10
|
+
export declare function editorFor(env: NodeJS.ProcessEnv, o: {
|
|
11
|
+
readonly stdoutIsTTY: boolean;
|
|
12
|
+
}): Edit | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CommandSpec } from "@nikovirtala/ledger/cli";
|
|
2
|
+
/**
|
|
3
|
+
* `adr new <title> …` (design §5.6). The editor runs with `--edit`, or without `--no-edit` when
|
|
4
|
+
* the terminal is interactive and an editor is set.
|
|
5
|
+
*/
|
|
6
|
+
export declare const newCommand: CommandSpec;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CliIO, GlobalFlags } from "@nikovirtala/ledger/cli";
|
|
2
|
+
import { type DecisionLog } from "../log.js";
|
|
3
|
+
import type { Display } from "./text.js";
|
|
4
|
+
/**
|
|
5
|
+
* The decision log a command works on: `--root` and `--ref` from the globals, git run with the
|
|
6
|
+
* CLI's environment.
|
|
7
|
+
*/
|
|
8
|
+
export declare function openForCli(io: CliIO, flags: GlobalFlags, o?: {
|
|
9
|
+
readonly reader?: "worktree" | "index";
|
|
10
|
+
}): Promise<DecisionLog>;
|
|
11
|
+
/**
|
|
12
|
+
* How other decisions display in a write's text, read again from the work tree, like the write
|
|
13
|
+
* result's own views: a commit on the main ref has numbered the decisions it accepted.
|
|
14
|
+
*/
|
|
15
|
+
export declare function displayAfterWrite(log: DecisionLog): Promise<{
|
|
16
|
+
readonly display: Display;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type CommandSpec } from "@nikovirtala/ledger/cli";
|
|
2
|
+
/**
|
|
3
|
+
* `adr scope <decision> --set|--clear|--add|--remove …` (design §6.8): the globs of the code a
|
|
4
|
+
* decision governs, written as one whole-set `scoped` event. `adr scope [<decision>]
|
|
5
|
+
* --follow-renames` rewrites them after staged renames and stages the events, never committing.
|
|
6
|
+
*/
|
|
7
|
+
export declare const scopeCommand: CommandSpec;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Actor } from "@nikovirtala/ledger";
|
|
2
|
+
import type { InitResultJson } from "../init.js";
|
|
3
|
+
import type { DecisionsForJson } from "../scope.js";
|
|
4
|
+
import type { DecisionListJson, DecisionShowJson } from "../views.js";
|
|
5
|
+
import type { ScopeOffer, WriteResultJson } from "../write.js";
|
|
6
|
+
/** How a decision id is shown: its display (`ADR-0007`), or the id when it has no number. */
|
|
7
|
+
export type Display = (id: string) => string;
|
|
8
|
+
/** `adr list`: one row per decision, `→ <effective>` when it differs from the decision. */
|
|
9
|
+
export declare function listText(doc: DecisionListJson, display: Display): string;
|
|
10
|
+
/**
|
|
11
|
+
* The stderr warning of `new` when the draft it wrote has unfilled sections (design §7.2), or
|
|
12
|
+
* `null` when there are none.
|
|
13
|
+
*/
|
|
14
|
+
export declare function unfilledWarning(doc: WriteResultJson): string | null;
|
|
15
|
+
export interface FollowRenamesBefore {
|
|
16
|
+
readonly before: ReadonlyMap<string, readonly string[]>;
|
|
17
|
+
readonly renamed: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The text of a write command (design §5.6, §6.1). `subject` names what a refused command was
|
|
21
|
+
* about; `number` is the decision's number in the write result, if it has one now; `reason` is
|
|
22
|
+
* the reason a lifecycle exit recorded.
|
|
23
|
+
*/
|
|
24
|
+
export declare function writeText(doc: WriteResultJson, display: Display, o: {
|
|
25
|
+
readonly subject: string;
|
|
26
|
+
readonly number: string | null;
|
|
27
|
+
readonly reason?: string | null;
|
|
28
|
+
/** The decision's globs before a `scope` write. */
|
|
29
|
+
readonly scopeBefore?: readonly string[];
|
|
30
|
+
/** For `scope --follow-renames`: every decision's globs before, and whether renames were staged. */
|
|
31
|
+
readonly followRenames?: FollowRenamesBefore;
|
|
32
|
+
/** The superseded scope an `accept` copied. */
|
|
33
|
+
readonly copied?: ScopeOffer | null;
|
|
34
|
+
/** Who proposed with `new`, and the log's `accept_requires_human`, for the next step. */
|
|
35
|
+
readonly actor?: Actor;
|
|
36
|
+
readonly acceptRequiresHuman?: boolean;
|
|
37
|
+
}): string;
|
|
38
|
+
/** `adr show`: the state header, the decision's fields and its body. */
|
|
39
|
+
export declare function showText(doc: DecisionShowJson, display: Display): string;
|
|
40
|
+
/**
|
|
41
|
+
* `adr for`: per path, one row per decision (named, status, title, how it matched), its warning
|
|
42
|
+
* below it when it isn't in force, then the anchors that name no decision of this log.
|
|
43
|
+
*/
|
|
44
|
+
export declare function forText(doc: DecisionsForJson, display: Display, keyword: string): string;
|
|
45
|
+
/** `adr init`: what was written and kept, then the next step; or that nothing was needed. */
|
|
46
|
+
export declare function initText(doc: InitResultJson): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CommitTemplate, type Severity, type ToolConfig } from "@nikovirtala/ledger";
|
|
2
|
+
/** The default `[commit] subject`. */
|
|
3
|
+
export declare const DEFAULT_COMMIT_SUBJECT = "docs(adr): {action} {title}";
|
|
4
|
+
/** adr's settings from `adr.toml` (design §5.3); every key is optional. */
|
|
5
|
+
export interface AdrConfig {
|
|
6
|
+
/** `[log] default_template`: the template `adr new` uses without `--template`. */
|
|
7
|
+
readonly defaultTemplate: string;
|
|
8
|
+
/** `[log] main_branch`: the ref numbers come from; `null` resolves it automatically. */
|
|
9
|
+
readonly mainBranch: string | null;
|
|
10
|
+
/** `[log] index`: validated now, used in phase 2. */
|
|
11
|
+
readonly index: "committed" | "none";
|
|
12
|
+
/** `[policy] accept_requires_human`. */
|
|
13
|
+
readonly acceptRequiresHuman: boolean;
|
|
14
|
+
/** `[policy] auto_commit`: commit writes without `--commit`. */
|
|
15
|
+
readonly autoCommit: boolean;
|
|
16
|
+
/** `[policy] mcp_can_decide`: validated now, used in phase 3. */
|
|
17
|
+
readonly mcpCanDecide: boolean;
|
|
18
|
+
/** `[verify] fail_on`. */
|
|
19
|
+
readonly failOn: Severity;
|
|
20
|
+
/** `[commit] subject`: the template of `--commit` subjects. */
|
|
21
|
+
readonly commitSubject: CommitTemplate;
|
|
22
|
+
/** `[scope] anchor_keyword`: the code-anchor keyword, `adr` in `// adr:<id>`. */
|
|
23
|
+
readonly anchorKeyword: string;
|
|
24
|
+
/** `[scope] anchor_exclude`: globs of files whose anchors are ignored (docs that show examples). */
|
|
25
|
+
readonly anchorExclude: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export declare const DEFAULT_ADR_CONFIG: AdrConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Validates `adr.toml` (ledger's parsed `ToolConfig`; `null` when there is none). A wrong type or
|
|
30
|
+
* value throws `ConfigError` naming the file and key. Unknown keys are ignored, so newer files
|
|
31
|
+
* still load. `[log] root` is ledger's (`openToolLog` already checked it).
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseAdrConfig(config: ToolConfig | null, file: string | null): AdrConfig;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { type AdrConfig, DEFAULT_ADR_CONFIG, parseAdrConfig } from "./config.js";
|
|
2
|
+
export { type InitResultJson, initDecisionLog } from "./init.js";
|
|
3
|
+
export { type AdrOptions, adrKind, DECISION_STATUSES, type DecisionEvents, type DecisionState, type DecisionStatus, type DecisionView, formatDecisionNumber, type LinkRel, parseDecisionNumber, REF, } from "./kind.js";
|
|
4
|
+
export { type DecisionLog, type DecisionsRead, openDecisionLog, readDecisions, readDecisionsFrom, resolveDecision, } from "./log.js";
|
|
5
|
+
export { type DecisionForJson, type DecisionsForJson, decisionsFor, type UnresolvedAnchorJson, } from "./scope.js";
|
|
6
|
+
export { adrChecks, verifyDecisions } from "./verify.js";
|
|
7
|
+
export { type DecisionListJson, type DecisionShowJson, listDecisions, type NumberingJson, showDecision, stateHeader, } from "./views.js";
|
|
8
|
+
export { acceptDecision, type CopyScope, deprecateDecision, followScopeRenames, type LinkInput, linkDecision, noteDecision, type ProposeInput, proposeDecision, type ReasonInput, rejectDecision, type ScopeInput, type ScopeOffer, scopeDecision, scopeToCopy, supersedeDecision, type WriteOptions, type WriteResultJson, withdrawDecision, } from "./write.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DecisionLog } from "./log.js";
|
|
2
|
+
/** `adr init --json` (design §6.2). */
|
|
3
|
+
export interface InitResultJson {
|
|
4
|
+
readonly format: "adr-init/v1";
|
|
5
|
+
readonly root: string;
|
|
6
|
+
/** The config file of the log after init, repo-relative; `null` if there is none. */
|
|
7
|
+
readonly config: string | null;
|
|
8
|
+
/** Repo-relative paths written, in write order. */
|
|
9
|
+
readonly written: readonly string[];
|
|
10
|
+
/** Files that were already there and were left as they are. */
|
|
11
|
+
readonly kept: readonly string[];
|
|
12
|
+
readonly commit: string | null;
|
|
13
|
+
readonly notes: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `adr init` (design §6.2): writes a config for the log, unless one is found, and each built-in
|
|
17
|
+
* template in `o.templates` (default `nygard`) to `<root>/templates/`, never overwriting a file.
|
|
18
|
+
* A `--root` other than `docs/adr` gets a repo-level `adr.toml` naming it, so later commands find
|
|
19
|
+
* the log without `--root`; when that file exists for another log, the config goes in the root.
|
|
20
|
+
* With `commit` (default `[policy] auto_commit`) the written files are committed on their own;
|
|
21
|
+
* nothing written means nothing committed. No events are written.
|
|
22
|
+
*/
|
|
23
|
+
export declare function initDecisionLog(log: DecisionLog, o?: {
|
|
24
|
+
readonly templates?: readonly string[];
|
|
25
|
+
readonly commit?: boolean;
|
|
26
|
+
readonly message?: string | null;
|
|
27
|
+
}): Promise<InitResultJson>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FoldedSubject, ProjectContext } from "@nikovirtala/ledger";
|
|
2
|
+
import type { DecisionState } from "./reduce.js";
|
|
3
|
+
import type { DecisionEvents, DecisionStatus } from "./schemas.js";
|
|
4
|
+
/** One decision as the CLI, the library and MCP show it (01:173–192, plus the added fields). */
|
|
5
|
+
export interface DecisionView {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly number: number | null;
|
|
8
|
+
/** `ADR-0007`, or the id when unnumbered. */
|
|
9
|
+
readonly display: string;
|
|
10
|
+
readonly title: string;
|
|
11
|
+
readonly status: DecisionStatus;
|
|
12
|
+
readonly superseded_by: string | null;
|
|
13
|
+
/** Ids whose applied `superseded.by` is this id, sorted. */
|
|
14
|
+
readonly supersedes: readonly string[];
|
|
15
|
+
/** The proposal's intent (01:164). */
|
|
16
|
+
readonly declared_supersedes: readonly string[];
|
|
17
|
+
/** Head of the `superseded_by` chain; `null` only on a cycle. */
|
|
18
|
+
readonly effective: string | null;
|
|
19
|
+
/** `[id, …, effective]`, following `superseded_by` forward. */
|
|
20
|
+
readonly chain: readonly string[];
|
|
21
|
+
readonly template: {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly version: number;
|
|
24
|
+
};
|
|
25
|
+
readonly body_path: string | null;
|
|
26
|
+
/** Recorded at acceptance. */
|
|
27
|
+
readonly body_sha256: string | null;
|
|
28
|
+
/** `null` until accepted; whether the current body hash equals the recorded one. */
|
|
29
|
+
readonly body_intact: boolean | null;
|
|
30
|
+
readonly links: readonly {
|
|
31
|
+
readonly to: string;
|
|
32
|
+
readonly rel: string;
|
|
33
|
+
}[];
|
|
34
|
+
readonly applies_to: readonly string[];
|
|
35
|
+
readonly notes: number;
|
|
36
|
+
readonly proposed_at: string;
|
|
37
|
+
readonly decided_at: string | null;
|
|
38
|
+
readonly status_at: string;
|
|
39
|
+
/** `proposed 2026-03-12`: state-changing events, in fold order. */
|
|
40
|
+
readonly history: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
type Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;
|
|
43
|
+
type Context = ProjectContext<DecisionStatus, DecisionState, DecisionEvents>;
|
|
44
|
+
/** `ADR-0007`: at least 4 digits, growing past 9999. */
|
|
45
|
+
export declare function formatDecisionNumber(n: number): string;
|
|
46
|
+
/** `7`, `0007`, `ADR-7`, `adr-0007` → 7; `null` for anything else. */
|
|
47
|
+
export declare function parseDecisionNumber(s: string): number | null;
|
|
48
|
+
/** Pure: the view of one folded decision. */
|
|
49
|
+
export declare function project(subject: Subject, ctx: Context): DecisionView;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Actor, FoldEvent } from "@nikovirtala/ledger";
|
|
2
|
+
import type { DecisionEvents } from "./schemas.js";
|
|
3
|
+
/** A decision's own state, maintained by `reduce` over its applied events. */
|
|
4
|
+
export interface DecisionState {
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly template: {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly version: number;
|
|
9
|
+
};
|
|
10
|
+
readonly declaredSupersedes: readonly string[];
|
|
11
|
+
/** From the applied `accepted` event. */
|
|
12
|
+
readonly bodySha256: string | null;
|
|
13
|
+
readonly acceptedBy: Actor | null;
|
|
14
|
+
readonly supersededBy: string | null;
|
|
15
|
+
/** In fold order, duplicates kept once. */
|
|
16
|
+
readonly links: readonly {
|
|
17
|
+
readonly to: string;
|
|
18
|
+
readonly rel: string;
|
|
19
|
+
}[];
|
|
20
|
+
/** The latest `scoped` set (latest wins). */
|
|
21
|
+
readonly appliesTo: readonly string[];
|
|
22
|
+
readonly notes: number;
|
|
23
|
+
/** State-changing events only, in fold order. */
|
|
24
|
+
readonly history: readonly {
|
|
25
|
+
readonly type: string;
|
|
26
|
+
readonly at: string;
|
|
27
|
+
}[];
|
|
28
|
+
readonly proposedAt: string;
|
|
29
|
+
/** `at` of `accepted`, `rejected` or `withdrawn`. */
|
|
30
|
+
readonly decidedAt: string | null;
|
|
31
|
+
/** `at` of the latest state-changing event. */
|
|
32
|
+
readonly statusAt: string;
|
|
33
|
+
}
|
|
34
|
+
/** Pure: the state after one applied event. The fold only passes events the machine allows. */
|
|
35
|
+
export declare function reduce(state: DecisionState | null, e: FoldEvent<DecisionEvents>): DecisionState;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FoldedLog, FoldedSubject, KindFinding } from "@nikovirtala/ledger";
|
|
2
|
+
import type { DecisionState } from "./reduce.js";
|
|
3
|
+
import type { DecisionEvents, DecisionStatus } from "./schemas.js";
|
|
4
|
+
/** Options of the decision kind's `invariants` and `relate`. */
|
|
5
|
+
export interface AdrOptions {
|
|
6
|
+
/** Default `true`: an agent may propose, but only a human may accept (01:98). */
|
|
7
|
+
readonly acceptRequiresHuman: boolean;
|
|
8
|
+
}
|
|
9
|
+
type Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;
|
|
10
|
+
type Log = FoldedLog<DecisionStatus, DecisionState, DecisionEvents>;
|
|
11
|
+
/** `decision/agent-accepted`: an applied `accepted` event written by an agent under the human policy. */
|
|
12
|
+
export declare function invariants(subject: Subject, options: AdrOptions): readonly KindFinding[];
|
|
13
|
+
/**
|
|
14
|
+
* Cross-subject rules: `decision/reference-missing` for a local `supersedes`, `superseded.by` or
|
|
15
|
+
* `linked.to` naming no subject in the log; `decision/superseded-by-unaccepted` when the target of
|
|
16
|
+
* an applied `superseded.by` has no applied `accepted` event in this tree; and
|
|
17
|
+
* `decision/supersession-cycle`, once per cycle on its smallest id.
|
|
18
|
+
*/
|
|
19
|
+
export declare function relate(log: Log): readonly KindFinding[];
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** A decision's computed status. */
|
|
2
|
+
export type DecisionStatus = "proposed" | "accepted" | "rejected" | "withdrawn" | "deprecated" | "superseded";
|
|
3
|
+
/** Every status, in lifecycle order. */
|
|
4
|
+
export declare const DECISION_STATUSES: readonly DecisionStatus[];
|
|
5
|
+
/** A local subject id, or a qualified `<source>:<id>` one. */
|
|
6
|
+
export declare const REF = "^(?:[a-z][a-z0-9-]*:)?[0-9a-hjkmnp-tv-z]{8}$";
|
|
7
|
+
/** How a `linked` decision relates to its target. */
|
|
8
|
+
export type LinkRel = "relates-to" | "refines" | "depends-on";
|
|
9
|
+
/** The decision kind's event types (01:128–136): data schemas, allowed states and targets. */
|
|
10
|
+
export declare const decisionEvents: {
|
|
11
|
+
readonly proposed: import("@nikovirtala/ledger").EventTypeDef<"proposed", {
|
|
12
|
+
title: string;
|
|
13
|
+
template: string;
|
|
14
|
+
template_version: number;
|
|
15
|
+
supersedes?: string[];
|
|
16
|
+
}>;
|
|
17
|
+
readonly accepted: import("@nikovirtala/ledger").EventTypeDef<"accepted" | "proposed", {
|
|
18
|
+
body_sha256: string;
|
|
19
|
+
}>;
|
|
20
|
+
readonly rejected: import("@nikovirtala/ledger").EventTypeDef<"proposed" | "rejected", Record<string, never>>;
|
|
21
|
+
readonly withdrawn: import("@nikovirtala/ledger").EventTypeDef<"proposed" | "withdrawn", Record<string, never>>;
|
|
22
|
+
readonly deprecated: import("@nikovirtala/ledger").EventTypeDef<"accepted" | "deprecated", Record<string, never>>;
|
|
23
|
+
readonly superseded: import("@nikovirtala/ledger").EventTypeDef<"accepted" | "deprecated" | "superseded", {
|
|
24
|
+
by: string;
|
|
25
|
+
}>;
|
|
26
|
+
readonly noted: import("@nikovirtala/ledger").EventTypeDef<never, {
|
|
27
|
+
text: string;
|
|
28
|
+
}>;
|
|
29
|
+
readonly linked: import("@nikovirtala/ledger").EventTypeDef<never, {
|
|
30
|
+
to: string;
|
|
31
|
+
rel: LinkRel;
|
|
32
|
+
}>;
|
|
33
|
+
readonly scoped: import("@nikovirtala/ledger").EventTypeDef<"accepted" | "deprecated" | "proposed", {
|
|
34
|
+
set: string[];
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
export type DecisionEvents = typeof decisionEvents;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Kind } from "@nikovirtala/ledger";
|
|
2
|
+
import { type DecisionView } from "./kind/project.js";
|
|
3
|
+
import { type DecisionState } from "./kind/reduce.js";
|
|
4
|
+
import { type AdrOptions } from "./kind/rules.js";
|
|
5
|
+
import { type DecisionEvents, type DecisionStatus } from "./kind/schemas.js";
|
|
6
|
+
export type { DecisionView } from "./kind/project.js";
|
|
7
|
+
export { formatDecisionNumber, parseDecisionNumber } from "./kind/project.js";
|
|
8
|
+
export type { DecisionState } from "./kind/reduce.js";
|
|
9
|
+
export type { AdrOptions } from "./kind/rules.js";
|
|
10
|
+
export { DECISION_STATUSES, type DecisionEvents, type DecisionStatus, type LinkRel, REF, } from "./kind/schemas.js";
|
|
11
|
+
/**
|
|
12
|
+
* The decision kind (01:128–194), as imported by other tools through `@nikovirtala/adr/kind`.
|
|
13
|
+
*
|
|
14
|
+
* Decisions are proposed, then accepted (which freezes the body and earns a number on main),
|
|
15
|
+
* rejected or withdrawn; accepted ones can be deprecated or superseded. This module imports only
|
|
16
|
+
* `@nikovirtala/ledger` and `./kind/*`, so importing the kind never loads CLI or MCP code.
|
|
17
|
+
*/
|
|
18
|
+
export declare const adrKind: Kind<DecisionStatus, DecisionEvents, DecisionState, DecisionView, AdrOptions>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Finding, type FoldedLogOf, type GitOptions, type LogLayout, type Numbering, type Projection, type ToolLog, type TreeReader } from "@nikovirtala/ledger";
|
|
2
|
+
import { type AdrConfig } from "./config.js";
|
|
3
|
+
import { adrKind, type DecisionView } from "./kind.js";
|
|
4
|
+
/** A decision log opened for the CLI or MCP: ledger's tool log plus adr's config. */
|
|
5
|
+
export interface DecisionLog extends ToolLog {
|
|
6
|
+
readonly adr: AdrConfig;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Opens the decision log (design D3): `--root`, else `adr.toml`'s `[log] root`, else `docs/adr`;
|
|
10
|
+
* read from the `ref` tree, the index or the work tree.
|
|
11
|
+
*/
|
|
12
|
+
export declare function openDecisionLog(o: {
|
|
13
|
+
readonly cwd: string;
|
|
14
|
+
readonly root?: string | null;
|
|
15
|
+
readonly ref?: string | null;
|
|
16
|
+
readonly reader?: "worktree" | "index";
|
|
17
|
+
readonly gitOptions?: GitOptions;
|
|
18
|
+
}): Promise<DecisionLog>;
|
|
19
|
+
/** One read of a decision log: everything list, show and the write plans need. */
|
|
20
|
+
export interface DecisionsRead {
|
|
21
|
+
/** The tree that was read, its layout and config; no `Git`, so plans can run on a MemoryTree. */
|
|
22
|
+
readonly tree: TreeReader;
|
|
23
|
+
readonly layout: LogLayout;
|
|
24
|
+
readonly adr: AdrConfig;
|
|
25
|
+
readonly root: string;
|
|
26
|
+
readonly ref: string | null;
|
|
27
|
+
readonly commit: string | null;
|
|
28
|
+
readonly folded: FoldedLogOf<typeof adrKind>;
|
|
29
|
+
readonly projection: Projection<DecisionView>;
|
|
30
|
+
readonly numbering: Numbering;
|
|
31
|
+
/** Load, transition, invariant and relate findings; no diff-based checks. */
|
|
32
|
+
readonly findings: readonly Finding[];
|
|
33
|
+
/** How the log was located (`ToolLog.notes`); `[]` for a MemoryTree read. */
|
|
34
|
+
readonly notes: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
/** `numberLog` on the repository, then `readDecisionsFrom(log.tree, …)`. */
|
|
37
|
+
export declare function readDecisions(log: DecisionLog): Promise<DecisionsRead>;
|
|
38
|
+
/** The git-free core of `readDecisions`; tests pass a MemoryTree and a hand-made `Numbering`. */
|
|
39
|
+
export declare function readDecisionsFrom(tree: TreeReader, o: {
|
|
40
|
+
readonly layout: LogLayout;
|
|
41
|
+
readonly adr: AdrConfig;
|
|
42
|
+
readonly numbering: Numbering;
|
|
43
|
+
readonly ref?: string | null;
|
|
44
|
+
readonly commit?: string | null;
|
|
45
|
+
readonly notes?: readonly string[];
|
|
46
|
+
}): Promise<DecisionsRead>;
|
|
47
|
+
/** Why numbers are unavailable, in words: `shallow clone` or `no main branch`. */
|
|
48
|
+
export declare function unavailableReason(n: Numbering): string;
|
|
49
|
+
/**
|
|
50
|
+
* The id a decision argument names: an id, or a number in any form `parseDecisionNumber` takes.
|
|
51
|
+
* Throws `UsageError` (exit 2) naming the reason when it names nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveDecision(read: DecisionsRead, input: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Finding, type ScopeVia } from "@nikovirtala/ledger";
|
|
2
|
+
import type { DecisionStatus } from "./kind.js";
|
|
3
|
+
import type { DecisionLog, DecisionsRead } from "./log.js";
|
|
4
|
+
import { type NumberingJson } from "./views.js";
|
|
5
|
+
/** One decision that governs a path in `adr-for/v1` (design §6.9). */
|
|
6
|
+
export interface DecisionForJson {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly display: string;
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly status: DecisionStatus;
|
|
11
|
+
readonly effective: string | null;
|
|
12
|
+
/** What to know before applying it: superseded, deprecated, never in force; `null` if nothing. */
|
|
13
|
+
readonly warning: string | null;
|
|
14
|
+
/** The globs that matched, then the anchor lines. */
|
|
15
|
+
readonly via: readonly ScopeVia[];
|
|
16
|
+
}
|
|
17
|
+
/** An anchor naming no decision of this log. */
|
|
18
|
+
export interface UnresolvedAnchorJson {
|
|
19
|
+
/** The id, or `<source>:<id>` for a qualified anchor. */
|
|
20
|
+
readonly ref: string;
|
|
21
|
+
readonly line: number;
|
|
22
|
+
readonly reason: "not-found" | "other-log";
|
|
23
|
+
}
|
|
24
|
+
/** `adr for <path>… --json` (design §6.9). */
|
|
25
|
+
export interface DecisionsForJson {
|
|
26
|
+
readonly format: "adr-for/v1";
|
|
27
|
+
readonly root: string;
|
|
28
|
+
readonly ref: string | null;
|
|
29
|
+
readonly commit: string | null;
|
|
30
|
+
readonly numbering: NumberingJson;
|
|
31
|
+
readonly paths: readonly {
|
|
32
|
+
readonly path: string;
|
|
33
|
+
/** In the index, or in the `--ref` tree. */
|
|
34
|
+
readonly tracked: boolean;
|
|
35
|
+
readonly decisions: readonly DecisionForJson[];
|
|
36
|
+
readonly unresolved: readonly UnresolvedAnchorJson[];
|
|
37
|
+
}[];
|
|
38
|
+
/** The read's findings of the listed decisions and of the log as a whole. */
|
|
39
|
+
readonly findings: readonly Finding[];
|
|
40
|
+
readonly notes: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Which decisions govern each path (design §6.9): globs of decisions that were ever in force, and
|
|
44
|
+
* comment anchors of any decision, read from the work tree or, with a `--ref` log, that commit.
|
|
45
|
+
* Paths are relative to `o.cwd` (default the repository root); directories and paths outside the
|
|
46
|
+
* repository are `UsageError`s. Paths need not be tracked.
|
|
47
|
+
*/
|
|
48
|
+
export declare function decisionsFor(read: DecisionsRead, log: DecisionLog, o: {
|
|
49
|
+
readonly paths: readonly string[];
|
|
50
|
+
readonly cwd?: string;
|
|
51
|
+
}): Promise<DecisionsForJson>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The built-in decision templates, version 1 (design §5.7). `adr new` copies the one it uses into
|
|
3
|
+
* `<root>/templates/<name>.md` on first use, so the team owns it and its versions live in git
|
|
4
|
+
* (D4). Every section is required unless marked optional, and each has a required Scope section
|
|
5
|
+
* (01:311).
|
|
6
|
+
*/
|
|
7
|
+
/** The built-in templates by name: the file text `adr new` materializes. */
|
|
8
|
+
export declare const BUILTIN_TEMPLATES: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Check, type Finding, type VerifyMode } from "@nikovirtala/ledger";
|
|
2
|
+
import type { DecisionLog } from "./log.js";
|
|
3
|
+
/**
|
|
4
|
+
* Every phase-1 check for the log: ledger's mechanical and fold checks with adr's actor policy,
|
|
5
|
+
* and template lint against the templates in the head tree and, for older versions, the history
|
|
6
|
+
* of `historyRef`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function adrChecks(log: DecisionLog, historyRef: string | null): readonly Check[];
|
|
9
|
+
/** Verifies the decision log in `mode` (staged, range or full) with `adrChecks`. */
|
|
10
|
+
export declare function verifyDecisions(log: DecisionLog, mode: VerifyMode): Promise<Finding[]>;
|