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.
- package/CHANGELOG.md +20 -0
- package/README.md +166 -0
- package/dist/audit/checks/broken_refs.d.ts +3 -0
- package/dist/audit/checks/broken_refs.d.ts.map +1 -0
- package/dist/audit/checks/broken_refs.js +67 -0
- package/dist/audit/checks/broken_refs.js.map +1 -0
- package/dist/audit/checks/staleness.d.ts +3 -0
- package/dist/audit/checks/staleness.d.ts.map +1 -0
- package/dist/audit/checks/staleness.js +114 -0
- package/dist/audit/checks/staleness.js.map +1 -0
- package/dist/audit/collect.d.ts +4 -0
- package/dist/audit/collect.d.ts.map +1 -0
- package/dist/audit/collect.js +134 -0
- package/dist/audit/collect.js.map +1 -0
- package/dist/audit/config.d.ts +4 -0
- package/dist/audit/config.d.ts.map +1 -0
- package/dist/audit/config.js +174 -0
- package/dist/audit/config.js.map +1 -0
- package/dist/audit/exit.d.ts +6 -0
- package/dist/audit/exit.d.ts.map +1 -0
- package/dist/audit/exit.js +8 -0
- package/dist/audit/exit.js.map +1 -0
- package/dist/audit/index.d.ts +2 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +93 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/links.d.ts +4 -0
- package/dist/audit/links.d.ts.map +1 -0
- package/dist/audit/links.js +156 -0
- package/dist/audit/links.js.map +1 -0
- package/dist/audit/report.d.ts +4 -0
- package/dist/audit/report.d.ts.map +1 -0
- package/dist/audit/report.js +58 -0
- package/dist/audit/report.js.map +1 -0
- package/dist/audit/types.d.ts +94 -0
- package/dist/audit/types.d.ts.map +1 -0
- package/dist/audit/types.js +11 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- 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 @@
|
|
|
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 @@
|
|
|
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":"AAAA,gCAAgC;AAChC,2EAA2E;AAC3E,0EAA0E;AAC1E,uDAAuD;AAKvD,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,IAAY,EAAW,EAAE,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;AAEvE,SAAS,eAAe,CAAC,QAAgB,EAAE,GAAS,EAAE,aAAqB;IACzE,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,OAAO,EAAE,GAAG,aAAa,GAAG,UAAU,CAAC;AACzC,CAAC;AAID,MAAM,UAAU,cAAc,CAC5B,SAAyB,EACzB,KAAiB,EACjB,aAAqB,EACrB,GAAS;IAIT,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;gBAC1C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;gBACtB,IAAI,EAAE,CAAC,CAAC,OAAO;gBACf,KAAK,EAAE,CAAC,CAAC,KAAK;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,yEAAyE;QACzE,qDAAqD;QACrD,IAAI,MAAM,GAAG,GAAG,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC;YAC1D,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,GAAG,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,SAAS,CAAC,yCAAyC;QAC3E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,kCAAkC;IAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAW,CAAC;IAClC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,aAAa,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,kEAAkE;IAClE,mEAAmE;IACnE,qBAAqB;IACrB,MAAM,aAAa,GAAG,IAAI,GAAG,EAAyB,CAAC,CAAC,uBAAuB;IAE/E,SAAS,QAAQ,CAAC,IAAa;QAC7B,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,yBAAyB;QAC5D,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QACpE,MAAM,IAAI,GAAG,IAAI,GAAG,EAA2B,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrB,MAAM,KAAK,GAAc,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,MAAM,GAAmB,IAAI,CAAC;QAClC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAa,CAAC;YACrC,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,GAAG,GAAG,CAAC;gBACb,MAAM;YACR,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;gBACtC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAmB,MAAM,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAC1B,CAAC;QACD,MAAM,KAAK,GAAU,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAClC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAa,CAAC;YACtC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACjE,CAAC,CAAC,CAAC;QACH,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,KAAK,EAAE,CAAC;YACV,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,KAAK;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -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"}
|