@kogu/context-check 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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/dist/analyzer/engine.d.ts +27 -0
  4. package/dist/analyzer/engine.js +64 -0
  5. package/dist/analyzer/engine.js.map +1 -0
  6. package/dist/analyzer/rules/context-overhead.d.ts +21 -0
  7. package/dist/analyzer/rules/context-overhead.js +53 -0
  8. package/dist/analyzer/rules/context-overhead.js.map +1 -0
  9. package/dist/analyzer/rules/duplicate.d.ts +15 -0
  10. package/dist/analyzer/rules/duplicate.js +53 -0
  11. package/dist/analyzer/rules/duplicate.js.map +1 -0
  12. package/dist/analyzer/rules/high-stakes.d.ts +36 -0
  13. package/dist/analyzer/rules/high-stakes.js +96 -0
  14. package/dist/analyzer/rules/high-stakes.js.map +1 -0
  15. package/dist/analyzer/rules/large-file.d.ts +15 -0
  16. package/dist/analyzer/rules/large-file.js +41 -0
  17. package/dist/analyzer/rules/large-file.js.map +1 -0
  18. package/dist/analyzer/rules/mapping.d.ts +29 -0
  19. package/dist/analyzer/rules/mapping.js +51 -0
  20. package/dist/analyzer/rules/mapping.js.map +1 -0
  21. package/dist/analyzer/rules/repetition.d.ts +20 -0
  22. package/dist/analyzer/rules/repetition.js +80 -0
  23. package/dist/analyzer/rules/repetition.js.map +1 -0
  24. package/dist/analyzer/rules/scoped-no-match.d.ts +15 -0
  25. package/dist/analyzer/rules/scoped-no-match.js +46 -0
  26. package/dist/analyzer/rules/scoped-no-match.js.map +1 -0
  27. package/dist/analyzer/run.d.ts +25 -0
  28. package/dist/analyzer/run.js +59 -0
  29. package/dist/analyzer/run.js.map +1 -0
  30. package/dist/analyzer/severity.d.ts +21 -0
  31. package/dist/analyzer/severity.js +39 -0
  32. package/dist/analyzer/severity.js.map +1 -0
  33. package/dist/analyzer/types.d.ts +35 -0
  34. package/dist/analyzer/types.js +17 -0
  35. package/dist/analyzer/types.js.map +1 -0
  36. package/dist/cli/index.d.ts +23 -0
  37. package/dist/cli/index.js +159 -0
  38. package/dist/cli/index.js.map +1 -0
  39. package/dist/config/load.d.ts +11 -0
  40. package/dist/config/load.js +38 -0
  41. package/dist/config/load.js.map +1 -0
  42. package/dist/discovery/adapters/agents.d.ts +8 -0
  43. package/dist/discovery/adapters/agents.js +25 -0
  44. package/dist/discovery/adapters/agents.js.map +1 -0
  45. package/dist/discovery/adapters/artifact.d.ts +21 -0
  46. package/dist/discovery/adapters/artifact.js +40 -0
  47. package/dist/discovery/adapters/artifact.js.map +1 -0
  48. package/dist/discovery/adapters/claude.d.ts +8 -0
  49. package/dist/discovery/adapters/claude.js +25 -0
  50. package/dist/discovery/adapters/claude.js.map +1 -0
  51. package/dist/discovery/adapters/cursor.d.ts +11 -0
  52. package/dist/discovery/adapters/cursor.js +32 -0
  53. package/dist/discovery/adapters/cursor.js.map +1 -0
  54. package/dist/discovery/adapters/index.d.ts +10 -0
  55. package/dist/discovery/adapters/index.js +18 -0
  56. package/dist/discovery/adapters/index.js.map +1 -0
  57. package/dist/discovery/adapters/skill.d.ts +9 -0
  58. package/dist/discovery/adapters/skill.js +26 -0
  59. package/dist/discovery/adapters/skill.js.map +1 -0
  60. package/dist/discovery/adapters/types.d.ts +22 -0
  61. package/dist/discovery/adapters/types.js +9 -0
  62. package/dist/discovery/adapters/types.js.map +1 -0
  63. package/dist/discovery/discover.d.ts +32 -0
  64. package/dist/discovery/discover.js +51 -0
  65. package/dist/discovery/discover.js.map +1 -0
  66. package/dist/discovery/exclusions.d.ts +19 -0
  67. package/dist/discovery/exclusions.js +40 -0
  68. package/dist/discovery/exclusions.js.map +1 -0
  69. package/dist/discovery/scanner.d.ts +31 -0
  70. package/dist/discovery/scanner.js +75 -0
  71. package/dist/discovery/scanner.js.map +1 -0
  72. package/dist/git/history.d.ts +25 -0
  73. package/dist/git/history.js +71 -0
  74. package/dist/git/history.js.map +1 -0
  75. package/dist/git/sufficiency.d.ts +38 -0
  76. package/dist/git/sufficiency.js +98 -0
  77. package/dist/git/sufficiency.js.map +1 -0
  78. package/dist/index.d.ts +8 -0
  79. package/dist/index.js +13 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/output/json.d.ts +58 -0
  82. package/dist/output/json.js +52 -0
  83. package/dist/output/json.js.map +1 -0
  84. package/dist/output/terminal.d.ts +21 -0
  85. package/dist/output/terminal.js +163 -0
  86. package/dist/output/terminal.js.map +1 -0
  87. package/dist/parser/frontmatter.d.ts +26 -0
  88. package/dist/parser/frontmatter.js +63 -0
  89. package/dist/parser/frontmatter.js.map +1 -0
  90. package/dist/parser/markdown.d.ts +23 -0
  91. package/dist/parser/markdown.js +63 -0
  92. package/dist/parser/markdown.js.map +1 -0
  93. package/dist/parser/normalize.d.ts +20 -0
  94. package/dist/parser/normalize.js +35 -0
  95. package/dist/parser/normalize.js.map +1 -0
  96. package/dist/parser/parse.d.ts +34 -0
  97. package/dist/parser/parse.js +35 -0
  98. package/dist/parser/parse.js.map +1 -0
  99. package/dist/parser/plain-text.d.ts +30 -0
  100. package/dist/parser/plain-text.js +52 -0
  101. package/dist/parser/plain-text.js.map +1 -0
  102. package/dist/parser/scope.d.ts +20 -0
  103. package/dist/parser/scope.js +44 -0
  104. package/dist/parser/scope.js.map +1 -0
  105. package/dist/snapshot/diff.d.ts +62 -0
  106. package/dist/snapshot/diff.js +146 -0
  107. package/dist/snapshot/diff.js.map +1 -0
  108. package/dist/snapshot/manager.d.ts +54 -0
  109. package/dist/snapshot/manager.js +131 -0
  110. package/dist/snapshot/manager.js.map +1 -0
  111. package/dist/tokens/estimator.d.ts +12 -0
  112. package/dist/tokens/estimator.js +16 -0
  113. package/dist/tokens/estimator.js.map +1 -0
  114. package/dist/types/configuration.d.ts +47 -0
  115. package/dist/types/configuration.js +9 -0
  116. package/dist/types/configuration.js.map +1 -0
  117. package/dist/types/finding.d.ts +32 -0
  118. package/dist/types/finding.js +14 -0
  119. package/dist/types/finding.js.map +1 -0
  120. package/dist/types/report.d.ts +20 -0
  121. package/dist/types/report.js +9 -0
  122. package/dist/types/report.js.map +1 -0
  123. package/package.json +70 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/parser/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAC3B,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CACjE,CAAC;QACF,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,IAAyC;IAEzC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAE1D,IAAI,QAAQ,KAAK,SAAS,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE1E,MAAM,KAAK,GAAuB,EAAE,CAAC;IACrC,IAAI,QAAQ,KAAK,SAAS;QAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACtD,IAAI,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;IAC/D,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Diff calculation (Spec §30) — Snapshot/Diff v1.
3
+ *
4
+ * Compares two snapshots (or a snapshot against current state) and reports:
5
+ * - per-file artifact changes (added / removed / modified)
6
+ * - total context token delta
7
+ * - analysis finding changes (added / resolved / changed)
8
+ *
9
+ * The value proposition is that a ContextCheck diff answers "the context layer
10
+ * changed, and as a result which analysis findings appeared / disappeared?"
11
+ * — NOT causal claims about model performance (Spec §28, Rule 28).
12
+ */
13
+ import type { Finding, FindingType } from "../types/finding.js";
14
+ import type { Snapshot, SnapshotEntry } from "./manager.js";
15
+ export interface FileDiff {
16
+ path: string;
17
+ beforeTokens: number;
18
+ afterTokens: number;
19
+ delta: number;
20
+ status: "added" | "removed" | "changed" | "unchanged";
21
+ }
22
+ export type FindingChangeStatus = "added" | "removed" | "changed" | "unchanged";
23
+ export interface FindingChange {
24
+ id: string;
25
+ type: FindingType;
26
+ status: FindingChangeStatus;
27
+ title: string;
28
+ filePaths: string[];
29
+ }
30
+ export interface FindingChangesSummary {
31
+ added: number;
32
+ removed: number;
33
+ changed: number;
34
+ unchanged: number;
35
+ }
36
+ export interface SnapshotDiff {
37
+ beforeTokens: number;
38
+ afterTokens: number;
39
+ delta: number;
40
+ files: FileDiff[];
41
+ addedFiles: string[];
42
+ removedFiles: string[];
43
+ findings: FindingChange[];
44
+ findingChanges: FindingChangesSummary;
45
+ }
46
+ /**
47
+ * Computes a diff between an older and a newer artifact set, plus analysis
48
+ * finding changes when finding arrays are supplied. Findings are optional so
49
+ * diffs against legacy snapshots (no stored findings) still work.
50
+ */
51
+ export declare function diffSnapshots(before: SnapshotEntry[], after: SnapshotEntry[], beforeFindings?: readonly Finding[], afterFindings?: readonly Finding[]): SnapshotDiff;
52
+ /**
53
+ * Finds finding changes between two artifact sets. Findings come from the
54
+ * stored snapshots (which now persist analysis findings alongside artifacts).
55
+ * Backward compatible: treats missing findings as an empty list.
56
+ *
57
+ * Finding identity: `id` (deterministic by type+paths+title).
58
+ */
59
+ export declare function findFindingChanges(before: readonly Finding[], after: readonly Finding[]): FindingChange[];
60
+ /** Formats a token count with thousands separators for display. */
61
+ export declare function formatTokens(tokens: number): string;
62
+ export declare function diffBetweenSnapshots(before: Snapshot, after: Snapshot): SnapshotDiff;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Diff calculation (Spec §30) — Snapshot/Diff v1.
3
+ *
4
+ * Compares two snapshots (or a snapshot against current state) and reports:
5
+ * - per-file artifact changes (added / removed / modified)
6
+ * - total context token delta
7
+ * - analysis finding changes (added / resolved / changed)
8
+ *
9
+ * The value proposition is that a ContextCheck diff answers "the context layer
10
+ * changed, and as a result which analysis findings appeared / disappeared?"
11
+ * — NOT causal claims about model performance (Spec §28, Rule 28).
12
+ */
13
+ function toTokenMap(artifacts) {
14
+ const map = {};
15
+ for (const a of artifacts)
16
+ map[a.path] = a;
17
+ return map;
18
+ }
19
+ /**
20
+ * Computes a diff between an older and a newer artifact set, plus analysis
21
+ * finding changes when finding arrays are supplied. Findings are optional so
22
+ * diffs against legacy snapshots (no stored findings) still work.
23
+ */
24
+ export function diffSnapshots(before, after, beforeFindings, afterFindings) {
25
+ const beforeMap = toTokenMap(before);
26
+ const afterMap = toTokenMap(after);
27
+ const allPaths = [
28
+ ...new Set([...Object.keys(beforeMap), ...Object.keys(afterMap)]),
29
+ ].sort();
30
+ const files = [];
31
+ for (const path of allPaths) {
32
+ const b = beforeMap[path];
33
+ const a = afterMap[path];
34
+ const beforeTokens = b?.metadata.estimatedTokens ?? 0;
35
+ const afterTokens = a?.metadata.estimatedTokens ?? 0;
36
+ let status;
37
+ if (!b && a)
38
+ status = "added";
39
+ else if (b && !a)
40
+ status = "removed";
41
+ else if (beforeTokens !== afterTokens)
42
+ status = "changed";
43
+ else
44
+ status = "unchanged";
45
+ files.push({
46
+ path,
47
+ beforeTokens,
48
+ afterTokens,
49
+ delta: afterTokens - beforeTokens,
50
+ status,
51
+ });
52
+ }
53
+ const beforeTokens = sumTokens(before);
54
+ const afterTokens = sumTokens(after);
55
+ const findingChanges = findFindingChanges(beforeFindings ?? [], afterFindings ?? []);
56
+ return {
57
+ beforeTokens,
58
+ afterTokens,
59
+ delta: afterTokens - beforeTokens,
60
+ files,
61
+ addedFiles: files.filter((f) => f.status === "added").map((f) => f.path),
62
+ removedFiles: files
63
+ .filter((f) => f.status === "removed")
64
+ .map((f) => f.path),
65
+ findings: findingChanges,
66
+ findingChanges: summarizeFindingChanges(findingChanges),
67
+ };
68
+ }
69
+ function sumTokens(artifacts) {
70
+ return artifacts.reduce((s, a) => s + a.metadata.estimatedTokens, 0);
71
+ }
72
+ /**
73
+ * Finds finding changes between two artifact sets. Findings come from the
74
+ * stored snapshots (which now persist analysis findings alongside artifacts).
75
+ * Backward compatible: treats missing findings as an empty list.
76
+ *
77
+ * Finding identity: `id` (deterministic by type+paths+title).
78
+ */
79
+ export function findFindingChanges(before, after) {
80
+ const beforeById = new Map(before.map((f) => [f.id, f]));
81
+ const afterById = new Map(after.map((f) => [f.id, f]));
82
+ const ids = new Set([...beforeById.keys(), ...afterById.keys()]);
83
+ const changes = [];
84
+ for (const id of ids) {
85
+ const b = beforeById.get(id);
86
+ const a = afterById.get(id);
87
+ if (a && !b)
88
+ changes.push(toChange(a, "added"));
89
+ else if (b && !a)
90
+ changes.push(toChange(b, "removed"));
91
+ else if (a)
92
+ changes.push(toChange(a, "unchanged"));
93
+ }
94
+ // Pair an added and a removed finding that share the same logical identity
95
+ // (type + file paths) but different id (title/text changed) as "changed".
96
+ const key = (c) => `${c.type}::${[...c.filePaths].sort().join(",")}`;
97
+ const byKey = new Map();
98
+ for (const c of changes) {
99
+ const k = key(c);
100
+ const list = byKey.get(k) ?? [];
101
+ list.push(c);
102
+ byKey.set(k, list);
103
+ }
104
+ for (const group of byKey.values()) {
105
+ const added = group.filter((c) => c.status === "added");
106
+ const removed = group.filter((c) => c.status === "removed");
107
+ while (added.length > 0 && removed.length > 0) {
108
+ const a = added.shift();
109
+ const r = removed.shift();
110
+ const idx = changes.indexOf(r);
111
+ if (idx !== -1)
112
+ changes.splice(idx, 1);
113
+ a.status = "changed";
114
+ }
115
+ }
116
+ return changes.sort((x, y) => x.status.localeCompare(y.status));
117
+ }
118
+ function toChange(f, status) {
119
+ return {
120
+ id: f.id,
121
+ type: f.type,
122
+ status,
123
+ title: f.title,
124
+ filePaths: f.filePaths,
125
+ };
126
+ }
127
+ function summarizeFindingChanges(changes) {
128
+ const summary = {
129
+ added: 0,
130
+ removed: 0,
131
+ changed: 0,
132
+ unchanged: 0,
133
+ };
134
+ for (const c of changes)
135
+ summary[c.status] += 1;
136
+ return summary;
137
+ }
138
+ /** Formats a token count with thousands separators for display. */
139
+ export function formatTokens(tokens) {
140
+ return Math.round(tokens).toLocaleString("en-US");
141
+ }
142
+ // Convenience: full snapshot-level diff.
143
+ export function diffBetweenSnapshots(before, after) {
144
+ return diffSnapshots(before.artifacts, after.artifacts, before.findings ?? [], after.findings ?? []);
145
+ }
146
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/snapshot/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Snapshot storage (Spec §29, §30).
3
+ *
4
+ * A snapshot is a privacy-safe record of the AI configuration discovered in a
5
+ * repo at a point in time. It stores only path/type/scope/metadata — never raw
6
+ * content — so snapshots are safe to persist locally and share.
7
+ *
8
+ * Snapshots are stored as JSON files under `.contextcheck/snapshots/`.
9
+ */
10
+ import type { ConfigurationArtifact } from "../types/configuration.js";
11
+ import type { Finding } from "../types/finding.js";
12
+ export declare const SNAPSHOT_DIR = ".contextcheck/snapshots";
13
+ export declare const SNAPSHOT_EXTENSION = ".json";
14
+ export interface SnapshotEntry {
15
+ id: string;
16
+ type: ConfigurationArtifact["type"];
17
+ path: string;
18
+ scope?: ConfigurationArtifact["scope"];
19
+ metadata: ConfigurationArtifact["metadata"];
20
+ }
21
+ export declare const SNAPSHOT_VERSION = 1;
22
+ export interface Snapshot {
23
+ /** Schema version. Introduced in v2; legacy snapshots lack it -> treated as 1. */
24
+ version?: number;
25
+ id: string;
26
+ createdAt: string;
27
+ configurationFiles: number;
28
+ skills: number;
29
+ estimatedTokens: number;
30
+ artifacts: SnapshotEntry[];
31
+ /** Analysis findings at snapshot time. Optional for backward compatibility. */
32
+ findings?: Finding[];
33
+ }
34
+ /** Builds a snapshot from a discovery result + findings (content excluded). */
35
+ export declare function buildSnapshot(artifacts: ConfigurationArtifact[], findings?: Finding[]): Snapshot;
36
+ /** Stable filename-friendly id derived from the creation timestamp. */
37
+ export declare function snapshotId(createdAt: string): string;
38
+ /** Resolves the snapshots directory for a repo root. */
39
+ export declare function snapshotsDir(rootPath: string): string;
40
+ /** Persists a snapshot to `.contextcheck/snapshots/<id>.json`. */
41
+ export declare function saveSnapshot(rootPath: string, snapshot: Snapshot): Promise<string>;
42
+ /** Loads a single snapshot by file path. Throws if invalid. */
43
+ export declare function loadSnapshotFile(filePath: string): Promise<Snapshot>;
44
+ /** Lists snapshot file paths, newest last. Returns [] when none exist. */
45
+ export declare function listSnapshotFiles(rootPath: string): Promise<string[]>;
46
+ /** Returns the most recently created snapshot file path, if any. */
47
+ export declare function latestSnapshotFile(rootPath: string): Promise<string | null>;
48
+ /**
49
+ * Resolves a snapshot reference (exact id, unique id prefix, or a direct file
50
+ * path) to a snapshot file path. Returns null when no snapshot matches.
51
+ */
52
+ export declare function resolveSnapshotRef(rootPath: string, ref: string): Promise<string | null>;
53
+ /** Loads metadata for every snapshot, newest last. */
54
+ export declare function listSnapshots(rootPath: string): Promise<Snapshot[]>;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Snapshot storage (Spec §29, §30).
3
+ *
4
+ * A snapshot is a privacy-safe record of the AI configuration discovered in a
5
+ * repo at a point in time. It stores only path/type/scope/metadata — never raw
6
+ * content — so snapshots are safe to persist locally and share.
7
+ *
8
+ * Snapshots are stored as JSON files under `.contextcheck/snapshots/`.
9
+ */
10
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { createHash } from "node:crypto";
13
+ import { toArtifactJson } from "../output/json.js";
14
+ export const SNAPSHOT_DIR = ".contextcheck/snapshots";
15
+ export const SNAPSHOT_EXTENSION = ".json";
16
+ export const SNAPSHOT_VERSION = 1;
17
+ /** Builds a snapshot from a discovery result + findings (content excluded). */
18
+ export function buildSnapshot(artifacts, findings = []) {
19
+ const entries = artifacts.map((a) => toArtifactJson(a));
20
+ const createdAt = new Date().toISOString();
21
+ return {
22
+ version: SNAPSHOT_VERSION,
23
+ id: snapshotId(createdAt),
24
+ createdAt,
25
+ configurationFiles: artifacts.length,
26
+ skills: artifacts.filter((a) => a.type === "skill").length,
27
+ estimatedTokens: artifacts.reduce((sum, a) => sum + a.metadata.estimatedTokens, 0),
28
+ artifacts: entries,
29
+ findings,
30
+ };
31
+ }
32
+ /** Stable filename-friendly id derived from the creation timestamp. */
33
+ export function snapshotId(createdAt) {
34
+ const base = createdAt.replace(/[:.]/g, "-");
35
+ return `snap_${createHash("sha1").update(createdAt).digest("hex").slice(0, 6)}_${base}`;
36
+ }
37
+ /** Resolves the snapshots directory for a repo root. */
38
+ export function snapshotsDir(rootPath) {
39
+ return join(rootPath, SNAPSHOT_DIR);
40
+ }
41
+ /** Persists a snapshot to `.contextcheck/snapshots/<id>.json`. */
42
+ export async function saveSnapshot(rootPath, snapshot) {
43
+ const dir = snapshotsDir(rootPath);
44
+ await mkdir(dir, { recursive: true });
45
+ const file = join(dir, `${snapshot.id}${SNAPSHOT_EXTENSION}`);
46
+ await writeFile(file, JSON.stringify(snapshot, null, 2), "utf8");
47
+ return file;
48
+ }
49
+ /** Loads a single snapshot by file path. Throws if invalid. */
50
+ export async function loadSnapshotFile(filePath) {
51
+ const text = await readFile(filePath, "utf8");
52
+ const parsed = JSON.parse(text);
53
+ if (!parsed.id || !Array.isArray(parsed.artifacts)) {
54
+ throw new Error(`Invalid snapshot: ${filePath}`);
55
+ }
56
+ return parsed;
57
+ }
58
+ /**
59
+ * Extracts the sortable timestamp portion from a snapshot filename. Ids are
60
+ * `snap_<hash>_<timestamp>`; the hash sorts before the timestamp, so sorting
61
+ * by full filename is NOT chronological. The trailing ISO-like timestamp is.
62
+ */
63
+ function snapshotTimestampKey(filename) {
64
+ const idx = filename.lastIndexOf("_");
65
+ return idx === -1 ? filename : filename.slice(idx + 1);
66
+ }
67
+ /** Lists snapshot file paths, newest last. Returns [] when none exist. */
68
+ export async function listSnapshotFiles(rootPath) {
69
+ const dir = snapshotsDir(rootPath);
70
+ let names;
71
+ try {
72
+ names = await readdir(dir);
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ return names
78
+ .filter((n) => n.endsWith(SNAPSHOT_EXTENSION))
79
+ .sort((a, b) => snapshotTimestampKey(a).localeCompare(snapshotTimestampKey(b)))
80
+ .map((n) => join(dir, n));
81
+ }
82
+ /** Returns the most recently created snapshot file path, if any. */
83
+ export async function latestSnapshotFile(rootPath) {
84
+ const files = await listSnapshotFiles(rootPath);
85
+ if (files.length === 0)
86
+ return null;
87
+ // Sorted newest last by embedded timestamp.
88
+ return files[files.length - 1] ?? null;
89
+ }
90
+ /**
91
+ * Resolves a snapshot reference (exact id, unique id prefix, or a direct file
92
+ * path) to a snapshot file path. Returns null when no snapshot matches.
93
+ */
94
+ export async function resolveSnapshotRef(rootPath, ref) {
95
+ // Direct file path (backward compatible with `diff <file>`).
96
+ const asPath = join(rootPath, SNAPSHOT_DIR, ref);
97
+ const files = await listSnapshotFiles(rootPath);
98
+ const exact = files.find((f) => basenameOf(f) === ref || f === ref || f === asPath);
99
+ if (exact)
100
+ return exact;
101
+ // Unique id prefix match. Match against the full id AND the short hash part.
102
+ const matches = files.filter((f) => {
103
+ const base = basenameOf(f).replace(SNAPSHOT_EXTENSION, "");
104
+ const short = base.split("_")[1] ?? "";
105
+ return (base === ref ||
106
+ base.startsWith(ref) ||
107
+ short === ref ||
108
+ short.startsWith(ref));
109
+ });
110
+ return matches.length === 1 ? matches[0] : null;
111
+ }
112
+ function basenameOf(filePath) {
113
+ const idx = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
114
+ return idx === -1 ? filePath : filePath.slice(idx + 1);
115
+ }
116
+ /** Loads metadata for every snapshot, newest last. */
117
+ export async function listSnapshots(rootPath) {
118
+ const files = await listSnapshotFiles(rootPath);
119
+ const snaps = [];
120
+ for (const file of files) {
121
+ try {
122
+ snaps.push(await loadSnapshotFile(file));
123
+ }
124
+ catch {
125
+ // Skip unreadable/corrupt snapshot rather than failing the whole list.
126
+ }
127
+ }
128
+ // Already newest-last; ensure by createdAt too.
129
+ return snaps.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
130
+ }
131
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/snapshot/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAU1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAelC,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAC3B,SAAkC,EAClC,WAAsB,EAAE;IAExB,MAAM,OAAO,GAAoB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,OAAO;QACL,OAAO,EAAE,gBAAgB;QACzB,EAAE,EAAE,UAAU,CAAC,SAAS,CAAC;QACzB,SAAS;QACT,kBAAkB,EAAE,SAAS,CAAC,MAAM;QACpC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM;QAC1D,eAAe,EAAE,SAAS,CAAC,MAAM,CAC/B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,eAAe,EAC5C,CAAC,CACF;QACD,SAAS,EAAE,OAAO;QAClB,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,QAAQ,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AAC1F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,QAAkB;IAElB,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,EAAE,GAAG,kBAAkB,EAAE,CAAC,CAAC;IAC9D,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IACrD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;SAC7C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACb,oBAAoB,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAC/D;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB;IAEhB,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,4CAA4C;IAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB,EAChB,GAAW;IAEX,6DAA6D;IAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAEhD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,MAAM,CAC1D,CAAC;IACF,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,6EAA6E;IAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO,CACL,IAAI,KAAK,GAAG;YACZ,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACpB,KAAK,KAAK,GAAG;YACb,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC;IACD,gDAAgD;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Token estimation (Spec §21).
3
+ *
4
+ * Goal is NOT exact model billing; it is a stable, deterministic estimate so
5
+ * users can reason about how much context their configuration occupies.
6
+ *
7
+ * Minimal implementation: `estimatedTokens = characterCount / 4`. A
8
+ * provider-specific tokenizer adapter may replace this later without changing
9
+ * the public contract.
10
+ */
11
+ /** Estimates the number of tokens in `text`. Always >= 0. */
12
+ export declare function estimateTokens(text: string): number;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Token estimation (Spec §21).
3
+ *
4
+ * Goal is NOT exact model billing; it is a stable, deterministic estimate so
5
+ * users can reason about how much context their configuration occupies.
6
+ *
7
+ * Minimal implementation: `estimatedTokens = characterCount / 4`. A
8
+ * provider-specific tokenizer adapter may replace this later without changing
9
+ * the public contract.
10
+ */
11
+ const CHARS_PER_TOKEN = 4;
12
+ /** Estimates the number of tokens in `text`. Always >= 0. */
13
+ export function estimateTokens(text) {
14
+ return Math.round(text.length / CHARS_PER_TOKEN);
15
+ }
16
+ //# sourceMappingURL=estimator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimator.js","sourceRoot":"","sources":["../../src/tokens/estimator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Normalized configuration model (Spec §16).
3
+ *
4
+ * Platform-specific files (CLAUDE.md, AGENTS.md, SKILL.md, .cursorrules,
5
+ * .cursor/rules/**) are normalized into this common shape so that analyzers
6
+ * and renderers never need to know which tool produced them.
7
+ */
8
+ /**
9
+ * The kind of configuration artifact. Determines how the analyzer treats it
10
+ * and how the renderer labels it in output.
11
+ */
12
+ export type ConfigurationType = "rule" | "skill" | "instruction" | "agent-config" | "unknown";
13
+ /**
14
+ * Declares when/where a configuration applies. Populated from glob patterns
15
+ * and platform-specific "always apply" flags.
16
+ */
17
+ export interface ConfigurationScope {
18
+ /** Glob patterns this configuration targets (e.g. `components/**\/*.tsx`). */
19
+ patterns?: string[];
20
+ /** Whether the configuration always applies regardless of path. */
21
+ alwaysApply?: boolean;
22
+ }
23
+ /** Lightweight metrics computed during parsing/normalization. */
24
+ export interface ArtifactMetadata {
25
+ /** Exact byte size on disk. */
26
+ sizeBytes: number;
27
+ /** Token estimate (`chars / 4`). Filled by the token estimator (Phase 3). */
28
+ estimatedTokens: number;
29
+ /** Number of lines in the file. */
30
+ lineCount: number;
31
+ }
32
+ /**
33
+ * Internal representation of a discovered configuration file.
34
+ *
35
+ * NOTE: this is the full model including raw `content`. Never serialize this
36
+ * directly to JSON/CI output — use a privacy-safe DTO instead (Spec §33).
37
+ */
38
+ export interface ConfigurationArtifact {
39
+ id: string;
40
+ type: ConfigurationType;
41
+ /** Path relative to the repository root. */
42
+ path: string;
43
+ /** Raw file content. Used only in-memory by analyzers. */
44
+ content: string;
45
+ scope?: ConfigurationScope;
46
+ metadata: ArtifactMetadata;
47
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Normalized configuration model (Spec §16).
3
+ *
4
+ * Platform-specific files (CLAUDE.md, AGENTS.md, SKILL.md, .cursorrules,
5
+ * .cursor/rules/**) are normalized into this common shape so that analyzers
6
+ * and renderers never need to know which tool produced them.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=configuration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../src/types/configuration.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Finding model (Spec §12, §10).
3
+ *
4
+ * `type`, `label`, `severity` and `confidence` are distinct concepts:
5
+ * - type: the technical reason a finding was produced
6
+ * - label: the category shown to the user in the CLI
7
+ * - severity: how important the finding is
8
+ * - confidence: how reliable the detection method is
9
+ *
10
+ * The type -> label -> severity -> confidence mapping in the spec is
11
+ * NORMATIVE (Rule 17). Analyzers must not invent new values (Rule 27).
12
+ */
13
+ /** Technical cause of the finding. */
14
+ export type FindingType = "duplicate" | "repetition" | "large-file" | "scoped-no-match" | "high-stakes" | "context-overhead";
15
+ /** Category shown in the CLI. */
16
+ export type FindingLabel = "INFO" | "CONTEXT" | "REVIEW" | "CAUTION";
17
+ /** Importance level. `warning` exists for future expansion only. */
18
+ export type FindingSeverity = "info" | "notice" | "warning";
19
+ /** Reliability of the detection method. */
20
+ export type FindingConfidence = "high" | "medium" | "low";
21
+ export interface Finding {
22
+ id: string;
23
+ type: FindingType;
24
+ label: FindingLabel;
25
+ severity: FindingSeverity;
26
+ confidence: FindingConfidence;
27
+ /** All files involved. Cross-file findings must list every path here. */
28
+ filePaths: string[];
29
+ title: string;
30
+ description: string;
31
+ recommendation?: string;
32
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Finding model (Spec §12, §10).
3
+ *
4
+ * `type`, `label`, `severity` and `confidence` are distinct concepts:
5
+ * - type: the technical reason a finding was produced
6
+ * - label: the category shown to the user in the CLI
7
+ * - severity: how important the finding is
8
+ * - confidence: how reliable the detection method is
9
+ *
10
+ * The type -> label -> severity -> confidence mapping in the spec is
11
+ * NORMATIVE (Rule 17). Analyzers must not invent new values (Rule 27).
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=finding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.js","sourceRoot":"","sources":["../../src/types/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Report model (Spec §12).
3
+ *
4
+ * Findings live at the report level, NOT inside individual artifacts
5
+ * (Rule 16). A configuration artifact only represents the configuration;
6
+ * it never embeds findings about itself.
7
+ */
8
+ import type { ConfigurationArtifact } from "./configuration.js";
9
+ import type { Finding } from "./finding.js";
10
+ export interface AnalysisSummary {
11
+ configurationFiles: number;
12
+ skills: number;
13
+ estimatedTokens: number;
14
+ potentialContextOverhead: number;
15
+ }
16
+ export interface AnalysisReport {
17
+ summary: AnalysisSummary;
18
+ artifacts: ConfigurationArtifact[];
19
+ findings: Finding[];
20
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Report model (Spec §12).
3
+ *
4
+ * Findings live at the report level, NOT inside individual artifacts
5
+ * (Rule 16). A configuration artifact only represents the configuration;
6
+ * it never embeds findings about itself.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/types/report.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}