daftari 1.12.6 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +166 -0
  3. package/dist/audit/checks/broken_refs.d.ts +3 -0
  4. package/dist/audit/checks/broken_refs.d.ts.map +1 -0
  5. package/dist/audit/checks/broken_refs.js +67 -0
  6. package/dist/audit/checks/broken_refs.js.map +1 -0
  7. package/dist/audit/checks/staleness.d.ts +3 -0
  8. package/dist/audit/checks/staleness.d.ts.map +1 -0
  9. package/dist/audit/checks/staleness.js +114 -0
  10. package/dist/audit/checks/staleness.js.map +1 -0
  11. package/dist/audit/collect.d.ts +4 -0
  12. package/dist/audit/collect.d.ts.map +1 -0
  13. package/dist/audit/collect.js +134 -0
  14. package/dist/audit/collect.js.map +1 -0
  15. package/dist/audit/config.d.ts +4 -0
  16. package/dist/audit/config.d.ts.map +1 -0
  17. package/dist/audit/config.js +174 -0
  18. package/dist/audit/config.js.map +1 -0
  19. package/dist/audit/exit.d.ts +6 -0
  20. package/dist/audit/exit.d.ts.map +1 -0
  21. package/dist/audit/exit.js +8 -0
  22. package/dist/audit/exit.js.map +1 -0
  23. package/dist/audit/index.d.ts +2 -0
  24. package/dist/audit/index.d.ts.map +1 -0
  25. package/dist/audit/index.js +93 -0
  26. package/dist/audit/index.js.map +1 -0
  27. package/dist/audit/links.d.ts +4 -0
  28. package/dist/audit/links.d.ts.map +1 -0
  29. package/dist/audit/links.js +156 -0
  30. package/dist/audit/links.js.map +1 -0
  31. package/dist/audit/report.d.ts +4 -0
  32. package/dist/audit/report.d.ts.map +1 -0
  33. package/dist/audit/report.js +58 -0
  34. package/dist/audit/report.js.map +1 -0
  35. package/dist/audit/types.d.ts +94 -0
  36. package/dist/audit/types.d.ts.map +1 -0
  37. package/dist/audit/types.js +11 -0
  38. package/dist/audit/types.js.map +1 -0
  39. package/dist/cli.d.ts.map +1 -1
  40. package/dist/cli.js +6 -0
  41. package/dist/cli.js.map +1 -1
  42. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.13.1] - 2026-05-30
11
+
12
+ ### Changed
13
+
14
+ - Expanded README `Coherence audit` section: multi-repo case promoted to the
15
+ headline, sample output added, transitive staleness defined in plain
16
+ language, GitHub Actions CI snippet added, exit-code table added, CLI flags
17
+ documented separately from `audit.yaml`. No code changes — docs only.
18
+
19
+ ## [1.13.0] - 2026-05-30
20
+
21
+ ### Added
22
+
23
+ - `daftari audit` CLI subcommand. Scans N markdown repos and reports broken
24
+ cross-repo references and link-graph transitive staleness. Outputs markdown
25
+ (default: stdout) and optional JSON. Exit code 1 if `fail_on.broken_refs` or
26
+ `fail_on.transitive_staleness` thresholds are exceeded. Anonymous repos passed
27
+ via `--repo` get no URL patterns — URL-based cross-refs into them aren't
28
+ detected; use `--config` with an `urls:` block to enable them. See issue #85.
29
+
10
30
  ## [1.12.6] - 2026-05-27
11
31
 
12
32
  ### Changed
package/README.md CHANGED
@@ -164,6 +164,165 @@ Deliberately deferred to keep the surface tight:
164
164
 
165
165
  Each is a clean increment on a surface that already works.
166
166
 
167
+ ## Coherence audit
168
+
169
+ `daftari audit` is a read-only, deterministic check across one or more
170
+ markdown repos for **broken cross-repo references** and **link-graph
171
+ transitive staleness**. It works against any markdown tree — daftari-managed
172
+ or not. The audit creates no `.daftari/` directory and writes nothing to the
173
+ audited repos.
174
+
175
+ ### Multi-repo (the headline use case)
176
+
177
+ When two or more repos link to each other, the audit detects broken
178
+ references that neither repo's own lint could see — because each repo only
179
+ knows about itself.
180
+
181
+ ```bash
182
+ daftari audit \
183
+ --repo ~/repos/service-a \
184
+ --repo ~/repos/service-b
185
+ ```
186
+
187
+ That works for relative-path links (`../service-b/docs/api.md`). For
188
+ GitHub-style URL links between repos (`https://github.com/org/service-b/...`),
189
+ declare each repo's URL patterns in an `audit.yaml` so the resolver can map
190
+ them back to the local repo:
191
+
192
+ ```yaml
193
+ # audit.yaml
194
+ repos:
195
+ - name: service-a
196
+ path: ~/repos/service-a
197
+ urls: ["github.com/org/service-a"]
198
+ - name: service-b
199
+ path: ~/repos/service-b
200
+ urls: ["github.com/org/service-b"]
201
+ ```
202
+
203
+ ```bash
204
+ daftari audit --config audit.yaml
205
+ ```
206
+
207
+ ### Single repo
208
+
209
+ The same command, one `--repo`:
210
+
211
+ ```bash
212
+ daftari audit --repo ./docs
213
+ ```
214
+
215
+ In single-repo mode the cross-repo check trivially has no work, but the
216
+ staleness check still runs over the in-repo link graph.
217
+
218
+ ### What gets detected
219
+
220
+ - **Missing files.** A link from `service-a/intro.md` to `../service-b/api.md`
221
+ or `https://github.com/org/service-b/blob/main/api.md` — flagged if `api.md`
222
+ doesn't exist in `service-b`.
223
+ - **Missing anchors.** Same link with `#run` — flagged if `api.md` has no
224
+ `## Run` heading.
225
+ - **Direct staleness.** Any doc whose git mtime is older than
226
+ `staleness.threshold_days` (default 540, ~18 months).
227
+ - **Transitive staleness.** A fresh doc that links — directly or through a
228
+ chain — to a stale doc is itself flagged, with the shortest chain reported.
229
+ Catches the case where you keep touching an index page while the docs it
230
+ links to are rotting.
231
+
232
+ ### Sample output
233
+
234
+ ```markdown
235
+ # Coherence Audit Report
236
+
237
+ ## Totals
238
+ - repos scanned: **2**
239
+ - docs scanned: **47**
240
+ - broken cross-repo refs: **2**
241
+ - directly stale docs: **3**
242
+ - transitively stale docs: **5**
243
+
244
+ ## Broken cross-repo references
245
+ | kind | source | target | href |
246
+ |----------------|---------------------------|-------------------------|------|
247
+ | missing_anchor | service-a/intro.md | service-b/api.md#run | `https://github.com/org/service-b/blob/main/api.md#run` |
248
+ | missing_file | service-a/architecture.md | service-b/deleted.md | `../service-b/deleted.md` |
249
+
250
+ ## Staleness
251
+ | kind | doc | mtime | chain |
252
+ |------------|--------------------------|------------|-------|
253
+ | transitive | service-a/onboarding.md | 2026-04-01 | service-a/onboarding.md → service-b/legacy-flow.md |
254
+ ```
255
+
256
+ JSON output (`--output-json` or `output.json` in config) carries the same
257
+ structure with full detail in `brokenRefs[]` and `staleness[]` arrays plus a
258
+ `totals` summary block for compact downstream rendering.
259
+
260
+ ### CI integration
261
+
262
+ The audit's exit code is designed to gate CI:
263
+
264
+ ```yaml
265
+ # .github/workflows/docs-audit.yml
266
+ name: Docs audit
267
+ on: [pull_request]
268
+ jobs:
269
+ audit:
270
+ runs-on: ubuntu-latest
271
+ steps:
272
+ - uses: actions/checkout@v4
273
+ with: { fetch-depth: 0 } # full history so git mtime works
274
+ - run: npx daftari@latest audit --config audit.yaml
275
+ ```
276
+
277
+ Exit codes:
278
+
279
+ | code | meaning |
280
+ |------|---------|
281
+ | `0` | clean run, all findings within `fail_on` thresholds |
282
+ | `1` | clean run but a threshold was exceeded — CI fails |
283
+ | `2` | config error (missing required fields, bad paths, malformed YAML) |
284
+ | `3` | runtime error (IO failure during collection) |
285
+
286
+ ### CLI flags
287
+
288
+ `audit.yaml` and CLI flags overlap; CLI wins. A warning is printed to stderr
289
+ when `--output` or `--output-json` displaces a value from the config.
290
+
291
+ - `--repo <path>` — add a repo. May be repeated. Anonymous CLI repos get no
292
+ URL patterns; URL-based cross-refs into them won't be detected. Use
293
+ `--config` for URL-aware repos.
294
+ - `--config <path>` — load an `audit.yaml`.
295
+ - `--output <md>` — markdown report destination (default: stdout).
296
+ - `--output-json <json>` — JSON report destination (default: not written).
297
+ - `--help` — full help text.
298
+
299
+ ### Full `audit.yaml` schema
300
+
301
+ ```yaml
302
+ repos:
303
+ - name: service-a
304
+ path: ~/repos/service-a
305
+ docs_glob: "docs/**/*.md" # default: "**/*.md"
306
+ urls: # optional; enables URL-pattern matching
307
+ - "github.com/org/service-a"
308
+
309
+ - name: service-b
310
+ path: ~/repos/service-b
311
+ urls:
312
+ - "github.com/org/service-b"
313
+
314
+ output:
315
+ markdown: coherence-report.md # default: stdout
316
+ json: coherence-report.json # default: not emitted
317
+
318
+ staleness:
319
+ threshold_days: 540 # default: 540 (18 months)
320
+
321
+ fail_on:
322
+ broken_refs: 1 # default: fail on any broken ref
323
+ transitive_staleness: 100 # default: generous; teams tune
324
+ ```
325
+
167
326
  ## Development
168
327
 
169
328
  ```
@@ -181,6 +340,13 @@ Design tenets: functions and types, no classes; tool handlers return
181
340
  - <docs/architecture.md> — layered design, request path, accumulation vs. generative domains
182
341
  - <docs/file-format.md> — complete frontmatter reference
183
342
 
343
+ ## Integrations
344
+
345
+ - [`integrations/langchain/`](integrations/langchain/) — `langchain-daftari`, a
346
+ Python package that exposes the 14 daftari tools as LangChain `BaseTool`s
347
+ for use with LangGraph / `create_react_agent`. Sync + async, schemas pulled
348
+ live from `tools/list`.
349
+
184
350
  ## Privacy
185
351
 
186
352
  Daftari is a local MCP server. It runs on your machine, against vault files on
@@ -0,0 +1,3 @@
1
+ import type { BrokenRefFinding, LinkEdge, RepoSnapshot } from "../types.js";
2
+ export declare function checkBrokenRefs(snapshots: RepoSnapshot[], edges: LinkEdge[]): BrokenRefFinding[];
3
+ //# sourceMappingURL=broken_refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broken_refs.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,gBAAgB,EAAE,CA0DhG"}
@@ -0,0 +1,67 @@
1
+ // src/audit/checks/broken_refs.ts
2
+ // Pure function: given snapshots and classified edges, return every broken
3
+ // reference as a BrokenRefFinding. No throws. No classes.
4
+ //
5
+ // Resolution logic:
6
+ // 1. Look up target repo by name; if not found → missing_file.
7
+ // 2. Look up targetPath in that repo's docs Map.
8
+ // 3. If not found and targetPath lacks a ".md" extension, try targetPath +
9
+ // ".md" (spec §7 bare-path fallback).
10
+ // 4. If still not found → missing_file.
11
+ // 5. If found and targetAnchor is set: check DocSnapshot.headings. If absent
12
+ // → missing_anchor.
13
+ export function checkBrokenRefs(snapshots, edges) {
14
+ // Index snapshots by repo name for O(1) lookup.
15
+ const byRepo = new Map();
16
+ for (const snap of snapshots) {
17
+ byRepo.set(snap.config.name, snap);
18
+ }
19
+ const findings = [];
20
+ for (const edge of edges) {
21
+ const targetSnap = byRepo.get(edge.targetRepo);
22
+ if (!targetSnap) {
23
+ // Target repo itself is unknown — treat as missing file.
24
+ findings.push({
25
+ kind: "missing_file",
26
+ source: { repo: edge.sourceRepo, path: edge.sourcePath },
27
+ target: { repo: edge.targetRepo, path: edge.targetPath, anchor: edge.targetAnchor },
28
+ rawHref: edge.rawHref,
29
+ });
30
+ continue;
31
+ }
32
+ // Resolve the target doc: exact match, then .md fallback.
33
+ let resolvedPath = null;
34
+ if (targetSnap.docs.has(edge.targetPath)) {
35
+ resolvedPath = edge.targetPath;
36
+ }
37
+ else if (!edge.targetPath.endsWith(".md")) {
38
+ const withMd = `${edge.targetPath}.md`;
39
+ if (targetSnap.docs.has(withMd)) {
40
+ resolvedPath = withMd;
41
+ }
42
+ }
43
+ if (resolvedPath === null) {
44
+ findings.push({
45
+ kind: "missing_file",
46
+ source: { repo: edge.sourceRepo, path: edge.sourcePath },
47
+ target: { repo: edge.targetRepo, path: edge.targetPath, anchor: edge.targetAnchor },
48
+ rawHref: edge.rawHref,
49
+ });
50
+ continue;
51
+ }
52
+ // File found. Check anchor if present.
53
+ if (edge.targetAnchor !== null) {
54
+ const doc = targetSnap.docs.get(resolvedPath);
55
+ if (doc && !doc.headings.has(edge.targetAnchor)) {
56
+ findings.push({
57
+ kind: "missing_anchor",
58
+ source: { repo: edge.sourceRepo, path: edge.sourcePath },
59
+ target: { repo: edge.targetRepo, path: resolvedPath, anchor: edge.targetAnchor },
60
+ rawHref: edge.rawHref,
61
+ });
62
+ }
63
+ }
64
+ }
65
+ return findings;
66
+ }
67
+ //# sourceMappingURL=broken_refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broken_refs.js","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { LinkEdge, RepoSnapshot, StalenessFinding } from "../types.js";
2
+ export declare function checkStaleness(snapshots: RepoSnapshot[], edges: LinkEdge[], thresholdDays: number, now: Date): StalenessFinding[];
3
+ //# sourceMappingURL=staleness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/staleness.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAY5E,wBAAgB,cAAc,CAC5B,SAAS,EAAE,YAAY,EAAE,EACzB,KAAK,EAAE,QAAQ,EAAE,EACjB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,IAAI,GACR,gBAAgB,EAAE,CAsGpB"}
@@ -0,0 +1,114 @@
1
+ // src/audit/checks/staleness.ts
2
+ // Memoized BFS from each fresh doc to the nearest directly-stale leaf. BFS
3
+ // (not DFS) so the recorded chain is the shortest path. Cycles handled by
4
+ // the BFS visited set; verdicts memoized across roots.
5
+ const key = (repo, path) => `${repo} ${path}`;
6
+ function isDirectlyStale(mtimeIso, now, thresholdDays) {
7
+ const ms = now.getTime() - new Date(mtimeIso).getTime();
8
+ return ms > thresholdDays * 86_400_000;
9
+ }
10
+ export function checkStaleness(snapshots, edges, thresholdDays, now) {
11
+ const nodes = new Map();
12
+ for (const snap of snapshots) {
13
+ for (const d of snap.docs.values()) {
14
+ nodes.set(key(snap.config.name, d.relPath), {
15
+ repo: snap.config.name,
16
+ path: d.relPath,
17
+ mtime: d.mtime,
18
+ });
19
+ }
20
+ }
21
+ const adj = new Map();
22
+ for (const edge of edges) {
23
+ const src = key(edge.sourceRepo, edge.sourcePath);
24
+ const dst = key(edge.targetRepo, edge.targetPath);
25
+ // Resolve the dst against the .md-extension fallback so transitive edges
26
+ // see the same node identity broken-refs check does.
27
+ let dstKey = dst;
28
+ if (!nodes.has(dstKey)) {
29
+ const alt = key(edge.targetRepo, `${edge.targetPath}.md`);
30
+ if (nodes.has(alt))
31
+ dstKey = alt;
32
+ }
33
+ if (!nodes.has(dstKey))
34
+ continue; // dangling edge — broken_refs handles it
35
+ if (!nodes.has(src))
36
+ continue;
37
+ const list = adj.get(src) ?? [];
38
+ list.push(dstKey);
39
+ adj.set(src, list);
40
+ }
41
+ // Classify directly stale leaves.
42
+ const direct = new Set();
43
+ for (const [k, info] of nodes) {
44
+ if (isDirectlyStale(info.mtime, now, thresholdDays))
45
+ direct.add(k);
46
+ }
47
+ // BFS from each fresh root to find shortest path to a stale node.
48
+ // Memoize the shortest chain per node so a second root reaching it
49
+ // doesn't recompute.
50
+ const shortestChain = new Map(); // null = no stale path
51
+ function chainFor(root) {
52
+ if (direct.has(root))
53
+ return null; // direct, not transitive
54
+ if (shortestChain.has(root))
55
+ return shortestChain.get(root) ?? null;
56
+ const prev = new Map();
57
+ prev.set(root, null);
58
+ const queue = [root];
59
+ let target = null;
60
+ while (queue.length > 0) {
61
+ const cur = queue.shift();
62
+ if (cur !== root && direct.has(cur)) {
63
+ target = cur;
64
+ break;
65
+ }
66
+ for (const next of adj.get(cur) ?? []) {
67
+ if (prev.has(next))
68
+ continue;
69
+ prev.set(next, cur);
70
+ queue.push(next);
71
+ }
72
+ }
73
+ if (!target) {
74
+ shortestChain.set(root, null);
75
+ return null;
76
+ }
77
+ const path = [];
78
+ let n = target;
79
+ while (n) {
80
+ path.unshift(n);
81
+ n = prev.get(n) ?? null;
82
+ }
83
+ const chain = path.map((k) => {
84
+ const info = nodes.get(k);
85
+ return { repo: info.repo, path: info.path, mtime: info.mtime };
86
+ });
87
+ shortestChain.set(root, chain);
88
+ return chain;
89
+ }
90
+ const findings = [];
91
+ for (const [k, info] of nodes) {
92
+ if (direct.has(k)) {
93
+ findings.push({
94
+ kind: "direct",
95
+ repo: info.repo,
96
+ path: info.path,
97
+ mtime: info.mtime,
98
+ });
99
+ continue;
100
+ }
101
+ const chain = chainFor(k);
102
+ if (chain) {
103
+ findings.push({
104
+ kind: "transitive",
105
+ repo: info.repo,
106
+ path: info.path,
107
+ mtime: info.mtime,
108
+ staleChain: chain,
109
+ });
110
+ }
111
+ }
112
+ return findings;
113
+ }
114
+ //# sourceMappingURL=staleness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.js","sourceRoot":"","sources":["../../../src/audit/checks/staleness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Result } from "../frontmatter/types.js";
2
+ import type { AuditConfig, AuditError, RepoSnapshot } from "./types.js";
3
+ export declare function collectRepos(config: AuditConfig): Promise<Result<RepoSnapshot[], AuditError>>;
4
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;AAoHjG,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC,CAa7C"}
@@ -0,0 +1,134 @@
1
+ // src/audit/collect.ts
2
+ // The only IO stage of the audit pipeline. Per repo: glob docs, strip
3
+ // frontmatter, extract headings (GitHub-slugged) and links, then batch
4
+ // git log to populate mtimes; on any git failure, fall back to fs mtime.
5
+ import { execFileSync } from "node:child_process";
6
+ import { statSync } from "node:fs";
7
+ import { readFile } from "node:fs/promises";
8
+ import { resolve as nodeResolve } from "node:path";
9
+ import { glob } from "glob";
10
+ import matter from "gray-matter";
11
+ import { err, ok } from "../frontmatter/types.js";
12
+ import { extractLinksFromBody } from "./links.js";
13
+ import { runtimeError } from "./types.js";
14
+ function slugify(heading) {
15
+ // GitHub slug: lowercase, strip non-alphanumeric (keep `-_`), whitespace -> `-`.
16
+ return heading
17
+ .toLowerCase()
18
+ .replace(/\s+/g, "-")
19
+ .replace(/[^a-z0-9\-_]/g, "")
20
+ .replace(/-+/g, "-")
21
+ .replace(/^-+|-+$/g, "");
22
+ }
23
+ function extractHeadings(body) {
24
+ const out = new Set();
25
+ for (const line of body.split(/\r?\n/)) {
26
+ const m = line.match(/^#{1,6}\s+(.+?)\s*#*\s*$/);
27
+ if (m)
28
+ out.add(slugify(m[1]));
29
+ }
30
+ return out;
31
+ }
32
+ function gitMtimes(repoPath, docsGlob) {
33
+ const opts = {
34
+ cwd: repoPath,
35
+ maxBuffer: 256 * 1024 * 1024,
36
+ stdio: ["ignore", "pipe", "ignore"],
37
+ };
38
+ try {
39
+ execFileSync("git", ["rev-parse", "--is-inside-work-tree"], opts);
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ let out;
45
+ try {
46
+ // Use the :(glob) pathspec magic so git's internal glob matcher handles
47
+ // "**/*.md" correctly on all platforms and git configurations, including
48
+ // matching top-level files (bare "**/*.md" without the magic prefix skips
49
+ // files at depth 0 on some git versions).
50
+ const pathspec = docsGlob.startsWith(":(") ? docsGlob : `:(glob)${docsGlob}`;
51
+ out = execFileSync("git", ["log", "--all", "--name-only", `--pretty=format:COMMIT %aI`, "--", pathspec], opts).toString();
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ const mtimes = new Map();
57
+ let currentIso = null;
58
+ for (const line of out.split("\n")) {
59
+ if (line.startsWith("COMMIT ")) {
60
+ currentIso = line.slice("COMMIT ".length).trim() || null;
61
+ continue;
62
+ }
63
+ const path = line.trim();
64
+ if (!path || !currentIso)
65
+ continue;
66
+ // First time we see a path is its newest commit (git log is newest-first).
67
+ if (!mtimes.has(path))
68
+ mtimes.set(path, currentIso);
69
+ }
70
+ return mtimes;
71
+ }
72
+ async function loadDoc(repoPath, relPath, mtimeFromGit) {
73
+ const absPath = nodeResolve(repoPath, relPath);
74
+ const text = await readFile(absPath, "utf-8");
75
+ const parsed = matter(text);
76
+ const body = parsed.content;
77
+ let mtime;
78
+ let mtimeSource;
79
+ if (mtimeFromGit) {
80
+ mtime = mtimeFromGit;
81
+ mtimeSource = "git";
82
+ }
83
+ else {
84
+ mtime = statSync(absPath).mtime.toISOString();
85
+ mtimeSource = "fs";
86
+ }
87
+ return {
88
+ relPath: relPath.split(/[\\/]/).join("/"),
89
+ absPath,
90
+ mtime,
91
+ mtimeSource,
92
+ headings: extractHeadings(body),
93
+ links: extractLinksFromBody(body),
94
+ };
95
+ }
96
+ async function collectOne(config) {
97
+ const files = await glob(config.docsGlob, {
98
+ cwd: config.path,
99
+ nodir: true,
100
+ posix: true,
101
+ dot: false,
102
+ });
103
+ const onlyMd = files.filter((f) => /\.(md|markdown)$/i.test(f));
104
+ const mtimes = gitMtimes(config.path, config.docsGlob);
105
+ const docs = new Map();
106
+ for (const rel of onlyMd) {
107
+ const posixRel = rel.split(/[\\/]/).join("/");
108
+ try {
109
+ const snap = await loadDoc(config.path, posixRel, mtimes?.get(posixRel));
110
+ docs.set(posixRel, snap);
111
+ }
112
+ catch (e) {
113
+ const msg = e instanceof Error ? e.message : String(e);
114
+ process.stderr.write(`daftari audit: warning: unreadable doc ${posixRel}: ${msg}\n`);
115
+ }
116
+ }
117
+ return { config, docs };
118
+ }
119
+ export async function collectRepos(config) {
120
+ // Sequential per-repo; concurrency would help but is out of scope until the
121
+ // 30s budget gets squeezed (see plan §perf).
122
+ const out = [];
123
+ for (const r of config.repos) {
124
+ try {
125
+ out.push(await collectOne(r));
126
+ }
127
+ catch (e) {
128
+ const reason = e instanceof Error ? e.message : String(e);
129
+ return err(runtimeError(`collect failed for repo ${r.name}: ${reason}`));
130
+ }
131
+ }
132
+ return ok(out);
133
+ }
134
+ //# sourceMappingURL=collect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Result } from "../frontmatter/types.js";
2
+ import { type AuditConfig, type AuditError } from "./types.js";
3
+ export declare function parseAuditConfig(argv: string[], yamlLoader?: (path: string) => string): Result<AuditConfig, AuditError>;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/audit/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAgC,MAAM,YAAY,CAAC;AAsG7F,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACpC,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAwFjC"}