madr-lint 0.2.0 → 0.4.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 +51 -0
- package/README.md +4 -2
- package/dist/cli.js +1534 -627
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +189 -5
- package/dist/index.js +460 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#87](https://github.com/knktkc/madr-lint/pull/87) [`7795ca7`](https://github.com/knktkc/madr-lint/commit/7795ca7c40634097806ad2bd7f82d4a3a02e465d) Thanks [@knktkc](https://github.com/knktkc)! - Autofix framework — `--fix`, `meta.fixable`, and the fixer API ([#28](https://github.com/knktkc/madr-lint/issues/28), [ADR-0008](https://github.com/knktkc/madr-lint/blob/main/docs/adr/0008-autofix-text-edits.md)). Fixes are raw-text offset edits, never AST serialization, so a fix changes only the span it targets and leaves every other byte untouched.
|
|
8
|
+
|
|
9
|
+
- **Rule API**: a rule declares `meta.fixable: 'code'` and attaches a lazy `fix: (fixer) => TextEdit | TextEdit[] | null` to `context.report(...)`. The `Fixer` (`replaceRange` / `insertAt` / `remove`) takes body (mdast) offsets and translates them to whole-file offsets past stripped frontmatter, so fixes are correct on files with YAML frontmatter. A fix from a rule that did not declare `meta.fixable` is a rule bug — the runner routes it through `core/internal-error`. Fix thunks are lazy: a normal lint constructs no fixer and invokes no thunk.
|
|
10
|
+
- **CLI**: `--fix` applies fixes in place (only files that change are written) and the exit code reflects the problems that **remain**; `--fix-dry-run` prints a per-file unified diff and writes nothing (exit code as if fixes were applied). Fixes compose with existing flags — `--quiet` / `--max-warnings` operate on the remaining diagnostics, and **suppressed** (`madr-lint-disable`) and **baselined** problems are never rewritten. `--update-baseline` combined with `--fix` / `--fix-dry-run` is a usage error (exit 2).
|
|
11
|
+
- **Applier**: collects fixes from reported diagnostics only (after suppression and baseline), sorts by offset, drops overlaps (first-by-position wins), applies in one pass, then re-lints to a fixpoint (max 10 passes). Per-file scope — cross-file fixes are tracked separately.
|
|
12
|
+
- **First real fix**: `madr/status-enum` now mechanically normalizes a v2 list-sourced status value that differs from an allowed value purely by case (e.g. `Accepted` → `accepted`). Frontmatter-sourced values and genuine typos get no fix.
|
|
13
|
+
- **Reporters**: `text` marks fixable diagnostics with a dim `🔧 fixable` tag; `json` gains a `fixable` boolean per result and a `summary.fixed` count when a fix pass ran; `github` / `sarif` are unchanged.
|
|
14
|
+
- **Programmatic API**: exports `applyEdits`, `makeFixer`, `collectFixes`, `fixFileContent`, `unifiedDiff`, `MAX_FIX_PASSES`, `frontmatterOffset`, and the `TextEdit` / `Fixer` / `FixFn` types.
|
|
15
|
+
- **Parser**: `ParsedFile.metadataValueLoc` (exposed to rules as `context.metadataValueLoc`) gives the body-coordinate `{ start, end }` offset range of a `metadata` value, for values sourced from the v2 leading list as a single contiguous text token — the offset source a fix's `fixer.replaceRange` targets.
|
|
16
|
+
|
|
17
|
+
**Breaking (type + cache):** `Diagnostic` gains a required `fixable: boolean` field (disclosed like the earlier `suggestion` / `docsUrl` additions), and the content-hash cache schema version is bumped to `3` (missing/mismatched ⇒ cold), so the first run after upgrading re-lints once instead of serving stale-shape cached diagnostics. Consumers constructing `Diagnostic` objects by hand must set `fixable`.
|
|
18
|
+
|
|
19
|
+
- [#88](https://github.com/knktkc/madr-lint/pull/88) [`32e1c04`](https://github.com/knktkc/madr-lint/commit/32e1c0475b9547236b32c94154ad023270d8b0e6) Thanks [@knktkc](https://github.com/knktkc)! - Make three rules fixable, including the first **cross-file** fix ([#29](https://github.com/knktkc/madr-lint/issues/29)). Building on the autofix framework ([#28](https://github.com/knktkc/madr-lint/issues/28)), fixes remain raw-text offset edits — only the targeted span changes.
|
|
20
|
+
|
|
21
|
+
- **`madr/date-iso8601`** now normalizes an **unambiguous** v2 body-list date to `YYYY-MM-DD`: year-first numeric with a single separator (`2026/7/3`, `2026.7.3`, `2026-7-3`) and English named-month forms (`3 Jul 2026`, `July 3, 2026`). Ambiguous day/month order (`03/07/2026`), two-digit years, impossible calendar dates (`2026/2/30`), non-English month names, and any **frontmatter**-sourced value are reported but never rewritten.
|
|
22
|
+
- **`madr/status-enum`** extends its case-only fix with a tiny synonym/misspelling map validated against the **configured** enum: curated typos (`superceded by …` → `superseded by …`, `depricated` → `deprecated`) fixed under any case setting, and prefix corrections that preserve the tail. Case-only corrections (`Accepted` → `accepted`, `Superseded By ADR-0042` → `superseded by ADR-0042`) apply under `caseSensitive: true` — with the default `caseSensitive: false` such values are valid and never flagged. A value that maps to two candidates, a synonym whose target is not configured, a genuine typo with no unique target, and frontmatter-sourced values get no fix.
|
|
23
|
+
- **`madr/supersedes-bidirectional`** gains the first **cross-file** fix: a `missingBackReference` inserts the reciprocal `<direction>: <expected>` line into the target ADR's **existing** YAML frontmatter, immediately before the closing `---`. The frontmatter is treated as opaque lines (no YAML reparse/reserialize), so key order, comments, and the file's newline style (LF/CRLF) are preserved. It declines when the target has no frontmatter (a block is never created), when the key already exists (value rewrite/append is out of scope), and for `unknownReference` (contextual). When two sources need a back-reference in the same target, one insertion lands per pass and the runner-up is reported.
|
|
24
|
+
- **Framework**: `context.report({ fix })` on a **project** rule is now honored (`meta.fixable: 'code'` gates it; a fix from a non-fixable project rule routes through `core/internal-error`, matching per-file behavior). `lintAndFix` runs a project-fix fixpoint on the fixed contents — collecting edits keyed by the target's `path`, applying them per file (the ADR-0008 per-file edit-set seam), and re-running the project pass to a fixpoint under the same 10-pass bound; suppressed and baselined problems are never rewritten.
|
|
25
|
+
- **Programmatic API**: exports `collectProjectFixes` and `applyEditsCounted` from `src/core/fix.ts`.
|
|
26
|
+
|
|
27
|
+
No breaking changes: `Diagnostic.fixable` was already required ([#28](https://github.com/knktkc/madr-lint/issues/28)); project-rule diagnostics that previously reported `fixable: false` now report `true` only when the rule attaches a fix.
|
|
28
|
+
|
|
29
|
+
- [#85](https://github.com/knktkc/madr-lint/pull/85) [`0e05d1c`](https://github.com/knktkc/madr-lint/commit/0e05d1cd338ae6adec99c066822c3c1edddac029) Thanks [@knktkc](https://github.com/knktkc)! - `madr-lint init` — scaffold a config file with detection heuristics ([#30](https://github.com/knktkc/madr-lint/issues/30)). Non-interactive by design (works in CI / piped stdin):
|
|
30
|
+
|
|
31
|
+
- **ADR directory**: first of `docs/adr`, `docs/decisions`, `doc/adr`, `adr`, `docs/architecture/decisions` whose top level contains at least one `NNNN-*.md` file; falls back to `docs/adr` (the linter's default) with a note when nothing qualifies.
|
|
32
|
+
- **MADR version**: samples up to 20 existing ADRs and lets the majority win — frontmatter with `decision-makers` counts as v4, other frontmatter as v3, a v2 metadata list as v2; empty/tie/no-metadata yields `auto` (omitted from the config, it is the default).
|
|
33
|
+
- **Config format**: `madr-lint.config.ts` when the project looks TypeScript-ish (`tsconfig.json`, or `typescript` among `package.json` dependencies), `.madrlintrc.json` otherwise.
|
|
34
|
+
- Refuses to overwrite an existing config file (exit 2); `--force` overwrites, `--dir <path>` overrides directory detection, `--json` emits a machine-readable summary of what was detected and written.
|
|
35
|
+
- The next-steps epilogue runs a cheap in-process lint of the detected directory and suggests `--update-baseline` when it finds violations, so legacy debt does not block adoption.
|
|
36
|
+
|
|
37
|
+
The plain `madr-lint [paths]` command is unchanged — `init` is dispatched only when it is the literal first argument, so paths, flags and exit codes behave exactly as before.
|
|
38
|
+
|
|
39
|
+
## 0.3.0
|
|
40
|
+
|
|
41
|
+
### Minor Changes
|
|
42
|
+
|
|
43
|
+
- [#71](https://github.com/knktkc/madr-lint/pull/71) [`55e7b89`](https://github.com/knktkc/madr-lint/commit/55e7b89a5d1dc6c3109e04b02c082aadd713b701) Thanks [@knktkc](https://github.com/knktkc)! - Diagnostics now carry machine-actionable `suggestion` and `docsUrl` in the json/text/github output ([#67](https://github.com/knktkc/madr-lint/issues/67)). Error feedback is self-contained — expected vs actual plus a concrete fix and a docs link — so agents and humans repair violations without a separate doc lookup.
|
|
44
|
+
|
|
45
|
+
- **Rule meta** gains an optional declarative `suggestions` map (messageId → template), interpolated with the diagnostic data exactly like `messages`. Rules stay declarative — no imperative suggestion building. Declare an entry only where a mechanical fix exists; omit where the fix is contextual.
|
|
46
|
+
- **`Diagnostic`** gains `suggestion: string | null` (null when the rule defines none for that message) and `docsUrl: string` (the rule's `docs.url`; the repository for `core/internal-error`), both resolved by the runner at report time.
|
|
47
|
+
- **Reporters**: `text` renders an indented `→ <suggestion>` line under a diagnostic and prints the docs URL once per rule per file group (compact — never per diagnostic); `json` adds `suggestion` and `docsUrl` to every result (keys always present); `github` appends ` — <suggestion>` to the annotation message (escaped). `sarif` is unchanged — its `helpUri` already carries the docs URL.
|
|
48
|
+
- **All 8 rules audited**; suggestions filled where the remediation is mechanical (`required-sections`, `status-enum` missing status, `date-iso8601`, `filename-format`, `no-broken-links`, `no-duplicate-numbering`, `supersedes-bidirectional`) and omitted where contextual (`no-numbering-gap`; `status-enum` invalid status already lists the allowed values).
|
|
49
|
+
- **Fixed** `supersedes-bidirectional`'s `missingBackReference` message, which named the wrong declared field: it now renders the field the source file actually declares (`supersedes:` vs `superseded-by:`) and states the exact `field: value` back-reference to add.
|
|
50
|
+
- **Breaking (pre-1.0)**: `Diagnostic` gains two required fields — external code that **constructs** `Diagnostic` values must now supply `suggestion` (`string | null`) and `docsUrl` (`string`); consumers that only read diagnostics are unaffected (the keys are always present). The on-disk cache manifest also gains a schema version (`schemaVersion: 2`; missing/mismatched ⇒ cold), so the first run after upgrading re-lints once instead of serving stale-shape cached diagnostics.
|
|
51
|
+
|
|
52
|
+
Resolution is report-time only, so it stays zero-cost on clean files and free on the sub-microsecond hot path.
|
|
53
|
+
|
|
3
54
|
## 0.2.0
|
|
4
55
|
|
|
5
56
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
[Getting started](https://knktkc.github.io/madr-lint/guides/getting-started/) ·
|
|
16
16
|
[Rules](https://knktkc.github.io/madr-lint/rules/)
|
|
17
17
|
|
|
18
|
+
<sub>For AI agents: [llms.txt](https://knktkc.github.io/madr-lint/llms.txt) indexes the docs; [llms-full.txt](https://knktkc.github.io/madr-lint/llms-full.txt) has the full text in one fetch; the [AI agents guide](https://knktkc.github.io/madr-lint/guides/ai-agents/) covers the ready-made [`adopt-madr-lint`](https://github.com/knktkc/madr-lint/blob/main/skills/adopt-madr-lint/SKILL.md) / [`new-adr`](https://github.com/knktkc/madr-lint/blob/main/skills/new-adr/SKILL.md) skills.</sub>
|
|
19
|
+
|
|
18
20
|
</div>
|
|
19
21
|
|
|
20
22
|
A fast, configurable linter for [MADR](https://adr.github.io/madr/) (Markdown Architectural Decision Records). It validates the things a plain Markdown linter can't: required sections, a valid status, ISO-8601 dates, filename convention, and cross-file integrity like unique numbering and non-broken links — across MADR **v2, v3, and v4**.
|
|
@@ -122,8 +124,8 @@ jobs:
|
|
|
122
124
|
with: { path: docs/adr }
|
|
123
125
|
```
|
|
124
126
|
|
|
125
|
-
> The floating `v0` tag
|
|
126
|
-
>
|
|
127
|
+
> The floating `v0` tag tracks the latest v0.x release; for stricter
|
|
128
|
+
> reproducibility, pin an exact tag like `@v0.3.0`.
|
|
127
129
|
|
|
128
130
|
Or run it via `npx` in any CI provider:
|
|
129
131
|
|