@isaacriehm/cairn-state 0.22.5
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/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/alignment-pending.d.ts +28 -0
- package/dist/alignment-pending.js +83 -0
- package/dist/alignment-pending.js.map +1 -0
- package/dist/anchor-map.d.ts +14 -0
- package/dist/anchor-map.js +56 -0
- package/dist/anchor-map.js.map +1 -0
- package/dist/archive.d.ts +48 -0
- package/dist/archive.js +96 -0
- package/dist/archive.js.map +1 -0
- package/dist/cache.d.ts +48 -0
- package/dist/cache.js +241 -0
- package/dist/cache.js.map +1 -0
- package/dist/component-registry.d.ts +93 -0
- package/dist/component-registry.js +0 -0
- package/dist/component-registry.js.map +1 -0
- package/dist/components.d.ts +192 -0
- package/dist/components.js +603 -0
- package/dist/components.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/drift.d.ts +8 -0
- package/dist/drift.js +23 -0
- package/dist/drift.js.map +1 -0
- package/dist/file-candidates-map.d.ts +23 -0
- package/dist/file-candidates-map.js +76 -0
- package/dist/file-candidates-map.js.map +1 -0
- package/dist/frontmatter.d.ts +32 -0
- package/dist/frontmatter.js +77 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/fs.d.ts +36 -0
- package/dist/fs.js +47 -0
- package/dist/fs.js.map +1 -0
- package/dist/glob.d.ts +10 -0
- package/dist/glob.js +46 -0
- package/dist/glob.js.map +1 -0
- package/dist/home.d.ts +69 -0
- package/dist/home.js +168 -0
- package/dist/home.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/languages.d.ts +113 -0
- package/dist/languages.js +512 -0
- package/dist/languages.js.map +1 -0
- package/dist/ledgers.d.ts +14 -0
- package/dist/ledgers.js +105 -0
- package/dist/ledgers.js.map +1 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.js +17 -0
- package/dist/logger.js.map +1 -0
- package/dist/manifest.d.ts +10 -0
- package/dist/manifest.js +84 -0
- package/dist/manifest.js.map +1 -0
- package/dist/missions.d.ts +119 -0
- package/dist/missions.js +414 -0
- package/dist/missions.js.map +1 -0
- package/dist/paths.d.ts +117 -0
- package/dist/paths.js +241 -0
- package/dist/paths.js.map +1 -0
- package/dist/quality-grades.d.ts +11 -0
- package/dist/quality-grades.js +100 -0
- package/dist/quality-grades.js.map +1 -0
- package/dist/rejected.d.ts +42 -0
- package/dist/rejected.js +100 -0
- package/dist/rejected.js.map +1 -0
- package/dist/schemas.d.ts +789 -0
- package/dist/schemas.js +506 -0
- package/dist/schemas.js.map +1 -0
- package/dist/scope-index.d.ts +96 -0
- package/dist/scope-index.js +299 -0
- package/dist/scope-index.js.map +1 -0
- package/dist/slug.d.ts +81 -0
- package/dist/slug.js +138 -0
- package/dist/slug.js.map +1 -0
- package/dist/sot-bindings.d.ts +14 -0
- package/dist/sot-bindings.js +79 -0
- package/dist/sot-bindings.js.map +1 -0
- package/dist/sot-cache.d.ts +18 -0
- package/dist/sot-cache.js +62 -0
- package/dist/sot-cache.js.map +1 -0
- package/dist/text.d.ts +27 -0
- package/dist/text.js +63 -0
- package/dist/text.js.map +1 -0
- package/dist/topic-index.d.ts +27 -0
- package/dist/topic-index.js +82 -0
- package/dist/topic-index.js.map +1 -0
- package/dist/walk.d.ts +7 -0
- package/dist/walk.js +34 -0
- package/dist/walk.js.map +1 -0
- package/package.json +35 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level `.cairn/config.yaml` reader for the non-component sections.
|
|
3
|
+
*
|
|
4
|
+
* (The `components:` block has its own typed loader in components.ts.)
|
|
5
|
+
* Kept deliberately small + tolerant: a missing or malformed config is
|
|
6
|
+
* never fatal — callers get the documented defaults.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { parse as parseYaml } from "yaml";
|
|
10
|
+
import { cairnDir } from "./home.js";
|
|
11
|
+
/** Parse `.cairn/config.yaml` into a plain object ({} when absent/broken). */
|
|
12
|
+
export function loadCairnConfig(repoRoot) {
|
|
13
|
+
const p = cairnDir(repoRoot, "config.yaml");
|
|
14
|
+
if (!existsSync(p))
|
|
15
|
+
return {};
|
|
16
|
+
try {
|
|
17
|
+
const doc = parseYaml(readFileSync(p, "utf8"));
|
|
18
|
+
return typeof doc === "object" && doc !== null
|
|
19
|
+
? doc
|
|
20
|
+
: {};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/C,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YAC5C,CAAC,CAAE,GAA+B;YAClC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/dist/drift.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DriftEvent } from "./schemas.js";
|
|
2
|
+
export interface DriftSnapshot {
|
|
3
|
+
generated: string;
|
|
4
|
+
/** Open drift events at the time of the snapshot. */
|
|
5
|
+
events: DriftEvent[];
|
|
6
|
+
}
|
|
7
|
+
export declare function recordDriftEvent(repoRoot: string, event: DriftEvent): void;
|
|
8
|
+
export declare function writeDriftSnapshot(repoRoot: string, events: DriftEvent[]): string;
|
package/dist/drift.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { getLogger } from "./logger.js";
|
|
3
|
+
import { stalenessCurrentPath, stalenessDir, stalenessLogPath } from "./paths.js";
|
|
4
|
+
import { DriftEvent } from "./schemas.js";
|
|
5
|
+
const log = getLogger();
|
|
6
|
+
export function recordDriftEvent(repoRoot, event) {
|
|
7
|
+
const validated = DriftEvent.parse(event);
|
|
8
|
+
mkdirSync(stalenessDir(repoRoot), { recursive: true });
|
|
9
|
+
const line = `${JSON.stringify(validated)}\n`;
|
|
10
|
+
appendFileSync(stalenessLogPath(repoRoot), line, "utf8");
|
|
11
|
+
log.info({ kind: validated.kind, path: validated.path, severity: validated.severity }, "drift");
|
|
12
|
+
}
|
|
13
|
+
export function writeDriftSnapshot(repoRoot, events) {
|
|
14
|
+
mkdirSync(stalenessDir(repoRoot), { recursive: true });
|
|
15
|
+
const snapshot = {
|
|
16
|
+
generated: new Date().toISOString(),
|
|
17
|
+
events,
|
|
18
|
+
};
|
|
19
|
+
const path = stalenessCurrentPath(repoRoot);
|
|
20
|
+
writeFileSync(path, JSON.stringify(snapshot, null, 2), "utf8");
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../src/drift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;AAQxB,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,KAAiB;IAClE,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1C,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC;IAC9C,cAAc,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACzD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,MAAoB;IACvE,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAkB;QAC9B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM;KACP,CAAC;IACF,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC5C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.cairn/ground/file-candidates-map.yaml` — per-file count of
|
|
3
|
+
* topic-index entries with `dec_id IS NULL`.
|
|
4
|
+
*
|
|
5
|
+
* Built (and rebuilt) anywhere `topic-index.yaml` is written. The
|
|
6
|
+
* read-enrich PostToolUse hook on `Read` consults this map per file
|
|
7
|
+
* touched by the agent — `O(1)` lookup avoids re-walking the topic
|
|
8
|
+
* index on every read. When an entry has its `dec_id` stamped
|
|
9
|
+
* (phase 6 emit, PR 2 `cairn_propose_decision`), the index writer is
|
|
10
|
+
* responsible for refreshing this file too — that's why the helper is
|
|
11
|
+
* a pure pair of `(topicIndex) → FileCandidatesMap` plus a writer.
|
|
12
|
+
*/
|
|
13
|
+
import { FileCandidatesMap, type TopicIndex } from "./schemas.js";
|
|
14
|
+
export declare function emptyFileCandidatesMap(): FileCandidatesMap;
|
|
15
|
+
/**
|
|
16
|
+
* Compute the per-file candidate count by walking the topic-index.
|
|
17
|
+
* Each entry without a `dec_id` contributes 1 to its `sot_source`
|
|
18
|
+
* bucket. Files with zero unpromoted candidates are omitted from the
|
|
19
|
+
* map (so `Map.has(file)` is the gate, not a zero-check).
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildFileCandidatesMap(topicIndex: TopicIndex): FileCandidatesMap;
|
|
22
|
+
export declare function readFileCandidatesMap(repoRoot: string): FileCandidatesMap;
|
|
23
|
+
export declare function writeFileCandidatesMap(repoRoot: string, topicIndex: TopicIndex): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.cairn/ground/file-candidates-map.yaml` — per-file count of
|
|
3
|
+
* topic-index entries with `dec_id IS NULL`.
|
|
4
|
+
*
|
|
5
|
+
* Built (and rebuilt) anywhere `topic-index.yaml` is written. The
|
|
6
|
+
* read-enrich PostToolUse hook on `Read` consults this map per file
|
|
7
|
+
* touched by the agent — `O(1)` lookup avoids re-walking the topic
|
|
8
|
+
* index on every read. When an entry has its `dec_id` stamped
|
|
9
|
+
* (phase 6 emit, PR 2 `cairn_propose_decision`), the index writer is
|
|
10
|
+
* responsible for refreshing this file too — that's why the helper is
|
|
11
|
+
* a pure pair of `(topicIndex) → FileCandidatesMap` plus a writer.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
15
|
+
import { writeFileSafe } from "./fs.js";
|
|
16
|
+
import { getLogger } from "./logger.js";
|
|
17
|
+
import { fileCandidatesMapPath } from "./paths.js";
|
|
18
|
+
import { FileCandidatesMap, } from "./schemas.js";
|
|
19
|
+
const log = getLogger();
|
|
20
|
+
export function emptyFileCandidatesMap() {
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
generated: new Date().toISOString(),
|
|
24
|
+
file_candidates: {},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Compute the per-file candidate count by walking the topic-index.
|
|
29
|
+
* Each entry without a `dec_id` contributes 1 to its `sot_source`
|
|
30
|
+
* bucket. Files with zero unpromoted candidates are omitted from the
|
|
31
|
+
* map (so `Map.has(file)` is the gate, not a zero-check).
|
|
32
|
+
*/
|
|
33
|
+
export function buildFileCandidatesMap(topicIndex) {
|
|
34
|
+
const counts = new Map();
|
|
35
|
+
for (const entry of Object.values(topicIndex.topics)) {
|
|
36
|
+
if (entry.dec_id !== undefined)
|
|
37
|
+
continue;
|
|
38
|
+
const cur = counts.get(entry.sot_source) ?? 0;
|
|
39
|
+
counts.set(entry.sot_source, cur + 1);
|
|
40
|
+
}
|
|
41
|
+
const file_candidates = {};
|
|
42
|
+
const sortedFiles = [...counts.keys()].sort();
|
|
43
|
+
for (const f of sortedFiles)
|
|
44
|
+
file_candidates[f] = counts.get(f);
|
|
45
|
+
return {
|
|
46
|
+
version: 1,
|
|
47
|
+
generated: new Date().toISOString(),
|
|
48
|
+
file_candidates,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function readFileCandidatesMap(repoRoot) {
|
|
52
|
+
const path = fileCandidatesMapPath(repoRoot);
|
|
53
|
+
if (!existsSync(path))
|
|
54
|
+
return emptyFileCandidatesMap();
|
|
55
|
+
try {
|
|
56
|
+
const raw = readFileSync(path, "utf8");
|
|
57
|
+
const parsed = FileCandidatesMap.safeParse(parseYaml(raw));
|
|
58
|
+
if (!parsed.success) {
|
|
59
|
+
log.warn({ path, error: parsed.error.message }, "file-candidates-map invalid; treating as empty");
|
|
60
|
+
return emptyFileCandidatesMap();
|
|
61
|
+
}
|
|
62
|
+
return parsed.data;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
log.warn({ path, err }, "file-candidates-map read failed; treating as empty");
|
|
66
|
+
return emptyFileCandidatesMap();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function writeFileCandidatesMap(repoRoot, topicIndex) {
|
|
70
|
+
const path = fileCandidatesMapPath(repoRoot);
|
|
71
|
+
const map = buildFileCandidatesMap(topicIndex);
|
|
72
|
+
writeFileSafe(path, stringifyYaml(map));
|
|
73
|
+
log.debug({ path, files: Object.keys(map.file_candidates).length }, "wrote file-candidates-map");
|
|
74
|
+
return path;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=file-candidates-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-candidates-map.js","sourceRoot":"","sources":["../src/file-candidates-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ProvenanceFrontmatter as ProvenanceType } from "./schemas.js";
|
|
2
|
+
export interface ParsedDocument {
|
|
3
|
+
frontmatter: ProvenanceType | null;
|
|
4
|
+
body: string;
|
|
5
|
+
/** Raw YAML block before parsing (empty if no frontmatter present). */
|
|
6
|
+
raw: string;
|
|
7
|
+
/** Number of lines the frontmatter (incl. fences) occupies. */
|
|
8
|
+
fenceLineCount: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract frontmatter from `source` and return it as an unvalidated
|
|
12
|
+
* `Record<string, unknown>` alongside the remaining body. Returns
|
|
13
|
+
* `{ fm: {}, body: source }` on any parse failure or absent frontmatter.
|
|
14
|
+
*
|
|
15
|
+
* Use this instead of an inline `.match(/^---…/)` when callers need
|
|
16
|
+
* arbitrary field access without a Zod schema.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseFrontmatterRecord(source: string): {
|
|
19
|
+
fm: Record<string, unknown>;
|
|
20
|
+
body: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function parseFrontmatter(source: string): ParsedDocument;
|
|
23
|
+
export declare function readFrontmatter(absPath: string): ParsedDocument;
|
|
24
|
+
export interface FreshnessVerdict {
|
|
25
|
+
/** ISO timestamp from frontmatter, or null if missing/unparseable. */
|
|
26
|
+
verifiedAt: string | null;
|
|
27
|
+
/** Days since verifiedAt; null if verifiedAt is missing. */
|
|
28
|
+
ageDays: number | null;
|
|
29
|
+
/** "fresh" | "warn" | "block" | "unknown" — matches FILESYSTEM_LAYOUT §3. */
|
|
30
|
+
status: "fresh" | "warn" | "block" | "unknown";
|
|
31
|
+
}
|
|
32
|
+
export declare function evaluateFreshness(fm: ProvenanceType | null, warnDays?: number, blockDays?: number, now?: Date): FreshnessVerdict;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { parse as parseYaml } from "yaml";
|
|
3
|
+
import { ProvenanceFrontmatter } from "./schemas.js";
|
|
4
|
+
const FENCE_RE = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?/;
|
|
5
|
+
/**
|
|
6
|
+
* Extract frontmatter from `source` and return it as an unvalidated
|
|
7
|
+
* `Record<string, unknown>` alongside the remaining body. Returns
|
|
8
|
+
* `{ fm: {}, body: source }` on any parse failure or absent frontmatter.
|
|
9
|
+
*
|
|
10
|
+
* Use this instead of an inline `.match(/^---…/)` when callers need
|
|
11
|
+
* arbitrary field access without a Zod schema.
|
|
12
|
+
*/
|
|
13
|
+
export function parseFrontmatterRecord(source) {
|
|
14
|
+
const match = source.match(FENCE_RE);
|
|
15
|
+
if (match === null || match.index !== 0) {
|
|
16
|
+
return { fm: {}, body: source };
|
|
17
|
+
}
|
|
18
|
+
const raw = match[1] ?? "";
|
|
19
|
+
const body = source.slice(match[0].length);
|
|
20
|
+
if (raw.trim().length === 0)
|
|
21
|
+
return { fm: {}, body };
|
|
22
|
+
try {
|
|
23
|
+
const parsed = parseYaml(raw);
|
|
24
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
25
|
+
return { fm: parsed, body };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* ignore — malformed YAML */
|
|
30
|
+
}
|
|
31
|
+
return { fm: {}, body };
|
|
32
|
+
}
|
|
33
|
+
export function parseFrontmatter(source) {
|
|
34
|
+
const match = source.match(FENCE_RE);
|
|
35
|
+
if (!match || match.index !== 0) {
|
|
36
|
+
return { frontmatter: null, body: source, raw: "", fenceLineCount: 0 };
|
|
37
|
+
}
|
|
38
|
+
const raw = match[1] ?? "";
|
|
39
|
+
const fence = match[0];
|
|
40
|
+
const body = source.slice(fence.length);
|
|
41
|
+
const fenceLineCount = fence.split(/\r?\n/).length - 1;
|
|
42
|
+
if (raw.trim().length === 0) {
|
|
43
|
+
return { frontmatter: null, body, raw, fenceLineCount };
|
|
44
|
+
}
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = parseYaml(raw);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return { frontmatter: null, body, raw, fenceLineCount };
|
|
51
|
+
}
|
|
52
|
+
const result = ProvenanceFrontmatter.safeParse(parsed);
|
|
53
|
+
return {
|
|
54
|
+
frontmatter: result.success ? result.data : null,
|
|
55
|
+
body,
|
|
56
|
+
raw,
|
|
57
|
+
fenceLineCount,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function readFrontmatter(absPath) {
|
|
61
|
+
const source = readFileSync(absPath, "utf8");
|
|
62
|
+
return parseFrontmatter(source);
|
|
63
|
+
}
|
|
64
|
+
export function evaluateFreshness(fm, warnDays = 30, blockDays = 60, now = new Date()) {
|
|
65
|
+
if (!fm || !fm["verified-at"]) {
|
|
66
|
+
return { verifiedAt: null, ageDays: null, status: "unknown" };
|
|
67
|
+
}
|
|
68
|
+
const ts = Date.parse(fm["verified-at"]);
|
|
69
|
+
if (Number.isNaN(ts)) {
|
|
70
|
+
return { verifiedAt: fm["verified-at"], ageDays: null, status: "unknown" };
|
|
71
|
+
}
|
|
72
|
+
const ageMs = now.getTime() - ts;
|
|
73
|
+
const ageDays = Math.floor(ageMs / 86_400_000);
|
|
74
|
+
const status = ageDays >= blockDays ? "block" : ageDays >= warnDays ? "warn" : "fresh";
|
|
75
|
+
return { verifiedAt: fm["verified-at"], ageDays, status };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"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"}
|
package/dist/fs.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Dirent } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* Write `data` to `path`, creating parent directories as needed.
|
|
4
|
+
* Encoding is always UTF-8.
|
|
5
|
+
*/
|
|
6
|
+
export declare function writeFileSafe(path: string, data: string): void;
|
|
7
|
+
/**
|
|
8
|
+
* Generic directory walker options.
|
|
9
|
+
*/
|
|
10
|
+
export interface WalkFsOptions {
|
|
11
|
+
/** The root directory to start walking from. */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** Optional set of directory names to skip (e.g., .git, node_modules). */
|
|
14
|
+
skipDirs?: Set<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Callback for each file found.
|
|
17
|
+
* @param rel Repo-relative path (if repoRoot provided) or dir-relative path.
|
|
18
|
+
* @param abs Absolute path to the file.
|
|
19
|
+
* @param entry The Dirent object.
|
|
20
|
+
*/
|
|
21
|
+
onFile?: (rel: string, abs: string, entry: Dirent) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Callback for each directory found.
|
|
24
|
+
* @returns false to skip recursion into this directory.
|
|
25
|
+
*/
|
|
26
|
+
onDir?: (rel: string, abs: string, entry: Dirent) => boolean | void;
|
|
27
|
+
/**
|
|
28
|
+
* If provided, relative paths passed to callbacks will be relative to this root.
|
|
29
|
+
* If not provided, paths will be relative to `dir`.
|
|
30
|
+
*/
|
|
31
|
+
repoRoot?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generic recursive directory walker.
|
|
35
|
+
*/
|
|
36
|
+
export declare function walkFs(opts: WalkFsOptions): void;
|
package/dist/fs.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readdirSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Write `data` to `path`, creating parent directories as needed.
|
|
5
|
+
* Encoding is always UTF-8.
|
|
6
|
+
*/
|
|
7
|
+
export function writeFileSafe(path, data) {
|
|
8
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
9
|
+
writeFileSync(path, data, "utf8");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generic recursive directory walker.
|
|
13
|
+
*/
|
|
14
|
+
export function walkFs(opts) {
|
|
15
|
+
const { dir, skipDirs, onFile, onDir, repoRoot } = opts;
|
|
16
|
+
const root = repoRoot ?? dir;
|
|
17
|
+
const stack = [dir];
|
|
18
|
+
while (stack.length > 0) {
|
|
19
|
+
const currentDir = stack.pop();
|
|
20
|
+
if (currentDir === undefined)
|
|
21
|
+
break;
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = readdirSync(currentDir, { withFileTypes: true, encoding: "utf8" });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const abs = join(currentDir, entry.name);
|
|
31
|
+
const rel = relative(root, abs).replace(/\\/g, "/");
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
if (skipDirs?.has(entry.name))
|
|
34
|
+
continue;
|
|
35
|
+
const shouldRecurse = onDir ? onDir(rel, abs, entry) : true;
|
|
36
|
+
if (shouldRecurse !== false) {
|
|
37
|
+
stack.push(abs);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (entry.isFile()) {
|
|
41
|
+
if (onFile)
|
|
42
|
+
onFile(rel, abs, entry);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=fs.js.map
|
package/dist/fs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAY;IACtD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AA6BD;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,IAAmB;IACxC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,IAAI,GAAG,QAAQ,IAAI,GAAG,CAAC;IAE7B,MAAM,KAAK,GAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,UAAU,KAAK,SAAS;YAAE,MAAM;QAEpC,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/E,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAEpD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,SAAS;gBACxC,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC5D,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;oBAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC1B,IAAI,MAAM;oBAAE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/glob.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal glob matcher (no external dep). Supports `**`, `*`, `?`, and literal
|
|
3
|
+
* segments. Patterns are POSIX-style; segments separator is forward slash.
|
|
4
|
+
*
|
|
5
|
+
* Single home for glob matching across the package — walker, GC sweep,
|
|
6
|
+
* sensors, mirror dirty-overlap, and MCP tools all import from here.
|
|
7
|
+
*/
|
|
8
|
+
export declare function matchGlob(path: string, glob: string): boolean;
|
|
9
|
+
export declare function matchAnyGlob(path: string, globs: readonly string[]): boolean;
|
|
10
|
+
export declare function compileGlob(glob: string): RegExp;
|
package/dist/glob.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal glob matcher (no external dep). Supports `**`, `*`, `?`, and literal
|
|
3
|
+
* segments. Patterns are POSIX-style; segments separator is forward slash.
|
|
4
|
+
*
|
|
5
|
+
* Single home for glob matching across the package — walker, GC sweep,
|
|
6
|
+
* sensors, mirror dirty-overlap, and MCP tools all import from here.
|
|
7
|
+
*/
|
|
8
|
+
export function matchGlob(path, glob) {
|
|
9
|
+
return compileGlob(glob).test(path);
|
|
10
|
+
}
|
|
11
|
+
export function matchAnyGlob(path, globs) {
|
|
12
|
+
return globs.some((g) => matchGlob(path, g));
|
|
13
|
+
}
|
|
14
|
+
export function compileGlob(glob) {
|
|
15
|
+
let re = "^";
|
|
16
|
+
for (let i = 0; i < glob.length; i++) {
|
|
17
|
+
const c = glob[i];
|
|
18
|
+
if (c === "*") {
|
|
19
|
+
if (glob[i + 1] === "*") {
|
|
20
|
+
i += 1;
|
|
21
|
+
if (glob[i + 1] === "/") {
|
|
22
|
+
i += 1;
|
|
23
|
+
re += "(?:.*/)?";
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
re += ".*";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
re += "[^/]*";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (c === "?") {
|
|
34
|
+
re += "[^/]";
|
|
35
|
+
}
|
|
36
|
+
else if (c !== undefined && /[.+^${}()|[\]\\]/.test(c)) {
|
|
37
|
+
re += `\\${c}`;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
re += c ?? "";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
re += "$";
|
|
44
|
+
return new RegExp(re);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=glob.js.map
|
package/dist/glob.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,KAAwB;IACjE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,EAAE,GAAG,GAAG,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,CAAC,IAAI,CAAC,CAAC;gBACP,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBACxB,CAAC,IAAI,CAAC,CAAC;oBACP,EAAE,IAAI,UAAU,CAAC;gBACnB,CAAC;qBAAM,CAAC;oBACN,EAAE,IAAI,IAAI,CAAC;gBACb,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,EAAE,IAAI,OAAO,CAAC;YAChB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,EAAE,IAAI,MAAM,CAAC;QACf,CAAC;aAAM,IAAI,CAAC,KAAK,SAAS,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,EAAE,IAAI,KAAK,CAAC,EAAE,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IACD,EAAE,IAAI,GAAG,CAAC;IACV,OAAO,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
|
package/dist/home.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cairn state-home resolver — the single source of truth for *where* a repo's
|
|
3
|
+
* Cairn state lives. Committed mode (the default) returns `<repoRoot>/.cairn`,
|
|
4
|
+
* byte-for-byte today's behavior. Ghost mode returns an out-of-repo directory
|
|
5
|
+
* under `~/.cairn/state/<repo-id>/`, so nothing Cairn-shaped ever lands in the
|
|
6
|
+
* client tree.
|
|
7
|
+
*
|
|
8
|
+
* This is pure indirection: every `paths.ts` helper (and every other call site
|
|
9
|
+
* that used to hardcode `join(repoRoot, ".cairn", …)`) builds on `cairnHome`
|
|
10
|
+
* here. Centralizing the state location is a standalone win independent of
|
|
11
|
+
* ghost — it makes the location relocatable, testable, and a single seam.
|
|
12
|
+
*
|
|
13
|
+
* The resolver chicken-and-egg (the ghost flag normally lives in the config
|
|
14
|
+
* file, but in ghost that file is *inside* the out-of-repo home) is broken by a
|
|
15
|
+
* global registry outside any client repo: `~/.cairn/registry.yaml`.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Global Cairn state home — `~/.cairn/state/`. Hardcoded (the operator hates
|
|
19
|
+
* env vars). Per-repo ghost state lives at `<stateHome>/<repo-id>/`, mirroring
|
|
20
|
+
* the in-repo `.cairn/` layout (`ground/`, `config.yaml`, `git-hooks/`, …) but
|
|
21
|
+
* physically outside every client repo tree.
|
|
22
|
+
*/
|
|
23
|
+
export declare function stateHome(): string;
|
|
24
|
+
/** `~/.cairn/state/<repoId>` — the out-of-repo home for one ghost repo. */
|
|
25
|
+
export declare function ghostStateDir(repoId: string): string;
|
|
26
|
+
/** `~/.cairn/registry.yaml` — the global repo→mode map, outside any repo. */
|
|
27
|
+
export declare function registryPath(): string;
|
|
28
|
+
/** One registry record. `<repo-id>` keys on the move-stable root-commit SHA. */
|
|
29
|
+
export interface RegistryEntry {
|
|
30
|
+
/** "ghost" | "committed" — only "ghost" entries relocate state. */
|
|
31
|
+
mode: string;
|
|
32
|
+
/** Absolute out-of-repo state dir. */
|
|
33
|
+
state_dir: string;
|
|
34
|
+
/** Move-stable identity: the repo's root-commit SHA. */
|
|
35
|
+
root_commit?: string;
|
|
36
|
+
/** Last-seen absolute path of the client checkout (abs-path fallback). */
|
|
37
|
+
last_path?: string;
|
|
38
|
+
}
|
|
39
|
+
interface Registry {
|
|
40
|
+
repos?: Record<string, RegistryEntry>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Drop the memoized resolution for a repo (or all repos). Call after mutating
|
|
44
|
+
* the registry — e.g. `cairn init --ghost` registers a repo, then seed must
|
|
45
|
+
* resolve to the *new* out-of-repo home rather than a stale committed cache.
|
|
46
|
+
*/
|
|
47
|
+
export declare function invalidateCairnHomeCache(repoRoot?: string): void;
|
|
48
|
+
/** Read `~/.cairn/registry.yaml`; null when absent/broken (→ committed). */
|
|
49
|
+
export declare function readRegistry(): Registry | null;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the Cairn state home for a repo — the base every path helper builds
|
|
52
|
+
* on. Committed: `<repoRoot>/.cairn`. Ghost: the out-of-repo `state_dir`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function cairnHome(repoRoot: string): string;
|
|
55
|
+
/** True when this repo is registered ghost. Registry lookup, memoized. */
|
|
56
|
+
export declare function isGhost(repoRoot: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* `join(cairnHome(repoRoot), ...segments)` — the workhorse builder that
|
|
59
|
+
* replaces every `join(repoRoot, ".cairn", …)` outside `paths.ts`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function cairnDir(repoRoot: string, ...segments: string[]): string;
|
|
62
|
+
/**
|
|
63
|
+
* Register a repo as ghost in the global registry, keyed on its root-commit
|
|
64
|
+
* SHA (abs-path key fallback for pre-first-commit repos), and return the
|
|
65
|
+
* resolved out-of-repo state dir. Invalidates the resolver cache so subsequent
|
|
66
|
+
* `cairnHome` calls relocate immediately.
|
|
67
|
+
*/
|
|
68
|
+
export declare function registerGhostRepo(repoRoot: string): RegistryEntry;
|
|
69
|
+
export {};
|