madr-lint 0.1.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 +21 -0
- package/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1500 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +370 -0
- package/dist/index.js +975 -0
- package/dist/index.js.map +1 -0
- package/package.json +90 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/core/cache.ts","../src/core/config.ts","../src/configs/recommended.ts","../src/core/discover.ts","../src/core/ignore.ts","../src/core/lint.ts","../src/core/runner.ts","../src/core/parser.ts","../src/core/types.ts","../src/core/reporter.ts","../src/rules/index.ts","../src/rules/date-iso8601/schema.json","../src/rules/date-iso8601/index.ts","../src/rules/filename-format/index.ts","../src/core/regex-safety.ts","../src/rules/filename-format/schema.json","../src/rules/no-broken-links/index.ts","../src/rules/no-broken-links/schema.json","../src/rules/no-duplicate-numbering/index.ts","../src/rules/no-duplicate-numbering/schema.json","../src/rules/no-numbering-gap/index.ts","../src/rules/no-numbering-gap/schema.json","../src/rules/required-sections/index.ts","../src/rules/required-sections/schema.json","../src/rules/status-enum/schema.json","../src/rules/status-enum/index.ts","../src/rules/supersedes-bidirectional/index.ts","../src/rules/supersedes-bidirectional/schema.json"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { relative, resolve, sep } from 'node:path';\nimport { defineCommand, runMain } from 'citty';\nimport { computeConfigHash } from './core/cache.js';\nimport { loadConfig, resolveExtends, type ResolvedConfig } from './core/config.js';\nimport { findAdrFiles } from './core/discover.js';\nimport { shouldIgnore } from './core/ignore.js';\nimport { lintFiles, type CacheConfig } from './core/lint.js';\nimport { reporters, type ReporterFormat } from './core/reporter.js';\nimport { RuleOptionsError } from './core/runner.js';\nimport type { AnyRule } from './core/types.js';\nimport * as builtinRules from './rules/index.js';\n\nfunction toPosix(p: string): string {\n if (sep === '/') return p;\n return p.split(sep).join('/');\n}\n\nconst pkg = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf8'),\n) as { version: string };\n\nconst main = defineCommand({\n meta: {\n name: 'madr-lint',\n version: pkg.version,\n description: 'A linter for MADR (Markdown Architectural Decision Records)',\n },\n args: {\n paths: {\n type: 'positional',\n description:\n 'One or more paths (files or directories). Defaults to config.adrDir',\n required: false,\n },\n cache: {\n type: 'boolean',\n description:\n 'Use per-file content-hash cache (use --no-cache to disable)',\n default: true,\n },\n 'cache-dir': {\n type: 'string',\n description: 'Cache directory (default: .madr-lint/cache)',\n required: false,\n },\n format: {\n type: 'string',\n description: 'Reporter format: text (default), json, or sarif',\n default: 'text',\n },\n },\n run({ args }) {\n const cwd = process.cwd();\n let config: ResolvedConfig = loadConfig(cwd);\n\n // CLI default UX: when no rules are explicitly configured, fall back to\n // the `recommended` preset so users get useful output without authoring\n // `.madrlintrc.json` first.\n if (Object.keys(config.rules).length === 0) {\n config = resolveExtends({ extends: ['madr-lint:recommended'] });\n }\n\n // citty: positionals collect into `args._` (array of strings). If\n // empty, fall back to the config's adrDir.\n const positionals = (args._ as string[] | undefined) ?? [];\n const inputs = positionals.length > 0 ? positionals : [config.adrDir];\n\n const seen = new Set<string>();\n const allFiles: string[] = [];\n for (const input of inputs) {\n for (const file of findAdrFiles(input)) {\n if (!seen.has(file)) {\n seen.add(file);\n allFiles.push(file);\n }\n }\n }\n\n // Apply ignorePatterns from config (e.g. README.md, template.md, 9999-*).\n // POSIX-normalize so path-suffix patterns work cross-platform on Windows.\n const files = allFiles.filter(\n (absPath) =>\n !shouldIgnore(toPosix(relative(cwd, absPath)), config.ignorePatterns),\n );\n\n if (files.length === 0) {\n const skipped = allFiles.length - files.length;\n const note = skipped > 0 ? ` (${skipped} ignored by config)` : '';\n const where = inputs.length === 1 ? inputs[0] : inputs.join(', ');\n console.log(`No .md files to lint in ${where}${note}`);\n process.exit(0);\n }\n\n // CLI flag wins over config — `--no-cache` sets args.cache=false,\n // overriding `cache: true` from .madrlintrc.json.\n const cacheEnabled = (args.cache ?? config.cache) === true;\n const cacheDir = (args['cache-dir'] ?? config.cacheLocation) as string;\n const cacheConfig: CacheConfig | null = cacheEnabled\n ? {\n dir: resolve(cwd, cacheDir),\n configHash: computeConfigHash({\n rules: config.rules,\n ignorePatterns: config.ignorePatterns,\n madrVersion: config.madrVersion,\n }),\n pkgVersion: pkg.version,\n }\n : null;\n\n const allRulesArray: AnyRule[] = Object.values(builtinRules);\n let result: ReturnType<typeof lintFiles>;\n try {\n result = lintFiles({\n rules: allRulesArray,\n ruleSeverity: config.rules,\n files,\n cwd,\n cache: cacheConfig,\n });\n } catch (err) {\n // Invalid rule options in the user's config fail AJV validation now that\n // options actually reach the rules. Surface a clear message instead of a\n // stack trace, and exit 2 (config error, distinct from lint failures).\n if (err instanceof RuleOptionsError) {\n console.error(`Invalid rule options in config: ${err.message}`);\n process.exit(2);\n }\n throw err;\n }\n\n const rulesByName = new Map<string, AnyRule>(\n allRulesArray.map((r) => [r.meta.name, r]),\n );\n const format = (args.format ?? 'text') as ReporterFormat;\n const reporter = reporters[format];\n if (!reporter) {\n console.error(\n `Unknown --format \"${format}\". Available: ${Object.keys(reporters).join(', ')}`,\n );\n process.exit(2);\n }\n console.log(reporter.format(result.diagnostics, rulesByName));\n\n const errorCount = result.diagnostics.filter(\n (d) => d.severity === 'error',\n ).length;\n process.exit(errorCount > 0 ? 1 : 0);\n },\n});\n\nawait runMain(main);\n","import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { Diagnostic } from './types.js';\n\n/**\n * Per-file cache entry: the file content's sha1 hash plus the per-file\n * diagnostics that the rule pipeline emitted last time.\n *\n * Note: project (cross-file) diagnostics are NOT cached — they depend on\n * the entire file set and would invalidate on any add/remove/rename.\n */\nexport interface CacheEntry {\n contentHash: string;\n perFileDiagnostics: Diagnostic[];\n}\n\nexport interface CacheManifest {\n /** madr-lint version when this manifest was written. Mismatch invalidates the cache. */\n version: string;\n /** Stable hash of the resolved config. Mismatch invalidates the cache. */\n configHash: string;\n /** Per-file cached entries, keyed by POSIX-normalized relative path. */\n files: Record<string, CacheEntry>;\n}\n\nexport function computeContentHash(content: string): string {\n return createHash('sha1').update(content, 'utf8').digest('hex');\n}\n\nexport function computeConfigHash(config: unknown): string {\n return createHash('sha1').update(stableStringify(config), 'utf8').digest('hex');\n}\n\n/**\n * Stable JSON stringify: object keys are sorted so the same logical\n * config always produces the same hash regardless of property insertion\n * order. Cycles are not handled — config objects are tree-shaped.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const entries = Object.entries(value as Record<string, unknown>).toSorted(\n ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0),\n );\n return (\n '{' +\n entries\n .map(([k, v]) => JSON.stringify(k) + ':' + stableStringify(v))\n .join(',') +\n '}'\n );\n}\n\nexport function manifestPath(cacheDir: string): string {\n return join(cacheDir, 'manifest.json');\n}\n\nexport function loadManifest(path: string): CacheManifest | null {\n if (!existsSync(path)) return null;\n try {\n const data = JSON.parse(readFileSync(path, 'utf8')) as Partial<CacheManifest>;\n if (\n typeof data.version !== 'string' ||\n typeof data.configHash !== 'string' ||\n typeof data.files !== 'object' ||\n data.files === null\n ) {\n return null;\n }\n return data as CacheManifest;\n } catch {\n return null;\n }\n}\n\nexport function saveManifest(path: string, manifest: CacheManifest): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(manifest), 'utf8');\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { createJiti } from 'jiti';\nimport { recommended } from '../configs/recommended.js';\nimport type { MadrLintConfig } from '../index.js';\nimport type { MadrVersion, RuleSeverity } from './types.js';\n\nexport interface ResolvedConfig {\n /** ADR directory, relative to cwd or absolute. Default: 'docs/adr'. */\n adrDir: string;\n /** Per-rule severity / options map. Empty when no preset extended. */\n rules: Record<string, RuleSeverity>;\n /** Glob patterns to skip. */\n ignorePatterns: string[];\n /** MADR version target. Default: 'auto'. */\n madrVersion: MadrVersion | 'auto';\n /** Whether to use per-file content-hash cache. Default: true. */\n cache: boolean;\n /** Cache directory (relative to cwd). Default: '.madr-lint/cache'. */\n cacheLocation: string;\n}\n\n/**\n * Config file resolution order. First match wins.\n *\n * `.json` is parsed directly (no module loader), so it stays the fastest\n * and dependency-free path. The other extensions go through `jiti`,\n * which transpiles TS/ESM/CJS on the fly.\n */\nconst CONFIG_FILES = [\n '.madrlintrc.json',\n '.madrlintrc.ts',\n '.madrlintrc.mts',\n '.madrlintrc.js',\n '.madrlintrc.mjs',\n '.madrlintrc.cjs',\n 'madr-lint.config.ts',\n 'madr-lint.config.mts',\n 'madr-lint.config.js',\n 'madr-lint.config.mjs',\n 'madr-lint.config.cjs',\n];\n\n/**\n * Loads and resolves a config from `cwd`. If no config file is found,\n * returns the defaults (no rules enabled — caller can opt into the\n * recommended preset by extending).\n *\n * `.json` is read synchronously and parsed inline. All other extensions\n * are loaded via `jiti`, supporting TypeScript and both module systems.\n */\nexport function loadConfig(cwd: string): ResolvedConfig {\n for (const name of CONFIG_FILES) {\n const path = resolve(cwd, name);\n if (!existsSync(path)) continue;\n\n if (name.endsWith('.json')) {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as MadrLintConfig;\n return resolveExtends(parsed);\n }\n\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const loaded = jiti(path) as MadrLintConfig | { default: MadrLintConfig };\n const config: MadrLintConfig =\n typeof loaded === 'object' && loaded !== null && 'default' in loaded\n ? (loaded.default as MadrLintConfig)\n : (loaded as MadrLintConfig);\n return resolveExtends(config);\n }\n return resolveExtends({});\n}\n\n/**\n * Pure function: turns a raw `MadrLintConfig` into a `ResolvedConfig`\n * with defaults filled in and `extends` presets merged.\n */\nexport function resolveExtends(config: MadrLintConfig): ResolvedConfig {\n const baseRules: Record<string, RuleSeverity> = config.extends?.includes(\n 'madr-lint:recommended',\n )\n ? { ...recommended }\n : {};\n\n return {\n adrDir: config.adrDir ?? 'docs/adr',\n rules: { ...baseRules, ...config.rules },\n ignorePatterns: config.ignorePatterns ?? [],\n madrVersion: config.madrVersion ?? 'auto',\n cache: config.cache ?? true,\n cacheLocation: config.cacheLocation ?? '.madr-lint/cache',\n };\n}\n","import type { RuleSeverity } from '../core/types.js';\n\n// The `recommended` preset enables rules whose default behavior is broadly\n// useful and grounded in the MADR spec. New rules append here via the\n// add-rule skill, with severity decided per-rule (default `error` for\n// spec-grounded rules, `warn` for convention-only rules).\n//\n// Both bare-string and tuple forms are supported here:\n// 'madr/foo': 'error' — enable with defaults\n// 'madr/foo': ['error', { pattern: '^X' }] — enable with override\n\nexport const recommended: Record<string, RuleSeverity> = {\n 'madr/date-iso8601': 'error',\n 'madr/filename-format': 'error',\n 'madr/no-broken-links': 'error',\n 'madr/no-duplicate-numbering': 'error',\n // Convention-only — opt in if your team treats ADR numbering as a\n // contiguous sequence. See docs/rules/no-numbering-gap.md.\n 'madr/no-numbering-gap': 'off',\n 'madr/required-sections': 'error',\n 'madr/status-enum': 'error',\n 'madr/supersedes-bidirectional': 'error',\n};\n","import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Resolves a target path to a list of `.md` files to lint.\n *\n * - If the target is a directory: recursively walks it and returns every `.md`\n * file, sorted lexicographically.\n * - If the target is a `.md` file: returns just that file.\n * - If the target is a non-`.md` file or does not exist: returns `[]`.\n *\n * The empty-array fallback lets the CLI print \"No .md files found\" rather\n * than crashing — the caller decides whether that is an error.\n */\nexport function findAdrFiles(target: string): string[] {\n if (!existsSync(target)) return [];\n\n const stat = statSync(target);\n if (stat.isFile()) {\n return target.endsWith('.md') ? [target] : [];\n }\n if (!stat.isDirectory()) return [];\n\n const entries = readdirSync(target, { recursive: true, withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith('.md'))\n .map((e) => join(e.parentPath, e.name))\n .toSorted();\n}\n","import { basename } from 'node:path';\nimport picomatch from 'picomatch';\n\n/**\n * Should the given relative path be excluded from linting?\n *\n * Patterns are tested in three forms (any match wins):\n * - **Full glob via picomatch** against the relative POSIX path:\n * `docs/**\\/draft-*.md`, `**\\/template.md`, `9999-*.md`\n * - Full glob via picomatch against the file's basename, so patterns\n * like `README.md` or `9999-*` work without a `**\\/` prefix.\n * - Exact relative-path match (defensive — picomatch with default\n * options also handles this, but we keep the explicit branch for\n * clarity and to avoid surprises if picomatch ever changes default\n * `dot`/`bash` semantics).\n *\n * Picomatch is invoked with `{ dot: true }` so dotfiles (rare for ADRs\n * but possible — e.g. `.archived/*.md`) are matched naturally.\n */\nexport function shouldIgnore(\n relativePath: string,\n patterns: readonly string[],\n): boolean {\n if (patterns.length === 0) return false;\n const base = basename(relativePath);\n for (const pattern of patterns) {\n if (matchesPattern(relativePath, base, pattern)) return true;\n }\n return false;\n}\n\nfunction matchesPattern(\n relativePath: string,\n base: string,\n pattern: string,\n): boolean {\n if (relativePath === pattern) return true;\n const matcher = picomatch(pattern, { dot: true });\n if (matcher(relativePath)) return true;\n if (matcher(base)) return true;\n // Back-compat shortcut: a pattern like `adr/template.md` (sub-path\n // without leading `**/`) should match `docs/adr/template.md`. Also\n // handles `adr/9999-*` against `docs/adr/9999-x.md` via picomatch\n // applied to the suffix.\n if (pattern.includes('/') && !pattern.startsWith('**/')) {\n const suffixMatcher = picomatch(`**/${pattern}`, { dot: true });\n if (suffixMatcher(relativePath)) return true;\n }\n return false;\n}\n","import { readFileSync, statSync } from 'node:fs';\nimport { isAbsolute, relative, resolve, sep } from 'node:path';\nimport {\n computeContentHash,\n loadManifest,\n manifestPath,\n saveManifest,\n type CacheEntry,\n type CacheManifest,\n} from './cache.js';\nimport {\n buildProjectFile,\n runRulesOnFile,\n runRulesOnProject,\n} from './runner.js';\n\n/**\n * Normalize a relative path to POSIX form (forward-slashes). On Windows,\n * `relative()` returns backslash-separated paths, but project rules\n * (e.g. `no-broken-links`) resolve URLs using `posix.normalize`. Mixing\n * separators causes silent false positives — so the orchestrator owns\n * the normalization.\n */\nfunction toPosix(p: string): string {\n if (sep === '/') return p;\n return p.split(sep).join('/');\n}\nimport {\n isProjectRule,\n type AnyRule,\n type Diagnostic,\n type ProjectRule,\n type Rule,\n type RuleSeverity,\n type Severity,\n} from './types.js';\n\nexport interface CacheConfig {\n /** Directory under which manifest.json is read/written. */\n dir: string;\n /** Stable hash of the resolved config. Mismatch invalidates the cache. */\n configHash: string;\n /** madr-lint package version. Mismatch invalidates the cache. */\n pkgVersion: string;\n}\n\nexport interface LintOptions {\n /** All available rules (per-file or project). The orchestrator picks\n * enabled ones via ruleSeverity and dispatches to the right runner. */\n rules: readonly AnyRule[];\n /** User-configured severity per rule name. Missing entry === 'off'. */\n ruleSeverity: Record<string, RuleSeverity>;\n /** Files to lint, absolute or cwd-relative paths. */\n files: readonly string[];\n /** Working directory. Diagnostics' `path` is reported relative to this. */\n cwd: string;\n /**\n * If set, persist per-file diagnostics keyed by content hash to enable\n * fast re-runs of unchanged files. Project (cross-file) rules always\n * re-run regardless of the cache. Pass `null` to disable.\n */\n cache?: CacheConfig | null;\n}\n\nexport interface LintResult {\n filesChecked: number;\n filesFromCache: number;\n diagnostics: Diagnostic[];\n}\n\n/**\n * Lint a list of files against per-file AND project rules.\n *\n * Per-file rules go through `runRulesOnFile` (single-pass AST per file).\n * Project rules go through `runRulesOnProject` (eager parse all files,\n * single check call per rule). See ADR-0005.\n *\n * File contents are read once and shared between both passes.\n */\nexport function lintFiles(opts: LintOptions): LintResult {\n const allDiagnostics: Diagnostic[] = [];\n\n // Read all file contents once — shared between per-file and project passes.\n // POSIX-normalize the relative path so cross-platform diagnostics and\n // path-based rules (e.g. no-broken-links) work consistently on Windows.\n const fileEntries = opts.files.map((absolutePath) => ({\n relativePath: toPosix(relative(opts.cwd, absolutePath)),\n content: readFileSync(absolutePath, 'utf8'),\n }));\n\n // Partition rules by kind.\n const perFileRules: Rule[] = [];\n const projectRules: ProjectRule[] = [];\n for (const rule of opts.rules) {\n if (isProjectRule(rule)) projectRules.push(rule);\n else perFileRules.push(rule);\n }\n\n // Per-rule options from config tuples (`['error', {...}]`), threaded into\n // both passes so rule options actually take effect (not just defaults).\n const optionsByRule = extractOptionsByRule(opts.ruleSeverity);\n\n // ── Cache setup ──────────────────────────────────────────────────\n let manifest: CacheManifest | null = null;\n let filesFromCache = 0;\n if (opts.cache) {\n const path = manifestPath(opts.cache.dir);\n const loaded = loadManifest(path);\n if (\n loaded &&\n loaded.version === opts.cache.pkgVersion &&\n loaded.configHash === opts.cache.configHash\n ) {\n manifest = loaded;\n } else {\n manifest = {\n version: opts.cache.pkgVersion,\n configHash: opts.cache.configHash,\n files: {},\n };\n }\n }\n\n // ── Per-file pass ────────────────────────────────────────────────\n for (const file of fileEntries) {\n if (manifest) {\n const contentHash = computeContentHash(file.content);\n const entry = manifest.files[file.relativePath];\n if (entry && entry.contentHash === contentHash) {\n allDiagnostics.push(...entry.perFileDiagnostics);\n filesFromCache++;\n continue;\n }\n // Cache miss — run rules and store result.\n const fileDiagnostics = runPerFileRulesForFile(\n perFileRules,\n opts.ruleSeverity,\n optionsByRule,\n file,\n );\n allDiagnostics.push(...fileDiagnostics);\n const next: CacheEntry = {\n contentHash,\n perFileDiagnostics: fileDiagnostics,\n };\n manifest.files[file.relativePath] = next;\n } else {\n allDiagnostics.push(\n ...runPerFileRulesForFile(\n perFileRules,\n opts.ruleSeverity,\n optionsByRule,\n file,\n ),\n );\n }\n }\n\n // ── Project pass ─────────────────────────────────────────────────\n const enabledProjectRules = projectRules.filter(\n (rule) => resolveSeverity(opts.ruleSeverity[rule.meta.name]) !== 'off',\n );\n if (enabledProjectRules.length > 0) {\n const projectFiles = fileEntries.map((f) =>\n buildProjectFile({ path: f.relativePath, content: f.content }),\n );\n\n // Does a path exist as a regular file WITHIN the project root? Lets\n // project rules verify link targets that are not in the linted .md set\n // (non-Markdown assets, files outside the scanned paths). Contract:\n // - a target that resolves at or above the root escapes the project →\n // false (path traversal cannot reach above the root);\n // - directories are not files → false (a link must point at a file);\n // - results are memoized per lint run (repeated targets cost one stat).\n // The containment test compares the resolved absolute path to the root\n // rather than a lexical prefix on `resolvedPath`, which may be\n // un-normalized (e.g. a `/`-rooted link resolving to `foo/../../x`).\n // NOTE: statSync follows symlinks, so a symlink that lives inside the root\n // but points outside it is accepted — it is a real entry in the project\n // tree. `resolvedPath` is POSIX; node's path.resolve accepts forward\n // slashes on all platforms.\n const projectRoot = resolve(opts.cwd);\n const existsCache = new Map<string, boolean>();\n const fileExists = (resolvedPath: string): boolean => {\n const cached = existsCache.get(resolvedPath);\n if (cached !== undefined) return cached;\n const abs = resolve(projectRoot, resolvedPath);\n const rel = relative(projectRoot, abs);\n const escapesRoot =\n rel === '' || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);\n let result: boolean;\n if (escapesRoot) {\n result = false;\n } else {\n try {\n result = statSync(abs, { throwIfNoEntry: false })?.isFile() ?? false;\n } catch {\n // ELOOP / EACCES / invalid path → cannot confirm a regular file.\n result = false;\n }\n }\n existsCache.set(resolvedPath, result);\n return result;\n };\n\n const errorRules: ProjectRule[] = [];\n const warnRules: ProjectRule[] = [];\n for (const rule of enabledProjectRules) {\n const sev = resolveSeverity(opts.ruleSeverity[rule.meta.name]);\n if (sev === 'error') errorRules.push(rule);\n else if (sev === 'warn') warnRules.push(rule);\n }\n\n if (errorRules.length > 0) {\n allDiagnostics.push(\n ...runRulesOnProject(errorRules, projectFiles, {\n severity: 'error',\n fileExists,\n optionsByRule,\n }),\n );\n }\n if (warnRules.length > 0) {\n allDiagnostics.push(\n ...runRulesOnProject(warnRules, projectFiles, {\n severity: 'warn',\n fileExists,\n optionsByRule,\n }),\n );\n }\n }\n\n // ── Persist cache ────────────────────────────────────────────────\n if (manifest && opts.cache) {\n saveManifest(manifestPath(opts.cache.dir), manifest);\n }\n\n return {\n filesChecked: opts.files.length,\n filesFromCache,\n diagnostics: allDiagnostics,\n };\n}\n\nfunction runPerFileRulesForFile(\n perFileRules: readonly Rule[],\n ruleSeverity: Record<string, RuleSeverity>,\n optionsByRule: Record<string, Record<string, unknown>>,\n file: { relativePath: string; content: string },\n): Diagnostic[] {\n const fileContext = {\n content: file.content,\n path: file.relativePath,\n };\n\n const errorRules: Rule[] = [];\n const warnRules: Rule[] = [];\n for (const rule of perFileRules) {\n const sev = resolveSeverity(ruleSeverity[rule.meta.name]);\n if (sev === 'off') continue;\n if (sev === 'error') errorRules.push(rule);\n else warnRules.push(rule);\n }\n\n const diagnostics: Diagnostic[] = [];\n if (errorRules.length > 0) {\n diagnostics.push(\n ...runRulesOnFile(errorRules, fileContext, {\n severity: 'error',\n optionsByRule,\n }),\n );\n }\n if (warnRules.length > 0) {\n diagnostics.push(\n ...runRulesOnFile(warnRules, fileContext, {\n severity: 'warn',\n optionsByRule,\n }),\n );\n }\n return diagnostics;\n}\n\nfunction resolveSeverity(config: RuleSeverity | undefined): Severity | 'off' {\n if (config === undefined) return 'off';\n if (typeof config === 'string') return config;\n return config[0];\n}\n\n/**\n * Build a ruleName → options map from config tuples (`['error', {...}]`).\n * Bare-string severities carry no options and are omitted, so rules without\n * configured options fall back to their `meta.defaultOptions` in the runner.\n */\nfunction extractOptionsByRule(\n ruleSeverity: Record<string, RuleSeverity>,\n): Record<string, Record<string, unknown>> {\n const map: Record<string, Record<string, unknown>> = {};\n for (const [name, config] of Object.entries(ruleSeverity)) {\n // Only tuples carry options; guard config[1] so a malformed 1-element\n // `['error']` doesn't put `undefined` into the map.\n if (Array.isArray(config) && config[1]) map[name] = config[1];\n }\n return map;\n}\n","import Ajv, { type AnySchemaObject, type ValidateFunction } from 'ajv';\nimport type {\n Diagnostic,\n FileContext,\n MdastNode,\n ProjectFile,\n ProjectRule,\n ProjectRuleContext,\n Rule,\n RuleContext,\n RuleListeners,\n Severity,\n} from './types.js';\nimport { parseFile, type ParsedFile } from './parser.js';\n\n// strict: true catches typo'd schema keywords at compile time.\n// allErrors: true so RuleOptionsError surfaces every issue at once.\nconst ajv = new Ajv({ strict: true, allErrors: true });\n\n// Cache compiled validators per schema object reference.\n// Schemas are module-level static on rules, so the cache key never goes stale.\n// (If a rule mutates `meta.schema` post-load — don't — the cache will desync.)\nconst validatorCache = new WeakMap<AnySchemaObject, ValidateFunction>();\n\nfunction getValidator(schema: AnySchemaObject): ValidateFunction {\n let validator = validatorCache.get(schema);\n if (!validator) {\n validator = ajv.compile(schema);\n validatorCache.set(schema, validator);\n }\n return validator;\n}\n\n/**\n * The reserved rule name used for internal-error diagnostics. A real rule\n * cannot register with this name (the registry should reject collisions).\n */\nexport const INTERNAL_ERROR_RULE_NAME = 'core/internal-error';\n\n/**\n * Thrown when a rule's merged options fail AJV validation against\n * `rule.meta.schema`. Distinct from generic `Error` so callers can\n * `instanceof` to skip rules with bad config and continue.\n */\nexport class RuleOptionsError extends Error {\n constructor(\n public readonly ruleName: string,\n public readonly ajvErrors: string,\n ) {\n super(`Invalid options for rule ${ruleName}: ${ajvErrors}`);\n this.name = 'RuleOptionsError';\n }\n}\n\nexport interface RunRuleOptions {\n /** Per-rule options to merge over rule.meta.defaultOptions. */\n options?: Record<string, unknown>;\n /**\n * Options keyed by rule name, for multi-rule calls where each rule needs\n * its own options (e.g. config tuples `['error', {...}]`). For a given rule,\n * an entry here takes precedence over `options`; rules absent from the map\n * fall back to `options` (if any), then `meta.defaultOptions`.\n */\n optionsByRule?: Record<string, Record<string, unknown>>;\n /** Severity to attach to emitted diagnostics. Defaults to 'error'. */\n severity?: Severity;\n /** Skip AJV options validation. Default false. */\n skipValidation?: boolean;\n /**\n * Filesystem-existence predicate exposed to project rules as\n * `context.fileExists`. Per-file runs ignore this. See\n * ProjectRuleContext.fileExists.\n */\n fileExists?: (resolvedPath: string) => boolean;\n}\n\n/**\n * Run a single rule against a single file. Sugar over runRulesOnFile\n * for the common one-rule case (used heavily by tests).\n */\nexport function runRule<TOptions extends Record<string, unknown>>(\n rule: Rule<TOptions>,\n file: FileContext,\n runtime: RunRuleOptions = {},\n): Diagnostic[] {\n return runRulesOnFile([rule as Rule], file, runtime);\n}\n\n/**\n * Run multiple rules against a single file with a single mdast traversal.\n *\n * Steps:\n * 1. Validate each rule's merged options against its schema (AJV).\n * Throws RuleOptionsError on failure (caller decides whether to skip).\n * 2. Call each rule's create(). If a rule throws, the error is caught\n * and reported as a `core/internal-error` diagnostic — other rules\n * continue. The same isolation wraps every enter/exit handler so a\n * single buggy rule cannot abort the whole lint run.\n * 3. If at least one rule returned RuleListeners, call parseFile()\n * ONCE (gray-matter + mdast-util-from-markdown together; there is\n * no separate cheap \"frontmatter only\" path) and walk the tree,\n * dispatching enter/exit handlers per node type.\n *\n * Frontmatter is exposed lazily via context.frontmatter — the getter\n * triggers parseFile() on first access. Filename-style rules that never\n * touch frontmatter and return void from create() pay zero parse cost.\n */\nexport function runRulesOnFile(\n rules: readonly Rule[],\n file: FileContext,\n runtime: RunRuleOptions = {},\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n const severity = runtime.severity ?? 'error';\n\n let parsed: ParsedFile | null = null;\n const ensureParsed = (): ParsedFile => {\n parsed ??= parseFile(file.content);\n return parsed;\n };\n\n const enterMap: Record<string, Array<(node: MdastNode) => void>> = {};\n const exitMap: Record<string, Array<(node: MdastNode) => void>> = {};\n let hasASTRule = false;\n\n for (const rule of rules) {\n const perRuleOptions =\n runtime.optionsByRule?.[rule.meta.name] ?? runtime.options;\n const mergedOptions = {\n ...rule.meta.defaultOptions,\n ...perRuleOptions,\n };\n\n if (rule.meta.schema && !runtime.skipValidation) {\n const validator = getValidator(rule.meta.schema);\n if (!validator(mergedOptions)) {\n throw new RuleOptionsError(rule.meta.name, ajv.errorsText(validator.errors));\n }\n }\n\n const context: RuleContext = {\n file,\n get frontmatter() {\n return ensureParsed().frontmatter;\n },\n get metadata() {\n return ensureParsed().metadata;\n },\n options: mergedOptions,\n report(d) {\n diagnostics.push({\n ruleName: rule.meta.name,\n severity,\n path: file.path,\n ...d,\n });\n },\n };\n\n let listeners: RuleListeners | void;\n try {\n listeners = rule.create(context);\n } catch (err) {\n // RuleOptionsError signals the user's config is wrong (e.g. unsafe\n // regex). Propagate it so the CLI can render a clear \"fix your\n // .madrlintrc\" message rather than burying it as a per-file\n // internal-error diagnostic.\n if (err instanceof RuleOptionsError) throw err;\n diagnostics.push(\n internalErrorDiagnostic(rule.meta.name, 'create', err, file.path),\n );\n continue;\n }\n\n if (listeners) {\n hasASTRule = true;\n collectListeners(rule, listeners, enterMap, exitMap, diagnostics, file);\n }\n }\n\n if (hasASTRule) {\n walk(ensureParsed().ast, enterMap, exitMap);\n }\n\n return diagnostics;\n}\n\n// Internal-error diagnostics are ALWAYS severity 'error' regardless of the\n// runtime-passed severity for the rule that threw — a rule misbehaving is\n// never something the user wants to silence as a warning.\nfunction internalErrorDiagnostic(\n ruleName: string,\n operation: 'create' | 'enter' | 'exit' | 'check',\n err: unknown,\n path: string,\n): Diagnostic {\n return {\n ruleName: INTERNAL_ERROR_RULE_NAME,\n messageId: 'ruleThrew',\n severity: 'error',\n path,\n data: {\n rule: ruleName,\n operation,\n error: err instanceof Error ? err.message : String(err),\n },\n };\n}\n\nfunction collectListeners(\n rule: Rule,\n listeners: RuleListeners,\n enterMap: Record<string, Array<(node: MdastNode) => void>>,\n exitMap: Record<string, Array<(node: MdastNode) => void>>,\n diagnostics: Diagnostic[],\n file: FileContext,\n): void {\n const wrap =\n (op: 'enter' | 'exit') => (handler: (node: MdastNode) => void) => (node: MdastNode) => {\n try {\n handler(node);\n } catch (err) {\n diagnostics.push(\n internalErrorDiagnostic(rule.meta.name, op, err, file.path),\n );\n }\n };\n\n if (listeners.enter) {\n const wrapEnter = wrap('enter');\n for (const [type, handler] of Object.entries(listeners.enter)) {\n if (handler) (enterMap[type] ??= []).push(wrapEnter(handler));\n }\n }\n if (listeners.exit) {\n const wrapExit = wrap('exit');\n for (const [type, handler] of Object.entries(listeners.exit)) {\n if (handler) (exitMap[type] ??= []).push(wrapExit(handler));\n }\n }\n}\n\nfunction walk(\n node: MdastNode,\n enterMap: Record<string, Array<(node: MdastNode) => void>>,\n exitMap: Record<string, Array<(node: MdastNode) => void>>,\n): void {\n const enterHandlers = enterMap[node.type];\n if (enterHandlers) {\n for (const handler of enterHandlers) handler(node);\n }\n if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n walk(child as MdastNode, enterMap, exitMap);\n }\n }\n const exitHandlers = exitMap[node.type];\n if (exitHandlers) {\n for (const handler of exitHandlers) handler(node);\n }\n}\n\n/**\n * Build a `ProjectFile` (path + content + parsed frontmatter + AST) from\n * a raw file. Used by `runRulesOnProject` and by the lint orchestrator\n * to share parsing between per-file and project passes when needed.\n */\nexport function buildProjectFile(file: FileContext): ProjectFile {\n const parsed: ParsedFile = parseFile(file.content);\n return {\n path: file.path,\n content: file.content,\n frontmatter: parsed.frontmatter,\n metadata: parsed.metadata,\n ast: parsed.ast,\n };\n}\n\n/**\n * Run multiple project rules across all parsed files. Each rule's\n * `check()` is called once with the full file array. Throws are\n * captured as `core/internal-error` diagnostics (attached to the\n * first file's path, since project rules have no file context).\n *\n * See ADR-0005 for the API rationale.\n */\nexport function runRulesOnProject(\n rules: readonly ProjectRule[],\n files: readonly ProjectFile[],\n runtime: RunRuleOptions = {},\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n const severity = runtime.severity ?? 'error';\n\n for (const rule of rules) {\n const perRuleOptions =\n runtime.optionsByRule?.[rule.meta.name] ?? runtime.options;\n const mergedOptions = {\n ...rule.meta.defaultOptions,\n ...perRuleOptions,\n };\n\n if (rule.meta.schema && !runtime.skipValidation) {\n const validator = getValidator(rule.meta.schema);\n if (!validator(mergedOptions)) {\n throw new RuleOptionsError(\n rule.meta.name,\n ajv.errorsText(validator.errors),\n );\n }\n }\n\n const context: ProjectRuleContext = {\n files,\n options: mergedOptions,\n fileExists: runtime.fileExists,\n report(d) {\n diagnostics.push({\n ruleName: rule.meta.name,\n severity,\n ...d,\n });\n },\n };\n\n try {\n rule.check(context);\n } catch (err) {\n // Project rules have no current-file context — attribute the\n // internal-error to a sentinel path rather than mis-attributing\n // to whatever file happens to be first in the array.\n diagnostics.push(\n internalErrorDiagnostic(rule.meta.name, 'check', err, '<project>'),\n );\n }\n }\n\n return diagnostics;\n}\n","import type { List, ListItem, Paragraph, Root } from 'mdast';\nimport { fromMarkdown } from 'mdast-util-from-markdown';\nimport { toString } from 'mdast-util-to-string';\nimport grayMatter from 'gray-matter';\n\nexport interface ParsedFile {\n /** Parsed YAML frontmatter (v3/v4), or null if absent. */\n frontmatter: Record<string, unknown> | null;\n /**\n * v2 body-list metadata extracted from the leading list, or null if absent.\n * Covers both the bold-key (`- **Status**:`) and the canonical MADR v2.1.2\n * plain-key (`* Status:`) shapes. See ADR-0006.\n */\n listMetadata: Record<string, unknown> | null;\n /**\n * Combined metadata: frontmatter merged with bold-list. Frontmatter wins\n * on key conflict. null only when both are absent. See ADR-0006.\n */\n metadata: Record<string, unknown> | null;\n /** mdast root of the body (frontmatter stripped). */\n ast: Root;\n /** Body content with frontmatter stripped. */\n body: string;\n}\n\n/**\n * Parse a Markdown file: extract YAML frontmatter via gray-matter, then\n * feed the body into mdast-util-from-markdown directly (per ADR-0002,\n * we skip the unified+remark pipeline overhead). Additionally, extract\n * v2-style list metadata from the body's first list — both bold-key and\n * canonical plain-key shapes (see ADR-0006).\n */\nexport function parseFile(content: string): ParsedFile {\n const matter = grayMatter(content);\n const data = matter.data as Record<string, unknown>;\n const frontmatter = Object.keys(data).length > 0 ? data : null;\n const ast = fromMarkdown(matter.content);\n const listMetadata = extractListMetadata(ast);\n\n let metadata: Record<string, unknown> | null = null;\n if (frontmatter && listMetadata) {\n // Frontmatter wins on conflict, BUT explicit null/undefined are\n // skipped so that `status: ~` in YAML doesn't blank a present\n // list value (counterintuitive UX otherwise).\n const frontmatterDefined: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(frontmatter)) {\n if (v !== null && v !== undefined) frontmatterDefined[k] = v;\n }\n metadata = { ...listMetadata, ...frontmatterDefined };\n } else if (frontmatter) {\n metadata = frontmatter;\n } else if (listMetadata) {\n metadata = listMetadata;\n }\n\n return { frontmatter, listMetadata, metadata, ast, body: matter.content };\n}\n\n// Canonical MADR metadata field names across v2/v3/v4 (after key\n// normalization). Used to distinguish a genuine metadata block from an\n// ordinary leading list: the plain-key shape (`Foo: bar`) is syntactically\n// identical to prose bullets, so we only promote a list to metadata when it\n// carries at least one of these keys. The set is deliberately the canonical\n// fields — a list keyed only by non-standard fields (e.g. `Tags`, `Author`)\n// is treated as prose, not metadata. See ADR-0006 and the xtone/ai-delivery\n// dogfooding.\nconst RECOGNIZED_METADATA_KEYS = new Set([\n 'status',\n 'date',\n 'deciders',\n 'decision-makers',\n 'consulted',\n 'informed',\n]);\n\n/**\n * Extract v2-style list metadata from an mdast Root. The metadata block is\n * the leading list — the first block after the H1 title (only headings may\n * precede it; an intervening paragraph means the list is body content, not\n * metadata). Each `listItem` is read as one `Key: value` pair in either of\n * two shapes:\n *\n * - Bold key: `- **Status**: accepted` (some MADR v2 authors)\n * - Plain key: `* Status: accepted` (official MADR v2.1.2 template)\n *\n * Key normalization: trim → lowercase → spaces become hyphens\n * (matches v4 frontmatter convention, e.g. \"Decision Makers\" →\n * \"decision-makers\"). On duplicate keys, first occurrence wins.\n *\n * Returns null unless the list carries a recognized MADR key, so ordinary\n * leading prose lists are not mistaken for metadata. (A leading list keyed by\n * a recognized field is still treated as metadata even if its value reads\n * prose-like, e.g. `Status: under discussion` — that position + key is\n * overwhelmingly metadata; this is an intentional, bounded tradeoff.)\n *\n * Because the result is gated on MADR field names, callers using this as a\n * generic list reader will get null for purely non-MADR lists.\n */\nexport function extractListMetadata(\n ast: Root,\n): Record<string, unknown> | null {\n let firstList: List | null = null;\n for (const child of ast.children) {\n if (child.type === 'heading') {\n if (child.depth >= 2) break; // reached the first H2 — no metadata block\n continue; // skip the H1 title (and any further headings)\n }\n // Leading HTML comments / markers don't displace the metadata block.\n if (child.type === 'html') continue;\n if (child.type === 'list') {\n firstList = child;\n break;\n }\n // Any other leading block (paragraph, code, thematic break, …) means the\n // metadata block — which must lead — is absent; the list, if any, is prose.\n break;\n }\n\n if (!firstList) return null;\n\n const result: Record<string, unknown> = {};\n for (const item of firstList.children) {\n const pair = extractListItemKV(item);\n if (pair && !(pair.key in result)) {\n result[pair.key] = pair.value;\n }\n }\n\n const hasRecognizedKey = Object.keys(result).some((k) =>\n RECOGNIZED_METADATA_KEYS.has(k),\n );\n return hasRecognizedKey ? result : null;\n}\n\nconst KEY_PATTERN = /^[A-Za-z][A-Za-z0-9 \\-_]*$/;\nconst SEPARATOR_PATTERN = /^\\s*:\\s*/;\n\nfunction normalizeKey(rawKey: string): string {\n return rawKey.trim().toLowerCase().replace(/\\s+/g, '-');\n}\n\n/**\n * Read a single `Key: value` pair from one list item. Returns null for any\n * item not shaped as metadata. Two shapes are supported:\n *\n * - Bold key: paragraph starts with `strong` (the Key), followed by a\n * `: value` text node and optional inline tail.\n * - Plain key: paragraph starts with a `text` node holding `Key: value`,\n * followed by optional inline tail.\n *\n * In both shapes the value's inline tail (links, code, emphasis) is\n * flattened via mdast-util-to-string so values keep their text.\n */\nfunction extractListItemKV(\n item: ListItem,\n): { key: string; value: string } | null {\n const para = item.children[0];\n if (para?.type !== 'paragraph') return null;\n\n const paragraph = para as Paragraph;\n const first = paragraph.children[0];\n if (!first) return null;\n\n // Bold key: `**Key**` then a separator text node carrying `: value`.\n if (first.type === 'strong') {\n const keyNode = first.children[0];\n if (keyNode?.type !== 'text' || !KEY_PATTERN.test(keyNode.value)) {\n return null;\n }\n\n const sep = paragraph.children[1];\n if (sep?.type !== 'text' || !SEPARATOR_PATTERN.test(sep.value)) return null;\n\n const valueHead = sep.value.replace(SEPARATOR_PATTERN, '');\n const valueTail = paragraph.children\n .slice(2)\n .map((n) => toString(n))\n .join('');\n const value = (valueHead + valueTail).trim();\n if (value === '') return null;\n\n return { key: normalizeKey(keyNode.value), value };\n }\n\n // Plain key (canonical MADR v2.1.2): first text node holds `Key: value`.\n if (first.type === 'text') {\n const colonIdx = first.value.indexOf(':');\n if (colonIdx === -1) return null;\n\n const rawKey = first.value.slice(0, colonIdx);\n if (!KEY_PATTERN.test(rawKey)) return null;\n\n const valueHead = first.value.slice(colonIdx + 1).replace(/^\\s+/, '');\n const valueTail = paragraph.children\n .slice(1)\n .map((n) => toString(n))\n .join('');\n const value = (valueHead + valueTail).trim();\n if (value === '') return null;\n\n return { key: normalizeKey(rawKey), value };\n }\n\n return null;\n}\n","// MADR linter core types.\n// Per ADR-0002, the runner uses a single-pass visitor registry over mdast.\n// The Rule type below supports BOTH:\n// - Filename / metadata-only rules: create() returns void\n// - AST-based rules: create() returns RuleListeners with enter/exit handlers\n// keyed by mdast node type. The runner walks each file's tree once and\n// dispatches to all subscribed rules.\n\nimport type { AnySchemaObject } from 'ajv';\nimport type { Nodes, Root } from 'mdast';\n// Note: imports of project-internal modules use the `.js` extension per\n// Node ESM convention. tsc resolves these to the .ts source files.\n\n// All mdast node types rules can subscribe to. `'root'` is the top-level\n// document node — useful for \"after all children visited\" reporting in\n// exit handlers.\nexport type MdastNodeType = Root['type'] | Nodes['type'];\n\n// The argument type for visitor handlers — covers root + all descendants.\nexport type MdastNode = Root | Nodes;\n\n/** Severity levels supported by every rule. */\nexport type Severity = 'error' | 'warn';\n\n/**\n * User-facing severity declaration in config files.\n * - bare string: enable/disable with default options\n * - tuple: enable with explicit options\n */\nexport type RuleSeverity =\n | Severity\n | 'off'\n | readonly [Severity, Record<string, unknown>];\n\nexport type MadrVersion = 'v2' | 'v3' | 'v4';\n\nexport interface RuleMeta<TOptions = Record<string, unknown>> {\n /** Rule identifier, e.g. 'madr/filename-format'. */\n name: string;\n /** Whether the rule operates per-file or across the project. */\n type: 'perFile' | 'project';\n /** Which MADR versions this rule applies to. */\n versionCompat: readonly MadrVersion[];\n docs: {\n description: string;\n url?: string;\n recommended: boolean;\n };\n /** messageId -> template string with {{placeholders}}. */\n messages: Record<string, string>;\n /** Default options merged with user config. */\n defaultOptions: TOptions;\n /** AJV JSON Schema for options. Validated at runRule time. */\n schema?: AnySchemaObject;\n}\n\nexport interface FileContext {\n /** File path (typically basename or repo-relative). */\n path: string;\n /** Raw file content. */\n content: string;\n}\n\nexport interface Diagnostic {\n /** Rule that emitted this diagnostic. */\n ruleName: string;\n /** messageId from rule.meta.messages. */\n messageId: string;\n /** Severity resolved against the user config. */\n severity: Severity;\n /** Path of the offending file. */\n path: string;\n /** Optional source range. Filename rules omit this. */\n loc?: {\n line: number;\n column: number;\n };\n /** Data for placeholder interpolation in messages[messageId]. */\n data?: Record<string, unknown>;\n}\n\nexport interface RuleContext<TOptions = Record<string, unknown>> {\n /** The file being linted. */\n file: FileContext;\n /**\n * Parsed YAML frontmatter (v3/v4 only), or null if absent.\n * Use this when the rule must validate strict YAML frontmatter shape.\n */\n frontmatter: Record<string, unknown> | null;\n /**\n * Combined metadata: YAML frontmatter merged with v2 bold-list extracted\n * from the body. Frontmatter wins on key conflict. null only when both\n * are absent. See ADR-0006. Use this for format-agnostic field reads.\n */\n metadata: Record<string, unknown> | null;\n /** User-merged options for this rule (validated against rule.meta.schema). */\n options: TOptions;\n /** Emit a diagnostic. */\n report(diagnostic: Omit<Diagnostic, 'ruleName' | 'severity' | 'path'>): void;\n}\n\n/**\n * Visitor map dispatched during single-pass mdast traversal.\n * Rules subscribe to specific node types; the runner walks the tree once\n * and calls the matching enter/exit handler for each node.\n */\nexport interface RuleListeners {\n enter?: Partial<Record<MdastNodeType, (node: MdastNode) => void>>;\n exit?: Partial<Record<MdastNodeType, (node: MdastNode) => void>>;\n}\n\n/**\n * A lint rule.\n *\n * - Filename / metadata-only rules: `create` reports diagnostics directly,\n * then returns void.\n * - AST-based rules: `create` returns RuleListeners; the runner invokes\n * them during single-pass traversal of the file's mdast tree.\n */\nexport interface Rule<TOptions = Record<string, unknown>> {\n meta: RuleMeta<TOptions>;\n create(context: RuleContext<TOptions>): RuleListeners | void;\n}\n\n// ──────────────────────────────────────────────────────────────────\n// Project rules (cross-file). See ADR-0005.\n// ──────────────────────────────────────────────────────────────────\n\n/**\n * A pre-parsed file passed to project rules. Built once per\n * `runRulesOnProject` invocation.\n */\nexport interface ProjectFile {\n /** File path relative to the project root. */\n path: string;\n /** Raw file content (unparsed). */\n content: string;\n /** Parsed YAML frontmatter (v3/v4 only), or null if absent. */\n frontmatter: Record<string, unknown> | null;\n /**\n * Combined metadata: frontmatter merged with v2 bold-list extracted\n * from the body. Frontmatter wins on key conflict. See ADR-0006.\n */\n metadata: Record<string, unknown> | null;\n /** mdast root of the body (frontmatter stripped). */\n ast: Root;\n}\n\nexport interface ProjectRuleContext<TOptions = Record<string, unknown>> {\n /** All files in the project (eager-parsed). */\n files: readonly ProjectFile[];\n /** User-merged options for this rule. */\n options: TOptions;\n /**\n * Predicate: does a project-root-relative POSIX path exist as a regular\n * file WITHIN the project root? Injected by the orchestrator so rules (e.g.\n * no-broken-links) can verify link targets that are NOT in the linted `.md`\n * set — non-Markdown assets or files outside the scanned paths. Returns\n * false for directories and for any target that resolves at or above the\n * project root (i.e. escapes it). Undefined when no filesystem is available\n * (e.g. in-memory unit tests), in which case rules fall back to the `files`\n * set alone.\n */\n fileExists?: (resolvedPath: string) => boolean;\n /**\n * Emit a diagnostic. Unlike per-file rules, project rules MUST set\n * `path` explicitly — the runner cannot infer which file the\n * diagnostic relates to.\n */\n report(diagnostic: Omit<Diagnostic, 'ruleName' | 'severity'>): void;\n}\n\n/**\n * A cross-file rule. Receives all parsed files at once and reports\n * diagnostics with explicit `path` per file.\n *\n * Project rules use a different runner function (`runRulesOnProject`)\n * and a distinct context shape from per-file rules. See ADR-0005 for\n * the rationale behind keeping the two APIs separate.\n */\nexport interface ProjectRule<TOptions = Record<string, unknown>> {\n meta: RuleMeta<TOptions>;\n check(context: ProjectRuleContext<TOptions>): void;\n}\n\n/** Either a per-file or a project rule. */\nexport type AnyRule<TOptions = Record<string, unknown>> =\n | Rule<TOptions>\n | ProjectRule<TOptions>;\n\n/** Type guard distinguishing project rules from per-file rules. */\nexport function isProjectRule(rule: AnyRule): rule is ProjectRule {\n return 'check' in rule;\n}\n","import pc from 'picocolors';\nimport type { AnyRule, Diagnostic } from './types.js';\n\nexport interface Reporter {\n format(\n diagnostics: readonly Diagnostic[],\n rulesByName: Map<string, AnyRule>,\n ): string;\n}\n\nexport type ReporterFormat = 'text' | 'json' | 'sarif';\n\n/**\n * Human-readable reporter for terminal output. Groups diagnostics by file,\n * interpolates {{placeholders}} from `data` against the rule's message\n * template, and ends with a count summary.\n *\n * Coloring uses picocolors which honors NO_COLOR / FORCE_COLOR env vars.\n */\nexport const textReporter: Reporter = {\n format(diagnostics, rulesByName) {\n if (diagnostics.length === 0) {\n return pc.green('✓ All clear.');\n }\n\n const byFile = groupByFile(diagnostics);\n const lines: string[] = [];\n\n for (const [file, fileDiagnostics] of byFile) {\n lines.push(pc.underline(file));\n for (const d of fileDiagnostics) {\n const sev =\n d.severity === 'error'\n ? pc.red(' error ')\n : pc.yellow(' warn ');\n const ruleId = pc.dim(d.ruleName.padEnd(28));\n const message = renderMessage(d, rulesByName);\n lines.push(`${sev}${ruleId} ${message}`);\n }\n lines.push('');\n }\n\n const errors = diagnostics.filter((d) => d.severity === 'error').length;\n const warnings = diagnostics.filter((d) => d.severity === 'warn').length;\n lines.push(pc.bold(formatSummary(errors, warnings)));\n\n return lines.join('\\n');\n },\n};\n\nfunction groupByFile(diagnostics: readonly Diagnostic[]): Map<string, Diagnostic[]> {\n const map = new Map<string, Diagnostic[]>();\n for (const d of diagnostics) {\n if (!map.has(d.path)) map.set(d.path, []);\n map.get(d.path)?.push(d);\n }\n return map;\n}\n\nfunction renderMessage(\n d: Diagnostic,\n rulesByName: Map<string, AnyRule>,\n): string {\n const rule = rulesByName.get(d.ruleName);\n const template = rule?.meta.messages[d.messageId] ?? d.messageId;\n return interpolate(template, d.data ?? {});\n}\n\nfunction interpolate(template: string, data: Record<string, unknown>): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key: string) => {\n const v = data[key];\n return v !== undefined ? String(v) : `{{${key}}}`;\n });\n}\n\nfunction formatSummary(errors: number, warnings: number): string {\n const parts: string[] = [];\n if (errors > 0) parts.push(`${errors} ${errors === 1 ? 'error' : 'errors'}`);\n if (warnings > 0) parts.push(`${warnings} ${warnings === 1 ? 'warning' : 'warnings'}`);\n return parts.join(', ');\n}\n\n/**\n * Machine-readable JSON reporter. Stable shape — additive changes only,\n * tagged via `version`. Tools can `JSON.parse` stdout and consume `results`\n * directly without scraping the human-readable formatter.\n */\nexport const jsonReporter: Reporter = {\n format(diagnostics, rulesByName) {\n const errors = diagnostics.filter((d) => d.severity === 'error').length;\n const warnings = diagnostics.filter((d) => d.severity === 'warn').length;\n const payload = {\n version: 1,\n summary: {\n total: diagnostics.length,\n errors,\n warnings,\n },\n results: diagnostics.map((d) => ({\n path: d.path,\n ruleName: d.ruleName,\n messageId: d.messageId,\n severity: d.severity,\n message: renderMessage(d, rulesByName),\n data: d.data ?? {},\n })),\n };\n return JSON.stringify(payload, null, 2);\n },\n};\n\n/**\n * SARIF 2.1.0 reporter. Emits a minimal-but-valid Static Analysis Results\n * Interchange Format document so GitHub Code Scanning, VS Code SARIF\n * Viewer, and other consumers can ingest madr-lint results.\n *\n * Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html\n */\nexport const sarifReporter: Reporter = {\n format(diagnostics, rulesByName) {\n const ruleNames = [...new Set(diagnostics.map((d) => d.ruleName))];\n const ruleIndex = new Map(ruleNames.map((name, i) => [name, i]));\n\n const sarifRules = ruleNames.map((name) => {\n const rule = rulesByName.get(name);\n return {\n id: name,\n name: name.replace(/[^A-Za-z0-9]/g, '_'),\n shortDescription: { text: rule?.meta.docs.description ?? name },\n helpUri: rule?.meta.docs.url,\n };\n });\n\n const sarifResults = diagnostics.map((d) => ({\n ruleId: d.ruleName,\n ruleIndex: ruleIndex.get(d.ruleName) ?? 0,\n level: d.severity === 'error' ? 'error' : 'warning',\n message: { text: renderMessage(d, rulesByName) },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: d.path, uriBaseId: '%SRCROOT%' },\n region: { startLine: 1 },\n },\n },\n ],\n }));\n\n const sarif = {\n $schema:\n 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json',\n version: '2.1.0',\n runs: [\n {\n tool: {\n driver: {\n name: 'madr-lint',\n informationUri: 'https://github.com/knktkc/madr-lint',\n rules: sarifRules,\n },\n },\n results: sarifResults,\n },\n ],\n };\n return JSON.stringify(sarif, null, 2);\n },\n};\n\nexport const reporters: Record<ReporterFormat, Reporter> = {\n text: textReporter,\n json: jsonReporter,\n sarif: sarifReporter,\n};\n","// Registry of all built-in madr-lint rules.\n// New rules append here via the add-rule skill.\n\nexport { default as dateIso8601 } from './date-iso8601/index.js';\nexport { default as filenameFormat } from './filename-format/index.js';\nexport { default as noBrokenLinks } from './no-broken-links/index.js';\nexport { default as noDuplicateNumbering } from './no-duplicate-numbering/index.js';\nexport { default as noNumberingGap } from './no-numbering-gap/index.js';\nexport { default as requiredSections } from './required-sections/index.js';\nexport { default as statusEnum } from './status-enum/index.js';\nexport { default as supersedesBidirectional } from './supersedes-bidirectional/index.js';\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"field\": {\n \"type\": \"string\",\n \"description\": \"Frontmatter key to read. Default: 'date'.\"\n }\n }\n}\n","import type { Rule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface DateIso8601Options extends Record<string, unknown> {\n field: string;\n}\n\nfunction normalizeDate(value: unknown): string | null {\n if (typeof value === 'string') return value;\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null;\n return value.toISOString().slice(0, 10);\n }\n return null;\n}\n\nfunction isValidIso8601Date(s: string): boolean {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) return false;\n const parts = s.split('-').map(Number);\n const [year, month, day] = parts;\n if (year === undefined || month === undefined || day === undefined) return false;\n const d = new Date(Date.UTC(year, month - 1, day));\n return (\n d.getUTCFullYear() === year &&\n d.getUTCMonth() === month - 1 &&\n d.getUTCDate() === day\n );\n}\n\nconst rule: Rule<DateIso8601Options> = {\n meta: {\n name: 'madr/date-iso8601',\n type: 'perFile',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description: 'Validate that ADR `date` (frontmatter or v2 bold-list) is a valid ISO 8601 calendar date (YYYY-MM-DD)',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/date-iso8601.md',\n recommended: true,\n },\n messages: {\n missingDate: 'Metadata does not contain a \"{{field}}\" field (checked frontmatter and v2 bold-list)',\n invalidDate: 'Date \"{{date}}\" is not a valid ISO 8601 calendar date (YYYY-MM-DD)',\n },\n defaultOptions: {\n field: 'date',\n },\n schema,\n },\n create(context) {\n const meta = context.metadata;\n const fieldName = context.options.field;\n\n if (!meta) {\n context.report({\n messageId: 'missingDate',\n data: { field: fieldName },\n });\n return;\n }\n\n const raw = meta[fieldName];\n const dateStr = normalizeDate(raw);\n if (dateStr === null) {\n context.report({\n messageId: 'missingDate',\n data: { field: fieldName },\n });\n return;\n }\n\n if (!isValidIso8601Date(dateStr)) {\n context.report({\n messageId: 'invalidDate',\n data: { date: dateStr },\n });\n }\n },\n};\n\nexport default rule;\n","import { basename } from 'node:path';\nimport { assertSafeRegex } from '../../core/regex-safety.js';\nimport type { Rule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface FilenameFormatOptions extends Record<string, unknown> {\n pattern: string;\n}\n\nconst rule: Rule<FilenameFormatOptions> = {\n meta: {\n name: 'madr/filename-format',\n type: 'perFile',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description: 'Enforce ADR filename convention NNNN-kebab-case.md',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/filename-format.md',\n recommended: true,\n },\n messages: {\n invalidFilename:\n 'Filename \"{{filename}}\" does not match expected pattern \"{{expected}}\"',\n },\n defaultOptions: {\n pattern: '^[0-9]{4}-[a-z0-9-]+\\\\.md$',\n },\n schema,\n },\n create(context) {\n const { pattern } = context.options;\n const regex = assertSafeRegex(pattern, 'madr/filename-format', 'pattern');\n const filename = basename(context.file.path);\n if (!regex.test(filename)) {\n context.report({\n messageId: 'invalidFilename',\n data: { filename, expected: pattern },\n });\n }\n },\n};\n\nexport default rule;\n","import safeRegex from 'safe-regex2';\nimport { RuleOptionsError } from './runner.js';\n\nconst SAFE_REGEX_LIMIT = 25;\n\n// Pattern → compiled RegExp cache. Only safe patterns are cached; unsafe\n// or syntactically invalid ones throw on every call (callers should fix\n// their config rather than burn cycles re-validating the same bad input).\n//\n// Cache scope: module-global. Keys are pattern strings, which are static\n// per rule.defaultOptions or per user config. Bounded in practice — a\n// long-running daemon would still see at most O(rules × user-overrides)\n// distinct patterns. If memory becomes a concern in pathological cases\n// (e.g. dynamically-generated patterns), the cache can be swapped for an\n// LRU. As of v0.1, a plain Map is sufficient.\nconst cache = new Map<string, RegExp>();\n\nexport function assertSafeRegex(\n pattern: string,\n ruleName: string,\n optionPath: string,\n): RegExp {\n const cached = cache.get(pattern);\n if (cached) return cached;\n\n let regex: RegExp;\n try {\n regex = new RegExp(pattern);\n } catch (err) {\n throw new RuleOptionsError(\n ruleName,\n `option \"${optionPath}\" is not a valid regular expression: ${(err as Error).message}`,\n );\n }\n if (!safeRegex(regex, { limit: SAFE_REGEX_LIMIT })) {\n throw new RuleOptionsError(\n ruleName,\n `option \"${optionPath}\" (${pattern}) appears unsafe (catastrophic backtracking). Reject before runtime.`,\n );\n }\n cache.set(pattern, regex);\n return regex;\n}\n\n/**\n * Test-only helper to drop the cache between unit tests so that\n * \"unsafe pattern\" assertions don't accidentally short-circuit on a\n * previously-cached safe entry. Not exported from src/index.ts.\n */\nexport function clearRegexCacheForTesting(): void {\n cache.clear();\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"pattern\": {\n \"type\": \"string\",\n \"description\": \"Regex (as string) the basename must match. Defaults to ^[0-9]{4}-[a-z0-9-]+\\\\.md$\"\n }\n }\n}\n","import { dirname, posix } from 'node:path';\nimport type { MdastNode, ProjectRule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface NoBrokenLinksOptions extends Record<string, unknown> {\n // No options at v0.1.\n}\n\nconst PROTOCOL_REGEX = /^[a-z][a-z0-9+.-]*:/i;\n\ninterface LinkLikeNode {\n url: string;\n}\n\nfunction isLinkNode(node: MdastNode): node is MdastNode & LinkLikeNode {\n return (\n node.type === 'link' &&\n 'url' in node &&\n typeof (node as { url: unknown }).url === 'string'\n );\n}\n\nfunction isExternalOrAnchor(url: string): boolean {\n if (url === '' || url.startsWith('#')) return true;\n return PROTOCOL_REGEX.test(url);\n}\n\nfunction stripAnchorAndQuery(url: string): string {\n const hash = url.indexOf('#');\n const query = url.indexOf('?');\n let cut = hash;\n if (query !== -1 && (cut === -1 || query < cut)) cut = query;\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n// Markdown link destinations may be percent-encoded (e.g. `my%20file.md`).\n// Decode to the real on-disk path for resolution; leave malformed encoding\n// (e.g. a bare `%`) untouched rather than throwing. Fast-path the common case\n// of no percent-encoding to keep per-link cost minimal (decodeURIComponent is\n// comparatively expensive).\nfunction safeDecode(path: string): string {\n if (!path.includes('%')) return path;\n try {\n return decodeURIComponent(path);\n } catch {\n return path;\n }\n}\n\nfunction resolveRelative(fromDir: string, url: string): string {\n // Treat absolute (`/foo`) as project-rooted by stripping the leading slash,\n // so `[x](/0042-y.md)` resolves to `0042-y.md` rather than the OS root.\n if (url.startsWith('/')) return url.slice(1);\n return posix.normalize(posix.join(fromDir, url));\n}\n\nfunction collectLinks(node: MdastNode, out: LinkLikeNode[]): void {\n if (isLinkNode(node)) out.push(node);\n if ('children' in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n collectLinks(child as MdastNode, out);\n }\n }\n}\n\nconst rule: ProjectRule<NoBrokenLinksOptions> = {\n meta: {\n name: 'madr/no-broken-links',\n type: 'project',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description:\n 'Relative-path Markdown links must resolve to an existing file in the project',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/no-broken-links.md',\n recommended: true,\n },\n messages: {\n brokenLink:\n 'Link to \"{{url}}\" resolves to \"{{resolvedPath}}\", which does not exist in the project',\n },\n defaultOptions: {},\n schema,\n },\n check(context) {\n const knownPaths = new Set<string>();\n for (const file of context.files) {\n knownPaths.add(file.path);\n }\n\n for (const file of context.files) {\n const links: LinkLikeNode[] = [];\n collectLinks(file.ast, links);\n\n for (const link of links) {\n const url = link.url;\n if (isExternalOrAnchor(url)) continue;\n\n const pathOnly = safeDecode(stripAnchorAndQuery(url));\n if (pathOnly === '') continue;\n\n const fileDir = dirname(file.path);\n const resolvedPath = resolveRelative(fileDir, pathOnly);\n\n // A target counts as present if it is one of the linted .md files\n // OR exists on disk (covers non-Markdown assets and files outside\n // the scanned paths). fileExists is undefined in pure in-memory\n // runs, where knownPaths is the only source of truth. NOTE: the\n // on-disk check inherits the host filesystem's case-sensitivity —\n // a wrong-case link may pass on macOS/Windows yet fail on Linux/CI.\n const exists =\n knownPaths.has(resolvedPath) ||\n (context.fileExists?.(resolvedPath) ?? false);\n\n if (!exists) {\n context.report({\n messageId: 'brokenLink',\n path: file.path,\n data: { url, resolvedPath },\n });\n }\n }\n }\n },\n};\n\nexport default rule;\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {}\n}\n","import { basename } from 'node:path';\nimport type { ProjectRule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface NoDuplicateNumberingOptions extends Record<string, unknown> {\n // No options at v0.1.\n}\n\nconst NUMBER_REGEX = /^(\\d{4})-/;\n\nconst rule: ProjectRule<NoDuplicateNumberingOptions> = {\n meta: {\n name: 'madr/no-duplicate-numbering',\n type: 'project',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description: 'No two ADRs share the same NNNN- number prefix',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/no-duplicate-numbering.md',\n recommended: true,\n },\n messages: {\n duplicateNumber: 'ADR number {{number}} is used by multiple files: {{paths}}',\n },\n defaultOptions: {},\n schema,\n },\n check(context) {\n const byNumber = new Map<string, string[]>();\n for (const file of context.files) {\n const match = NUMBER_REGEX.exec(basename(file.path));\n if (!match) continue;\n const number = match[1];\n if (number === undefined) continue;\n const group = byNumber.get(number) ?? [];\n group.push(file.path);\n byNumber.set(number, group);\n }\n\n for (const [number, paths] of byNumber) {\n if (paths.length > 1) {\n const pathList = paths.join(', ');\n for (const path of paths) {\n context.report({\n messageId: 'duplicateNumber',\n path,\n data: { number, paths: pathList },\n });\n }\n }\n }\n },\n};\n\nexport default rule;\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {}\n}\n","import { basename } from 'node:path';\nimport type { ProjectRule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface NoNumberingGapOptions extends Record<string, unknown> {\n // No options at v0.1.\n}\n\nconst NUMBER_REGEX = /^(\\d{4})-/;\n\nfunction format4(n: number): string {\n return n.toString().padStart(4, '0');\n}\n\nconst rule: ProjectRule<NoNumberingGapOptions> = {\n meta: {\n name: 'madr/no-numbering-gap',\n type: 'project',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description:\n 'Detect gaps in ADR numbering (e.g. 0001 and 0003 exist but 0002 is missing). Convention-only; default off.',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/no-numbering-gap.md',\n recommended: false,\n },\n messages: {\n numberingGap:\n 'Numbering gap: missing {{missing}} between ADR-{{from}} and ADR-{{to}}',\n },\n defaultOptions: {},\n schema,\n },\n check(context) {\n // Map ADR number → file path. Files without a NNNN- prefix are ignored.\n const numberToPath = new Map<number, string>();\n for (const file of context.files) {\n const match = NUMBER_REGEX.exec(basename(file.path));\n if (!match?.[1]) continue;\n const n = Number.parseInt(match[1], 10);\n if (!Number.isNaN(n)) numberToPath.set(n, file.path);\n }\n if (numberToPath.size < 2) return;\n\n const sortedNumbers = Array.from(numberToPath.keys()).toSorted(\n (a, b) => a - b,\n );\n\n for (let i = 1; i < sortedNumbers.length; i++) {\n const prev = sortedNumbers[i - 1];\n const curr = sortedNumbers[i];\n if (prev === undefined || curr === undefined) continue;\n if (curr === prev + 1) continue;\n\n const missingNumbers: string[] = [];\n for (let n = prev + 1; n < curr; n++) {\n missingNumbers.push(format4(n));\n }\n\n const currPath = numberToPath.get(curr);\n if (!currPath) continue;\n\n context.report({\n messageId: 'numberingGap',\n path: currPath,\n data: {\n from: format4(prev),\n to: format4(curr),\n missing: missingNumbers.join(', '),\n },\n });\n }\n },\n};\n\nexport default rule;\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {}\n}\n","import { toString } from 'mdast-util-to-string';\nimport type { Rule, RuleListeners } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface RequiredSectionsOptions extends Record<string, unknown> {\n sections: string[];\n matchMode: 'exact' | 'startsWith';\n}\n\nconst rule: Rule<RequiredSectionsOptions> = {\n meta: {\n name: 'madr/required-sections',\n type: 'perFile',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description: 'Enforce that ADRs contain required heading sections',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/required-sections.md',\n recommended: true,\n },\n messages: {\n missingSection: 'Missing required section: \"{{section}}\"',\n },\n defaultOptions: {\n sections: [\n 'Context and Problem Statement',\n 'Decision Outcome',\n 'Consequences',\n ],\n matchMode: 'exact',\n },\n schema,\n },\n create(context): RuleListeners {\n const seen: string[] = [];\n return {\n enter: {\n heading(node) {\n if (node.type !== 'heading') return;\n seen.push(toString(node).trim());\n },\n },\n exit: {\n root() {\n const { sections, matchMode } = context.options;\n for (const section of sections) {\n const matched = seen.some((heading) =>\n matchMode === 'startsWith'\n ? heading.startsWith(section)\n : heading === section,\n );\n if (!matched) {\n context.report({\n messageId: 'missingSection',\n data: { section, found: [...seen] },\n });\n }\n }\n },\n },\n };\n },\n};\n\nexport default rule;\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"sections\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Required heading texts. Order does not matter.\"\n },\n \"matchMode\": {\n \"type\": \"string\",\n \"enum\": [\"exact\", \"startsWith\"],\n \"description\": \"Comparison mode against heading text.\"\n }\n }\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"values\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Exact-match allowed status values.\"\n },\n \"prefixValues\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"startsWith-match allowed prefixes (e.g. 'superseded by').\"\n },\n \"caseSensitive\": {\n \"type\": \"boolean\",\n \"description\": \"When false, comparisons are case-insensitive.\"\n }\n }\n}\n","import type { Rule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface StatusEnumOptions extends Record<string, unknown> {\n values: string[];\n prefixValues: string[];\n caseSensitive: boolean;\n}\n\nconst rule: Rule<StatusEnumOptions> = {\n meta: {\n name: 'madr/status-enum',\n type: 'perFile',\n versionCompat: ['v2', 'v3', 'v4'],\n docs: {\n description: 'Validate that ADR `status` (frontmatter or v2 bold-list) is in the allowed enum',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/status-enum.md',\n recommended: true,\n },\n messages: {\n missingStatus: 'Metadata does not contain a \"status\" field (checked frontmatter and v2 bold-list)',\n invalidStatus: 'Status \"{{status}}\" is not one of: {{allowed}}',\n },\n defaultOptions: {\n values: ['proposed', 'rejected', 'accepted', 'deprecated'],\n prefixValues: ['superseded by'],\n caseSensitive: false,\n },\n schema,\n },\n create(context) {\n const meta = context.metadata;\n if (!meta || typeof meta.status !== 'string') {\n context.report({ messageId: 'missingStatus', data: {} });\n return;\n }\n\n const { values, prefixValues, caseSensitive } = context.options;\n const status = meta.status;\n const compareStatus = caseSensitive ? status : status.toLowerCase();\n const compareValues = caseSensitive\n ? values\n : values.map((v) => v.toLowerCase());\n const comparePrefixes = caseSensitive\n ? prefixValues\n : prefixValues.map((p) => p.toLowerCase());\n\n const exactMatch = compareValues.includes(compareStatus);\n const prefixMatch = comparePrefixes.some((p) =>\n compareStatus.startsWith(p),\n );\n\n if (!exactMatch && !prefixMatch) {\n const allowed = [\n ...values,\n ...prefixValues.map((p) => `${p} ...`),\n ];\n context.report({\n messageId: 'invalidStatus',\n data: { status, allowed },\n });\n }\n },\n};\n\nexport default rule;\n","import { basename } from 'node:path';\nimport type { ProjectFile, ProjectRule } from '../../core/types.js';\nimport schema from './schema.json' with { type: 'json' };\n\ninterface SupersedesBidirectionalOptions extends Record<string, unknown> {\n // No options at v0.1.\n}\n\nconst ADR_NUMBER_REGEX = /^(\\d{4})-/;\nconst FORWARD_FIELD = 'supersedes';\nconst BACKWARD_FIELD = 'superseded-by';\n\nfunction extractAdrNumber(path: string): string | null {\n const match = ADR_NUMBER_REGEX.exec(basename(path));\n return match?.[1] ?? null;\n}\n\nfunction normalizeRefs(value: unknown): string[] {\n if (typeof value === 'string') return [value];\n if (Array.isArray(value)) {\n return value.filter((v): v is string => typeof v === 'string');\n }\n return [];\n}\n\nconst rule: ProjectRule<SupersedesBidirectionalOptions> = {\n meta: {\n name: 'madr/supersedes-bidirectional',\n type: 'project',\n versionCompat: ['v3', 'v4'],\n docs: {\n description:\n 'Frontmatter `supersedes` and `superseded-by` must reference each other bidirectionally',\n url: 'https://github.com/knktkc/madr-lint/blob/main/docs/rules/supersedes-bidirectional.md',\n recommended: true,\n },\n messages: {\n unknownReference:\n 'Frontmatter `{{direction}}: {{ref}}` references an ADR that does not exist',\n missingBackReference:\n '`{{source}}` declares `{{direction}}: {{ref}}`, but `{{ref}}` (this file) does not back-reference it via `{{expected}}`',\n },\n defaultOptions: {},\n schema,\n },\n check(context) {\n // Build ADR-NNNN → file index. Files without a NNNN- prefix are\n // not addressable via `supersedes`/`superseded-by` and thus skipped.\n const adrIndex = new Map<string, ProjectFile>();\n for (const file of context.files) {\n const num = extractAdrNumber(file.path);\n if (num) adrIndex.set(`ADR-${num}`, file);\n }\n\n // Walk every file with a frontmatter and check both directions.\n for (const file of context.files) {\n const fm = file.frontmatter;\n if (!fm) continue;\n const fileNumber = extractAdrNumber(file.path);\n if (!fileNumber) continue;\n const fileRef = `ADR-${fileNumber}`;\n\n const forwardRefs = normalizeRefs(fm[FORWARD_FIELD]);\n const backwardRefs = normalizeRefs(fm[BACKWARD_FIELD]);\n\n // Forward direction: this file `supersedes` something — verify\n // the target back-references this file via `superseded-by`.\n for (const ref of forwardRefs) {\n const target = adrIndex.get(ref);\n if (!target) {\n context.report({\n messageId: 'unknownReference',\n path: file.path,\n data: { ref, direction: FORWARD_FIELD },\n });\n continue;\n }\n const targetBack = normalizeRefs(target.frontmatter?.[BACKWARD_FIELD]);\n if (!targetBack.includes(fileRef)) {\n context.report({\n messageId: 'missingBackReference',\n path: target.path,\n data: {\n ref,\n source: file.path,\n direction: BACKWARD_FIELD,\n expected: fileRef,\n },\n });\n }\n }\n\n // Backward direction: this file is `superseded-by` something —\n // verify the target forward-references this file via `supersedes`.\n for (const ref of backwardRefs) {\n const target = adrIndex.get(ref);\n if (!target) {\n context.report({\n messageId: 'unknownReference',\n path: file.path,\n data: { ref, direction: BACKWARD_FIELD },\n });\n continue;\n }\n const targetFwd = normalizeRefs(target.frontmatter?.[FORWARD_FIELD]);\n if (!targetFwd.includes(fileRef)) {\n context.report({\n messageId: 'missingBackReference',\n path: target.path,\n data: {\n ref,\n source: file.path,\n direction: FORWARD_FIELD,\n expected: fileRef,\n },\n });\n }\n }\n }\n },\n};\n\nexport default rule;\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {}\n}\n"],"mappings":"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","names":["readFileSync","relative","resolve","sep","existsSync","readFileSync","existsSync","readFileSync","existsSync","join","readFileSync","statSync","resolve","sep","rule","rule","readFileSync","rule","resolve","statSync","rule","basename","schema_default","rule","schema_default","basename","dirname","schema_default","rule","schema_default","dirname","basename","schema_default","rule","schema_default","basename","basename","schema_default","NUMBER_REGEX","rule","schema_default","basename","toString","schema_default","rule","schema_default","toString","schema_default","rule","schema_default","basename","schema_default","basename","rule","schema_default","toPosix","sep","readFileSync","relative","resolve"]}
|