okf-kit 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,118 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.0] - 2026-08-27
11
+
12
+ ### Added
13
+
14
+ - `citations-resolve`: a new backtick-delimited `` `path:#heading` `` citation
15
+ form (`.md` targets only) resolves to a whole Markdown section instead of
16
+ a line range, so it is immune to a line-number shift caused by an edit
17
+ anywhere above the cited section -- unlike even a heading-anchored
18
+ `path:N-M#anchor` citation, whose line range still drifts on every such
19
+ edit even though the anchor catches it landing in the wrong section. The
20
+ heading text must match exactly one level 1 or 2 heading in the target
21
+ (`heading-section-not-found` / `heading-section-ambiguous`), and the
22
+ resolved section must have at least one non-blank line before the next
23
+ heading of the same or shallower level (`heading-section-empty`). An
24
+ optional content anchor, `` `path:#heading#"text"` ``, must occur on
25
+ exactly one line inside the resolved section
26
+ (`heading-section-content-anchor-not-found` /
27
+ `-content-anchor-ambiguous`); a malformed attempt is reported rather than
28
+ silently dropped (`heading-section-malformed`). See the doc comment above
29
+ `HEADING_SECTION_CITATION_RE` in `src/rules/citations-resolve.ts` for the
30
+ full rationale, including why the grammar requires the `:#` colon-hash
31
+ rather than a bare `#`. Existing bundles that already use the older
32
+ `` `path#heading` `` shape in ordinary prose (a Markdown link's target
33
+ written in backticks) are unaffected: they no longer parse as a citation
34
+ at all, producing the same findings as before this form existed.
35
+ - `citations-resolve`: a new `--require-anchors` opt-in (and
36
+ `--require-anchors-allow <patterns...>` for exempting specific citedPath
37
+ globs/exact matches) adds four stricter, `warning`-severity checks, off
38
+ by default so an existing bundle's plain `check` findings are unaffected:
39
+ `anchor-required` (an in-repo full citation carries no `#anchor` at all,
40
+ except a reserved citing doc or an allowlisted target),
41
+ `anchor-not-on-last-line` (a string anchor was found in its cited range,
42
+ but not on the range's own last content line -- an anchor on the first
43
+ line survives a small insertion above the range, since the shifted
44
+ window still contains its original content just at a different offset;
45
+ the last content line falls out of the window on any insertion at all),
46
+ `anchor-not-unique-in-range` (a string anchor occurs on more than one
47
+ line of its cited range; a count of zero stays
48
+ `anchor-not-found-in-range` as before, unconditionally), and
49
+ `test-range-straddles-block` (a FULL citation's own range into a
50
+ `.test.`/`.spec.` target (`.ts`, `.js`, `.mjs`) contains a
51
+ `describe`/`it`/`test` block-head line, at the same or a shallower
52
+ indent than the range's own first line, on any line other than that
53
+ first line -- its own new rule, not a reuse of the existing
54
+ `test-range-start-not-head`/`test-range-end-not-closing` pair, which
55
+ stay short-form-only). See "Anchor strictness (opt-in,
56
+ `--require-anchors`)" in the README for the full rationale; verified
57
+ byte-identical findings on existing bundles before and after this
58
+ change, off by default and additive only.
59
+ - `citations-resolve`: four details of the `--require-anchors` checks
60
+ added above, worth knowing when adopting them:
61
+ `test-range-straddles-block` no longer flags a block-head line nested
62
+ strictly deeper than the range's own start line, so citing a whole
63
+ `describe` in full (including every `it(` nested inside it) is no
64
+ longer misreported as straddling into another block; only a sibling or
65
+ outer block-head line still counts. `anchor-not-on-last-line` now
66
+ anchors against a range's last CONTENT line rather than its literal
67
+ last line, so a range that (correctly) ends on bare closing boilerplate
68
+ (`});`, `]);`, `}),`, and similar) can anchor on the real content line
69
+ before it instead of being forced onto the boilerplate itself. Both of
70
+ the opt-in's per-atom checks (`test-range-straddles-block` and the
71
+ string-anchor checks) are now also exempt for a reserved citing doc
72
+ (`index.md`/`log.md`), matching `anchor-required`'s existing exemption
73
+ rather than only that one check having it; and a citation that both
74
+ straddles a block boundary and carries a missing/misplaced anchor now
75
+ gets both findings instead of the anchor problem silently vanishing
76
+ behind the straddle one.
77
+
78
+ ## [0.7.0] - 2026-08-26
79
+
80
+ ### Added
81
+
82
+ - `citations-resolve`: a new `anchor-malformed` notice fires when a `#`
83
+ immediately follows a full citation's range but the text after it does
84
+ not parse as either anchor form (unbalanced quotes, a backtick inside a
85
+ quoted anchor, or nothing at all after the `#`, e.g. `path:N-M#` at end
86
+ of a line). The citation is still checked exactly as an ordinary
87
+ anchorless citation would be (backward compatible, no new error); without
88
+ this, a typo in an anchor silently disabled the very drift check it was
89
+ written for. A valid anchor, or a `#` in prose not immediately following
90
+ a citation's range, produces nothing. Zero new findings on the
91
+ orchestrator-workflow bundle (still 35 findings / 13 warnings / 22
92
+ notices, 0 anchor findings).
93
+
94
+ ### Changed
95
+
96
+ - `citations-resolve`: an anchored full citation's finding label now
97
+ carries the anchor (`path:N-M#anchor`, or `path:N-M#"text"` for a string
98
+ anchor), and the `anchor-heading-does-not-enclose` /
99
+ `anchor-heading-not-found` messages now name the anchor text, matching
100
+ `anchor-heading-mismatch` and `anchor-not-found-in-range`, which already
101
+ did. Previously, two citations to the identical range with different
102
+ anchors were indistinguishable in the output.
103
+
104
+ ### Internal
105
+
106
+ - `citations-resolve`: the three independent fence-state scanners
107
+ (`computeFencedSpans` for the citing doc, `computeFencedLineIndices` for
108
+ the target, `isFenceOpeningLine`) now all derive from one shared
109
+ `scanFenceLines` state machine instead of each replaying the same
110
+ open/close fence logic on its own; behavior is unchanged (existing fence
111
+ tests, including the citing-side tilde-fence and fence-opening-exception
112
+ cases, stay green), plus two new target-side tests (a tilde fence and an
113
+ unterminated fence reaching end of file) added as a regression net for
114
+ the derivation itself.
115
+ - Review-round follow-up on the anchor heading form's known limitations
116
+ (substring rather than token-boundary matching, and running the heading
117
+ search against non-Markdown targets too): both revisited and kept as
118
+ documented known limitations rather than implemented, given this round's
119
+ deliberately small, non-speculative scope; see the README's "Known
120
+ limitations" note.
121
+
10
122
  ## [0.6.0] - 2026-08-25
11
123
 
12
124
  ### Added
package/README.md CHANGED
@@ -70,7 +70,7 @@ Every template doc except `benchmark-template.md` ships with `sources: [path/to/
70
70
  | `no-absolute-links` | warning | Link targets should not start with `/`. GitHub resolves a leading slash against the repository root, not the bundle root, so an absolute link 404s once the bundle is viewed outside its own repository. Use a same-directory relative link instead. |
71
71
  | `sources-shape` | error | Frontmatter `sources`, when present, must be a non-empty array of non-empty strings. With a repo root (explicit or auto-detected), each listed path (file or directory) must also exist under it. |
72
72
  | `sources-fresh` | warning / notice | For docs with a `sources` list and a repo root, flags a source path whose last git commit is newer than both the doc's `timestamp` and the doc file's own last commit. See "Staleness (sources-fresh)" below. |
73
- | `citations-resolve` | warning / notice | For docs with a repo root, flags a `` `path:N`/`path:N-M` `` citation (and its `` `:N` ``/`` -`M` ``/`` (`N`) `` continuations, and bare paragraph-bound short forms `:N-M`/`(N-M)`) whose target file is missing, whose range is inverted or exceeds the file, or whose start line is blank or (for a non-markdown target) only a closing brace. A full citation may also carry an optional `#anchor` (e.g. `` `CHANGELOG.md:50-144#0.24.0` ``), checked against the target's own structure/content instead of just its line numbers. A short-form citation's range into a test file is also checked for a describe/it block boundary. See "Citation resolution (citations-resolve)" below. |
73
+ | `citations-resolve` | warning / notice | For docs with a repo root, flags a `` `path:N`/`path:N-M` `` citation (and its `` `:N` ``/`` -`M` ``/`` (`N`) `` continuations, and bare paragraph-bound short forms `:N-M`/`(N-M)`) whose target file is missing, whose range is inverted or exceeds the file, or whose start line is blank or (for a non-markdown target) only a closing brace. A full citation may also carry an optional `#anchor` (e.g. `` `CHANGELOG.md:50-144#0.24.0` ``), checked against the target's own structure/content instead of just its line numbers. A backtick-delimited `` `path:#heading` `` citation (`.md` targets only) resolves to a whole Markdown section instead of a line range, immune to every line-number shift above it; see "Heading-section citations" below. A short-form citation's range into a test file is also checked for a describe/it block boundary. `--require-anchors` opts into four additional checks; see "Anchor strictness (opt-in, `--require-anchors`)" below. See "Citation resolution (citations-resolve)" below. |
74
74
 
75
75
  ## repo-root auto-detection
76
76
 
@@ -123,6 +123,17 @@ Known limitation: the doc-commit comparison suppresses staleness for every sourc
123
123
  | `anchor-heading-does-not-enclose` | A heading-anchored citation's anchor text matches its nearest enclosing heading, but the range runs past that heading's own section (a heading of the same or shallower level starts before the range ends). **Warning**. |
124
124
  | `anchor-heading-not-found` | A heading-anchored citation has no heading (level 1 or 2) anywhere before its start line to anchor against; use a string anchor (`#"..."`) instead against a target with no heading structure. **Warning**. |
125
125
  | `anchor-not-found-in-range` | A string-anchored citation's (see below) anchor text does not occur, verbatim, on any line of the cited range. **Warning**. |
126
+ | `anchor-malformed` | A `#` immediately follows a citation's range, but the text after it does not parse as either anchor form (unbalanced quotes, a backtick inside a quoted anchor, or nothing at all after the `#`). The citation is still checked as an ordinary anchorless citation; reported as a `notice` (never counted toward `--strict`) so a typo in an anchor does not silently turn off the very check it was written for. |
127
+ | `heading-section-not-found` | A heading-section citation's (see below) heading text does not match any level 1 or 2 heading in the target. **Warning**. |
128
+ | `heading-section-ambiguous` | A heading-section citation's heading text matches more than one level 1 or 2 heading in the target; reported rather than silently resolved to the first match. **Warning**. |
129
+ | `heading-section-empty` | A heading-section citation resolved to a single heading, but that heading's section has no non-blank content before the next heading of the same or shallower level. **Warning**. |
130
+ | `heading-section-content-anchor-not-found` | A heading-section citation's optional content anchor (`` `path:#heading#"text"` ``) does not occur on any line of the resolved section. **Warning**. |
131
+ | `heading-section-content-anchor-ambiguous` | A heading-section citation's content anchor occurs on more than one line of the resolved section; expected exactly one. **Warning**. |
132
+ | `heading-section-malformed` | A backtick-delimited `` `path:#...` `` opener does not parse as a well-formed heading-section citation (an unterminated or empty content-anchor quote, an unquoted third segment, or a non-`.md` target, including a non-lowercase `.MD` extension); reported as a `notice` (never counted toward `--strict`) so a typo does not silently vanish. |
133
+ | `anchor-required` | (opt-in, `--require-anchors`) An in-repo full citation carries no `#anchor` at all. **Warning**. |
134
+ | `anchor-not-on-last-line` | (opt-in, `--require-anchors`) A string-anchored citation's anchor text was found in the cited range, but not on the range's own last CONTENT line (see below). **Warning**. |
135
+ | `anchor-not-unique-in-range` | (opt-in, `--require-anchors`) A string-anchored citation's anchor text occurs on more than one line of the cited range. **Warning**. |
136
+ | `test-range-straddles-block` | (opt-in, `--require-anchors`) A full citation's own range into a `.test.`/`.spec.` target (`.ts`, `.js`, `.mjs`) contains a `describe`/`it`/`test` block-head line, at the same or a shallower indent than the range's own first line, on any line other than that first line. **Warning**. |
126
137
 
127
138
  **Anchored citations.** A full citation may carry an anchor directly after its range: `` `path:N-M#anchor` ``, e.g. `` `CHANGELOG.md:50-144#0.24.0` ``. This closes a gap the checks above cannot: a CHANGELOG.md that grows by inserting each new release at the top shifts every later entry's absolute line numbers, so a citation that lands 15 lines off in the *wrong* release section is exactly as green as before the shift -- none of `missing-file`/`inverted-range`/`range-exceeds-file`/`blank-start-line`/`closing-brace-start-line` can tell the difference. An anchor pins the citation to a piece of the target's own structure or content that a pure line-shift does not preserve. Two forms, told apart by the anchor text itself:
128
139
 
@@ -133,9 +144,11 @@ Anchors are checked only once the base checks above (blank-start-line, closing-b
133
144
 
134
145
  **Anchor syntax note.** Any `#` immediately following a citation's range is read as the start of an anchor, with no other signal required -- there is no way to write a bare range immediately followed by an unrelated `#` and have it ignored. This means a Markdown link like `` [note](path:1-2#x) `` or an editor-style fragment such as `path:1-2#L7` is interpreted as an anchored citation (`x` and `L7` respectively), whether that was intended or not. The recommended, unambiguous form is the one used throughout this doc: the whole citation, range and anchor together, inside a single pair of backticks, e.g. `` `path:1-2#L7` ``.
135
146
 
136
- **Known limitations.** The heading form's `heading.text.includes(anchor.text)` check is a plain substring match against the heading's raw text, not a token-boundary match: an anchor `0.1` matches a heading containing `[0.10.0]` (and, by the same construction, `[0.24.0]` "contains" `24.0`). This is a deliberate simplification, not implemented as token-boundary matching in this round; treat a heading-anchored citation as a strong drift signal, not a semantic guarantee, the same way the rest of this rule's checks are mechanical rather than semantic. Separately, the heading form runs `MD_HEADING_RE` (a bare `` `#{1,6}\s+...` `` match) against the target's raw lines regardless of the target's own file type, so a `# comment` line in a `.yml` or `.json` target is matched as if it were a Markdown heading; restricting the heading form to `.md` targets is left as a known limitation rather than implemented here.
147
+ **Known limitations.** The heading form's `heading.text.includes(anchor.text)` check is a plain substring match against the heading's raw text, not a token-boundary match: an anchor `0.1` matches a heading containing `[0.10.0]` (and, by the same construction, `[0.24.0]` "contains" `24.0`). This is a deliberate simplification, not implemented as token-boundary matching in this round; treat a heading-anchored citation as a strong drift signal, not a semantic guarantee, the same way the rest of this rule's checks are mechanical rather than semantic. Separately, the heading form runs `MD_HEADING_RE` (a bare `` `#{1,6}\s+...` `` match) against the target's raw lines regardless of the target's own file type, so a `# comment` line in a `.yml` or `.json` target is matched as if it were a Markdown heading; restricting the heading form to `.md` targets is left as a known limitation rather than implemented here. Both limitations stand as documented rather than implemented; both remain easy to tighten later without touching anchor syntax or backward compatibility.
137
148
 
138
- **Continuation citations.** Once a sentence states a full `path:N` citation, prose commonly repeats just the line (or range) for a later reference in the same sentence: `` `:N` ``/`` `:N-M` `` (bare colon-prefixed), `` -`M` ``/`` –`M` `` (hyphen- or en-dash-led, the tail of a split range like `` `path:N`-`M` ``), or `` (`N`) `` (parenthesized). Each resolves against the nearest preceding citation in the same doc that resolved to a real file; a continuation right after an unresolved, ambiguous, or out-of-scope citation is silently skipped, not misattributed to an earlier, unrelated file.
149
+ **Heading-section citations.** `` `path:#heading` `` resolves to a whole Markdown section instead of a line range, so an edit anywhere above the cited section (e.g. inserting a new release at the top of a CHANGELOG) changes nothing about the citation -- see the doc comment above `HEADING_SECTION_CITATION_RE` in `src/rules/citations-resolve.ts` for the full rationale, including why the grammar requires the `:#` colon-hash and is restricted to `.md` targets. Grammar: `` `path:#heading` `` or, with an optional content anchor, `` `path:#heading#"text"` `` (always the quoted form). The heading text must match exactly one level 1 or 2 heading in the target (same containment check and same level cap as the line-range anchor's heading form above); the resolved section must be non-empty; the content anchor, when given, must occur on exactly one line inside that section. A malformed attempt (an unterminated or empty content-anchor quote, an unquoted third segment, or a non-`.md` target) is reported rather than silently dropped -- see `heading-section-malformed` in the table above. An unbalanced bracket in the heading position (`` `path:#[unclosed` ``) is not malformed by this definition: it parses as a heading named `[unclosed` and is reported as `heading-section-not-found`, the same way the line-range anchor form treats it. A bare `` `path#heading` `` without the colon is never a citation (it is the shape of an ordinary Markdown link target written in prose). See the rule table above for the exact finding names.
150
+
151
+ **Continuation citations.** Once a sentence states a full `path:N` citation, prose commonly repeats just the line (or range) for a later reference in the same sentence: `` `:N` ``/`` `:N-M` `` (bare colon-prefixed), `` -`M` ``/`` –`M` `` (hyphen- or en-dash-led, the tail of a split range like `` `path:N`-`M` ``), or `` (`N`) `` (parenthesized). Each resolves against the nearest preceding citation in the same doc that resolved to a real file; a continuation right after an unresolved, ambiguous, or out-of-scope citation is silently skipped, not misattributed to an earlier, unrelated file. A heading-section citation (see above) carries no line number and is never a continuation's target: it never sets or clears which citation a later continuation resolves against.
139
152
 
140
153
  **Short-form citations.** Distinct from a continuation above: a *bare* (no backtick, no file name at all) colon-range `:N-M`, e.g. `:580-588` in running prose. A short-form citation binds to **the last full `path:N-M` citation named earlier in the same paragraph** -- not the nearest preceding citation anywhere in the doc (that is what a continuation does); a paragraph boundary is a blank (empty or whitespace-only) line. A short-form citation with no full citation earlier in its own paragraph is reported `short-form-unbound`, not silently skipped. Only a range is recognised, never a bare single number (`:5`): a bare number is too easily an unrelated enumeration marker (e.g. a numbered list item) to detect mechanically without a large false-positive cost. A resolved short-form citation gets every check a full citation gets, plus the test-file/Markdown block-boundary check above (`test-range-*` / `markdown-range-boundary-bracket-or-fence`).
141
154
 
@@ -155,6 +168,17 @@ The colon form does not have this problem, but still needs a gate: a candidate `
155
168
 
156
169
  Like `sources-fresh`, this rule requires a repo root (explicit `--repo-root` or auto-detected): without one, it emits a single bundle-level notice (`citation resolution skipped: not inside a git work tree`) rather than silently reporting nothing.
157
170
 
171
+ ### Anchor strictness (opt-in, `--require-anchors`)
172
+
173
+ Everything above is on by default. `--require-anchors` opts into four additional, stricter checks against `citations-resolve`, all `warning`-severity, off unless the flag is passed -- an existing bundle's findings under a plain `check` are byte-for-byte unaffected by this section existing. `--require-anchors` (and its sibling `--require-anchors-allow`) is a CLI flag only; okf-kit has no bundle-level config file today (no `.okf.yml`, no `okf-kit.config`, no frontmatter field), so there is no other way to turn this on. All four checks are also exempt for a citing doc that is a reserved file (`index.md`, `log.md`), the same carve-out `citations-resolve` already gives reserved docs for short-form matching elsewhere in this doc -- a reserved doc's citations still get the unconditional base checks (`missing-file`, `inverted-range`, `anchor-not-found-in-range`, and so on), just none of the four opt-in ones:
174
+
175
+ - **`anchor-required`.** An in-repo full citation (resolved to a real target -- an unresolved one already gets its own `missing-file`/`unresolved-ambiguous` finding, not this one) carrying no `#anchor` at all is flagged. Also exempt (beyond the reserved-doc carve-out above): a citedPath matching one of `--require-anchors-allow`'s patterns (repeatable/space-separated, exact string or a `*`/`?` glob against the citation's raw, as-written path text), for a doc category not meant to be anchor-checked, e.g. `--require-anchors-allow README.md INSTALL-AGENT.md`. A target cited under several different spellings in the same bundle (`init.ts` in one paragraph, `src/init.ts` in another) needs one allow pattern per spelling, or a `*basename` glob that covers all of them -- the match is against the citation's own raw text, not the path it resolves to. `--require-anchors-allow` is commander-variadic: its list stops at the next `-`-prefixed token, so flags placed after it parse normally, but it swallows the positional bundle path if placed before it (`check --require-anchors-allow README.md <bundleDir>` fails with a missing-argument error); pass it after the bundle path.
176
+ - **`anchor-not-on-last-line`.** A string-anchored citation (`` `path:N-M#"text"` ``) whose anchor text was found in the cited range, but not on the range's own last CONTENT line. A content line is any line whose trimmed text is not empty and not composed only of closing brackets/braces/parens plus an optional trailing `,`/`;` (`}`, `});`, `]);`, `}),`, and similar); a range ending on one or more such boilerplate lines (the common shape of a `});` that closes a whole cited block) resolves to the real content line before them instead. An anchor sitting on the range's first line survives a small insertion above the range (the shifted window still contains the anchor's original content, just at a different offset); anchoring on the last content line closes that, since the original last content line falls out of the shifted window on any insertion at all.
177
+ - **`anchor-not-unique-in-range`.** A string-anchored citation whose anchor text occurs on more than one line of the cited range -- ambiguous evidence for which occurrence is the one actually pinning the citation. A count of zero is unaffected (already `anchor-not-found-in-range`, unconditionally).
178
+ - **`test-range-straddles-block`.** A FULL citation's own range into a `.test.`/`.spec.` target (`.ts`, `.js`, `.mjs`) must not run into a sibling or outer `describe`/`it`/`test` block (including `.only`/`.skip`/`.each` variants): a block-head line at the same or a shallower indent than the range's own start line, found on any line of the range OTHER than that start line, means the citation straddled out into a sibling or outer block. A block-head line indented STRICTLY DEEPER than the start line is a nested block (e.g. every `it(` inside a `describe(` the range cites in full) and is not a straddle -- citing a whole block is expected to contain every head line nested inside it. The start line itself is never checked here -- it is either a legitimate block head (the range correctly starts a block) or legitimately inside a block's body (a deliberate partial citation), and both are fine. A range that leaves its block without a later block-head line inside it (ending on an outer block's closing line) is not detected by this line-based check. Deliberately scoped to test-file targets only, and to this same opt-in: a full citation into a Markdown target legitimately cites a couple of arbitrary lines all the time (the same reasoning that keeps the Markdown half of `test-range-start-not-head`/`test-range-end-not-closing` scoped to short-form citations only, see above), and turning this on unconditionally for every existing full citation would silently regress an already-green bundle for every consumer, not just one that opted in.
179
+
180
+ `--require-anchors` is off by default and, once on, surfaces a real backlog the first time it is run against an existing, unaudited bundle -- the same posture `blank-start-line` already documents for `citations-resolve` itself. `anchor-required` in particular is only useful once a bundle's citations have actually been anchored; running it against a bundle that predates anchoring will flag most of that bundle's full citations.
181
+
158
182
  ## Exit codes
159
183
 
160
184
  | Code | Meaning |
@@ -169,7 +193,7 @@ This is advisory: don't fail the build on warnings unless you pass `--strict`. U
169
193
 
170
194
  ```yaml
171
195
  - name: OKF bundle check
172
- run: npx okf-kit@0.6.0 check path/to/bundle
196
+ run: npx okf-kit@0.8.0 check path/to/bundle
173
197
  ```
174
198
 
175
199
  Pin the version: an unpinned `npx okf-kit` picks up new rules on their release day, which turns an unrelated PR red.
package/dist/cli.d.ts CHANGED
@@ -5,6 +5,20 @@ export { UsageError };
5
5
  export interface CheckOptions {
6
6
  repoRoot?: string;
7
7
  strict?: boolean;
8
+ /**
9
+ * Opt-in for `citations-resolve`'s stricter anchor checks
10
+ * (`anchor-required`, `anchor-not-on-last-line`,
11
+ * `anchor-not-unique-in-range`, and the test-file block-boundary check
12
+ * applied to a full citation's own range). See the "Anchor strictness
13
+ * (opt-in)" doc block in `src/rules/citations-resolve.ts`.
14
+ */
15
+ requireAnchors?: boolean;
16
+ /**
17
+ * Glob/exact-match patterns exempting a matching citedPath from
18
+ * `anchor-required` (see `RequireAnchorsOptions` in `src/types.ts`).
19
+ * Ignored when `requireAnchors` is not set.
20
+ */
21
+ requireAnchorsAllow?: string[];
8
22
  /** Test-only override for git access; production code shells out to the real `git` binary. */
9
23
  runGit?: RunGit;
10
24
  }
package/dist/cli.js CHANGED
@@ -25,6 +25,9 @@ export function runCheck(bundleDir, options = {}) {
25
25
  ? path.resolve(options.repoRoot)
26
26
  : detectRepoRoot(resolvedBundleDir, options.runGit);
27
27
  const ctx = loadBundle(resolvedBundleDir, repoRoot, options.runGit);
28
+ if (options.requireAnchors) {
29
+ ctx.requireAnchors = { allow: options.requireAnchorsAllow ?? [] };
30
+ }
28
31
  const findings = allRules.flatMap((rule) => rule.run(ctx));
29
32
  const summary = summarize(findings);
30
33
  const exitCode = summary.errors > 0 || (Boolean(options.strict) && summary.warnings > 0)
@@ -51,12 +54,18 @@ program
51
54
  "(auto-detected via `git rev-parse --show-toplevel` from the bundle dir when omitted)")
52
55
  .option("-j, --json", "Output findings as JSON")
53
56
  .option("-s, --strict", "Also fail (exit 1) when warnings are present")
57
+ .option("--require-anchors", "citations-resolve: also require every in-repo full citation to carry a #anchor, and check " +
58
+ "a string anchor lands uniquely on the last line of its range (opt-in, see README)")
59
+ .option("--require-anchors-allow <patterns...>", "citations-resolve: citedPath glob/exact patterns (e.g. README.md) exempt from " +
60
+ "--require-anchors' anchor-required check")
54
61
  .exitOverride()
55
62
  .action((bundleDir, opts) => {
56
63
  try {
57
64
  const result = runCheck(bundleDir, {
58
65
  repoRoot: opts.repoRoot,
59
66
  strict: opts.strict,
67
+ requireAnchors: opts.requireAnchors,
68
+ requireAnchorsAllow: opts.requireAnchorsAllow,
60
69
  });
61
70
  const output = opts.json
62
71
  ? renderJson(result.bundleDir, result.findings)
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}