@isaacriehm/cairn-core 0.19.0 → 0.21.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 (164) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/attention/index.d.ts +0 -2
  3. package/dist/attention/index.js +0 -2
  4. package/dist/attention/index.js.map +1 -1
  5. package/dist/attention/restore.d.ts +1 -1
  6. package/dist/attention/restore.js +1 -1
  7. package/dist/attention/source-strip.d.ts +2 -2
  8. package/dist/attention/source-strip.js +2 -2
  9. package/dist/components/audit.d.ts +20 -3
  10. package/dist/components/audit.js +220 -51
  11. package/dist/components/audit.js.map +1 -1
  12. package/dist/doctor/index.js +96 -3
  13. package/dist/doctor/index.js.map +1 -1
  14. package/dist/gc/citation-integrity.d.ts +2 -2
  15. package/dist/gc/citation-integrity.js +9 -7
  16. package/dist/gc/citation-integrity.js.map +1 -1
  17. package/dist/gc/classify.js +3 -16
  18. package/dist/gc/classify.js.map +1 -1
  19. package/dist/gc/entity-orphan.js +7 -5
  20. package/dist/gc/entity-orphan.js.map +1 -1
  21. package/dist/gc/sweep.d.ts +1 -1
  22. package/dist/gc/sweep.js +1 -1
  23. package/dist/hooks/post-tool-use/ask-user-blocked.d.ts +1 -2
  24. package/dist/hooks/post-tool-use/ask-user-blocked.js +1 -2
  25. package/dist/hooks/post-tool-use/ask-user-blocked.js.map +1 -1
  26. package/dist/hooks/post-tool-use/citation-scanner.d.ts +2 -2
  27. package/dist/hooks/post-tool-use/citation-scanner.js +1 -1
  28. package/dist/hooks/post-tool-use/read-enricher.d.ts +1 -1
  29. package/dist/hooks/post-tool-use/read-enricher.js +1 -1
  30. package/dist/hooks/post-tool-use/write-guardian.d.ts +1 -5
  31. package/dist/hooks/post-tool-use/write-guardian.js +21 -14
  32. package/dist/hooks/post-tool-use/write-guardian.js.map +1 -1
  33. package/dist/hooks/runners/session-start.js +61 -1
  34. package/dist/hooks/runners/session-start.js.map +1 -1
  35. package/dist/hooks/runners/stop.js +20 -30
  36. package/dist/hooks/runners/stop.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/init/detect-components.d.ts +3 -2
  41. package/dist/init/detect-components.js +31 -13
  42. package/dist/init/detect-components.js.map +1 -1
  43. package/dist/init/detect.d.ts +1 -5
  44. package/dist/init/detect.js +157 -143
  45. package/dist/init/detect.js.map +1 -1
  46. package/dist/init/glob-inference.js +24 -0
  47. package/dist/init/glob-inference.js.map +1 -1
  48. package/dist/init/index.d.ts +3 -3
  49. package/dist/init/index.js +1 -1
  50. package/dist/init/index.js.map +1 -1
  51. package/dist/init/init.js +0 -14
  52. package/dist/init/init.js.map +1 -1
  53. package/dist/init/mapper-merge.d.ts +1 -4
  54. package/dist/init/mapper-merge.js +4 -17
  55. package/dist/init/mapper-merge.js.map +1 -1
  56. package/dist/init/mapper-parallel.js +1 -1
  57. package/dist/init/mapper-parallel.js.map +1 -1
  58. package/dist/init/mapper-prompts.d.ts +1 -23
  59. package/dist/init/mapper-prompts.js +1 -25
  60. package/dist/init/mapper-prompts.js.map +1 -1
  61. package/dist/init/mapper.d.ts +0 -6
  62. package/dist/init/mapper.js +0 -3
  63. package/dist/init/mapper.js.map +1 -1
  64. package/dist/init/module-slicer.js +3 -24
  65. package/dist/init/module-slicer.js.map +1 -1
  66. package/dist/init/overlay.js +5 -16
  67. package/dist/init/overlay.js.map +1 -1
  68. package/dist/init/phases/9d-comp-walk.js +1 -1
  69. package/dist/init/phases/9d-comp-walk.js.map +1 -1
  70. package/dist/init/source-comments/strip-replace.d.ts +1 -1
  71. package/dist/init/source-comments/strip-replace.js +2 -2
  72. package/dist/init/source-comments/walker.js +8 -79
  73. package/dist/init/source-comments/walker.js.map +1 -1
  74. package/dist/init/types.d.ts +8 -16
  75. package/dist/init/walker.d.ts +1 -1
  76. package/dist/init/walker.js +1 -1
  77. package/dist/mcp/schemas.d.ts +4 -4
  78. package/dist/mcp/schemas.js +1 -1
  79. package/dist/mcp/serve.js +9 -0
  80. package/dist/mcp/serve.js.map +1 -1
  81. package/dist/mcp/tools/decision-get.js +2 -2
  82. package/dist/mcp/tools/decision-get.js.map +1 -1
  83. package/dist/mcp/tools/index.js +0 -6
  84. package/dist/mcp/tools/index.js.map +1 -1
  85. package/dist/mcp/tools/mission-advance.d.ts +3 -4
  86. package/dist/mcp/tools/mission-advance.js +10 -14
  87. package/dist/mcp/tools/mission-advance.js.map +1 -1
  88. package/dist/mcp/tools/record-decision.js +7 -6
  89. package/dist/mcp/tools/record-decision.js.map +1 -1
  90. package/dist/mcp/tools/resolve-attention.js +50 -30
  91. package/dist/mcp/tools/resolve-attention.js.map +1 -1
  92. package/dist/migrate/config-io.d.ts +16 -0
  93. package/dist/migrate/config-io.js +71 -0
  94. package/dist/migrate/config-io.js.map +1 -0
  95. package/dist/migrate/index.d.ts +5 -0
  96. package/dist/migrate/index.js +5 -0
  97. package/dist/migrate/index.js.map +1 -0
  98. package/dist/migrate/migrations/0001-drop-dead-config-fields.d.ts +12 -0
  99. package/dist/migrate/migrations/0001-drop-dead-config-fields.js +39 -0
  100. package/dist/migrate/migrations/0001-drop-dead-config-fields.js.map +1 -0
  101. package/dist/migrate/registry.d.ts +9 -0
  102. package/dist/migrate/registry.js +10 -0
  103. package/dist/migrate/registry.js.map +1 -0
  104. package/dist/migrate/runner.d.ts +45 -0
  105. package/dist/migrate/runner.js +170 -0
  106. package/dist/migrate/runner.js.map +1 -0
  107. package/dist/migrate/semver.d.ts +11 -0
  108. package/dist/migrate/semver.js +35 -0
  109. package/dist/migrate/semver.js.map +1 -0
  110. package/dist/migrate/types.d.ts +39 -0
  111. package/dist/migrate/types.js +13 -0
  112. package/dist/migrate/types.js.map +1 -0
  113. package/dist/missions/task-link.js +3 -3
  114. package/dist/missions/task-link.js.map +1 -1
  115. package/dist/sensors/catalog.js +4 -9
  116. package/dist/sensors/catalog.js.map +1 -1
  117. package/dist/sensors/decisions.js +55 -19
  118. package/dist/sensors/decisions.js.map +1 -1
  119. package/dist/sensors/diff.d.ts +12 -0
  120. package/dist/sensors/diff.js +103 -0
  121. package/dist/sensors/diff.js.map +1 -1
  122. package/dist/sensors/index.d.ts +4 -5
  123. package/dist/sensors/index.js +2 -3
  124. package/dist/sensors/index.js.map +1 -1
  125. package/dist/sensors/runner.d.ts +34 -37
  126. package/dist/sensors/runner.js +63 -53
  127. package/dist/sensors/runner.js.map +1 -1
  128. package/dist/sensors/stub-catalog.d.ts +6 -3
  129. package/dist/sensors/stub-catalog.js +7 -19
  130. package/dist/sensors/stub-catalog.js.map +1 -1
  131. package/dist/sensors/types.d.ts +11 -55
  132. package/dist/sensors/types.js +4 -8
  133. package/dist/sensors/types.js.map +1 -1
  134. package/dist/session-start/build.js +5 -5
  135. package/dist/session-start/build.js.map +1 -1
  136. package/dist/session-start/templates.d.ts +1 -1
  137. package/dist/session-start/templates.js +1 -1
  138. package/dist/tasks/lifecycle.d.ts +3 -4
  139. package/dist/tasks/lifecycle.js +9 -12
  140. package/dist/tasks/lifecycle.js.map +1 -1
  141. package/package.json +2 -2
  142. package/templates/.cairn/git-hooks/pre-commit +7 -4
  143. package/templates/.github/workflows/cairn-check.yml +8 -0
  144. package/dist/attention/bulk-accept.d.ts +0 -78
  145. package/dist/attention/bulk-accept.js +0 -348
  146. package/dist/attention/bulk-accept.js.map +0 -1
  147. package/dist/attention/serve/api.d.ts +0 -24
  148. package/dist/attention/serve/api.js +0 -383
  149. package/dist/attention/serve/api.js.map +0 -1
  150. package/dist/attention/serve/index.d.ts +0 -63
  151. package/dist/attention/serve/index.js +0 -219
  152. package/dist/attention/serve/index.js.map +0 -1
  153. package/dist/mcp/tools/attention-serve.d.ts +0 -23
  154. package/dist/mcp/tools/attention-serve.js +0 -84
  155. package/dist/mcp/tools/attention-serve.js.map +0 -1
  156. package/dist/mcp/tools/attention-wait.d.ts +0 -18
  157. package/dist/mcp/tools/attention-wait.js +0 -84
  158. package/dist/mcp/tools/attention-wait.js.map +0 -1
  159. package/dist/mcp/tools/bulk-accept-attention.d.ts +0 -22
  160. package/dist/mcp/tools/bulk-accept-attention.js +0 -84
  161. package/dist/mcp/tools/bulk-accept-attention.js.map +0 -1
  162. package/dist/sensors/attestation.d.ts +0 -44
  163. package/dist/sensors/attestation.js +0 -262
  164. package/dist/sensors/attestation.js.map +0 -1
@@ -1,6 +1,4 @@
1
1
  export { scoreDecDraft, scoreInvariant, type DraftConfidence, type DraftScoreInput, } from "./scoring.js";
2
- export { bulkAcceptObvious, type BulkAcceptArgs, type BulkAcceptResult, } from "./bulk-accept.js";
3
2
  export { findDuplicateClusters, DEFAULT_THRESHOLD_DEFINITE, DEFAULT_THRESHOLD_FLOOR, type DraftRef, type DuplicateCluster, type DedupResult, } from "./dedup.js";
4
3
  export { parseDraftMeta, findLatestSourceCommentsAudit, runDecSourceStrip, type DraftMeta, type StripOutcomeSummary, } from "./source-strip.js";
5
4
  export { restoreDec, type RestoreArgs, type RestoreResult, type RestoreState, } from "./restore.js";
6
- export { getActiveAttentionServer, startAttentionServer, type AttentionServeOptions, type AttentionServeHandle, type DoneState, } from "./serve/index.js";
@@ -1,7 +1,5 @@
1
1
  export { scoreDecDraft, scoreInvariant, } from "./scoring.js";
2
- export { bulkAcceptObvious, } from "./bulk-accept.js";
3
2
  export { findDuplicateClusters, DEFAULT_THRESHOLD_DEFINITE, DEFAULT_THRESHOLD_FLOOR, } from "./dedup.js";
4
3
  export { parseDraftMeta, findLatestSourceCommentsAudit, runDecSourceStrip, } from "./source-strip.js";
5
4
  export { restoreDec, } from "./restore.js";
6
- export { getActiveAttentionServer, startAttentionServer, } from "./serve/index.js";
7
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/attention/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,GAGf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,iBAAiB,GAGlB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,uBAAuB,GAIxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,UAAU,GAIX,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GAIrB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/attention/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,GAGf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,uBAAuB,GAIxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,UAAU,GAIX,MAAM,cAAc,CAAC"}
@@ -17,7 +17,7 @@
17
17
  * `_inbox/<id>.draft.md`, flip `status: accepted` → the original
18
18
  * draft status, and rebuild the decisions ledger so the canonical
19
19
  * surface no longer resolves the id. Source files keep the
20
- * `// §DEC-NNNN` cite (we don't reverse-strip — the cite is a
20
+ * `// §DEC-<hash>` cite (we don't reverse-strip — the cite is a
21
21
  * durable artifact of the prior accept and removing it without
22
22
  * operator intent risks dropping a legit DEC reference if the
23
23
  * operator never re-accepts). Operator can re-accept (idempotent
@@ -17,7 +17,7 @@
17
17
  * `_inbox/<id>.draft.md`, flip `status: accepted` → the original
18
18
  * draft status, and rebuild the decisions ledger so the canonical
19
19
  * surface no longer resolves the id. Source files keep the
20
- * `// §DEC-NNNN` cite (we don't reverse-strip — the cite is a
20
+ * `// §DEC-<hash>` cite (we don't reverse-strip — the cite is a
21
21
  * durable artifact of the prior accept and removing it without
22
22
  * operator intent risks dropping a legit DEC reference if the
23
23
  * operator never re-accepts). Operator can re-accept (idempotent
@@ -5,7 +5,7 @@
5
5
  * comments) to `_inbox/` and stamps `sourceFile` + `blockId` on the
6
6
  * frontmatter. When the operator accepts a draft — either inline via
7
7
  * `cairn_resolve_attention` or in bulk via `cairn_bulk_accept_attention`
8
- * — the original essay should be replaced with `// §DEC-NNNN` so the
8
+ * — the original essay should be replaced with `// §DEC-<hash>` so the
9
9
  * file ends up carrying the canonical bare cite, not the original prose.
10
10
  *
11
11
  * The full block coordinates (start/end byte offsets, language) live in
@@ -34,7 +34,7 @@ export interface StripOutcomeSummary {
34
34
  export declare function findLatestSourceCommentsAudit(repoRoot: string): string | null;
35
35
  /**
36
36
  * Strip the original source-comment block for one accepted DEC and
37
- * replace it with `// §DEC-NNNN` (or `# §DEC-NNNN` in hash-comment
37
+ * replace it with `// §DEC-<hash>` (or `# §DEC-<hash>` in hash-comment
38
38
  * languages). Reads block coordinates from the latest 7b audit YAML;
39
39
  * skips when the audit is missing, the block id isn't found, or the
40
40
  * source file is dirty (strip-replace's own dirty check).
@@ -5,7 +5,7 @@
5
5
  * comments) to `_inbox/` and stamps `sourceFile` + `blockId` on the
6
6
  * frontmatter. When the operator accepts a draft — either inline via
7
7
  * `cairn_resolve_attention` or in bulk via `cairn_bulk_accept_attention`
8
- * — the original essay should be replaced with `// §DEC-NNNN` so the
8
+ * — the original essay should be replaced with `// §DEC-<hash>` so the
9
9
  * file ends up carrying the canonical bare cite, not the original prose.
10
10
  *
11
11
  * The full block coordinates (start/end byte offsets, language) live in
@@ -136,7 +136,7 @@ export function findLatestSourceCommentsAudit(repoRoot) {
136
136
  }
137
137
  /**
138
138
  * Strip the original source-comment block for one accepted DEC and
139
- * replace it with `// §DEC-NNNN` (or `# §DEC-NNNN` in hash-comment
139
+ * replace it with `// §DEC-<hash>` (or `# §DEC-<hash>` in hash-comment
140
140
  * languages). Reads block coordinates from the latest 7b audit YAML;
141
141
  * skips when the audit is missing, the block id isn't found, or the
142
142
  * source file is dirty (strip-replace's own dirty check).
@@ -1,16 +1,33 @@
1
1
  /**
2
2
  * Component audit — advisory, read-only, never blocks (port invariant 5).
3
3
  *
4
+ * Language-agnostic: class extraction, unit-shape, and style attributes come
5
+ * from the shared `languages.ts` profile table (className for JSX, class= /
6
+ * :class for Vue/Svelte/HTML, SwiftUI `View`, Flutter `Widget`, …). The
7
+ * Tailwind-specific inline-rebuild scan (1) is gated on a detected Tailwind
8
+ * config — a non-Tailwind repo skips it rather than misfiring.
9
+ *
4
10
  * Finds violations that predate adoption:
5
- * 1. Probable inline rebuilds — `className` lists in non-component code
11
+ * 1. Probable inline rebuilds — class lists in non-component code
6
12
  * whose Tailwind utility ROOTS closely match an indexed component's
7
13
  * (max-w-2xl ≈ max-w-4xl, so value-tweaked copies still match).
14
+ * Similarity is IDF-weighted over the component corpus: a utility root
15
+ * that nearly every component uses (`flex`, `gap`, `mx`, `px`) carries
16
+ * almost no weight, so a page sharing only generic layout scaffolding
17
+ * does NOT match — only overlap on DISTINCTIVE class roots counts. This
18
+ * is self-tuning to the project's own CSS; no hardcoded utility list.
8
19
  * 2. Name collisions — `@cairn` names colliding with a type/interface name.
20
+ * 3. Unregistered components — a component-shaped file (PascalCase
21
+ * basename, real exports, JSX markup) sitting OUTSIDE the declared
22
+ * component dirs. These are co-located components the registry can't
23
+ * see; route entry files (page.tsx/layout.tsx — lowercase) are
24
+ * naturally excluded, so no framework convention list is hardcoded.
25
+ * Surfaced as an offer to relocate or register the dir — never moved.
9
26
  *
10
27
  * Findings are triage input surfaced to the attention queue with EXTEND /
11
- * rename recommendations; never auto-fixed.
28
+ * rename / register recommendations; never auto-fixed.
12
29
  */
13
- export type ComponentAuditKind = "inline-rebuild" | "name-collision";
30
+ export type ComponentAuditKind = "inline-rebuild" | "name-collision" | "unregistered-component";
14
31
  export interface ComponentAuditFinding {
15
32
  kind: ComponentAuditKind;
16
33
  /** Where the probable violation lives (repo-relative POSIX). */
@@ -1,24 +1,83 @@
1
1
  /**
2
2
  * Component audit — advisory, read-only, never blocks (port invariant 5).
3
3
  *
4
+ * Language-agnostic: class extraction, unit-shape, and style attributes come
5
+ * from the shared `languages.ts` profile table (className for JSX, class= /
6
+ * :class for Vue/Svelte/HTML, SwiftUI `View`, Flutter `Widget`, …). The
7
+ * Tailwind-specific inline-rebuild scan (1) is gated on a detected Tailwind
8
+ * config — a non-Tailwind repo skips it rather than misfiring.
9
+ *
4
10
  * Finds violations that predate adoption:
5
- * 1. Probable inline rebuilds — `className` lists in non-component code
11
+ * 1. Probable inline rebuilds — class lists in non-component code
6
12
  * whose Tailwind utility ROOTS closely match an indexed component's
7
13
  * (max-w-2xl ≈ max-w-4xl, so value-tweaked copies still match).
14
+ * Similarity is IDF-weighted over the component corpus: a utility root
15
+ * that nearly every component uses (`flex`, `gap`, `mx`, `px`) carries
16
+ * almost no weight, so a page sharing only generic layout scaffolding
17
+ * does NOT match — only overlap on DISTINCTIVE class roots counts. This
18
+ * is self-tuning to the project's own CSS; no hardcoded utility list.
8
19
  * 2. Name collisions — `@cairn` names colliding with a type/interface name.
20
+ * 3. Unregistered components — a component-shaped file (PascalCase
21
+ * basename, real exports, JSX markup) sitting OUTSIDE the declared
22
+ * component dirs. These are co-located components the registry can't
23
+ * see; route entry files (page.tsx/layout.tsx — lowercase) are
24
+ * naturally excluded, so no framework convention list is hardcoded.
25
+ * Surfaced as an offer to relocate or register the dir — never moved.
9
26
  *
10
27
  * Findings are triage input surfaced to the attention queue with EXTEND /
11
- * rename recommendations; never auto-fixed.
28
+ * rename / register recommendations; never auto-fixed.
12
29
  */
13
- import { readFileSync } from "node:fs";
30
+ import { existsSync, readFileSync } from "node:fs";
14
31
  import { join } from "node:path";
15
- import { collectComponents, hasComponentConfig, loadComponentsConfig, walkFs, } from "@isaacriehm/cairn-state";
16
- import { jaccard } from "../text/jaccard.js";
32
+ import { collectComponents, escapeRegExp, extractExportName, hasComponentConfig, loadComponentsConfig, profileForExtension, profileForFile, typeDeclKeywordsForFile, walkFs, } from "@isaacriehm/cairn-state";
17
33
  const SIMILARITY_THRESHOLD = 0.7;
18
34
  const MIN_SHARED_ROOTS = 3;
19
35
  const MIN_CLASSES = 3;
20
- const CLASS_ATTR_RE = /className\s*=\s*(?:"([^"]+)"|'([^']+)'|\{`([^`]+)`\})/g;
36
+ /**
37
+ * A shared root only counts toward MIN_SHARED_ROOTS if it is distinctive —
38
+ * idf ≥ ln(2), i.e. the root appears in fewer than ~half of all components.
39
+ * Ubiquitous layout utilities fall below this and never satisfy the gate.
40
+ */
41
+ const IDF_DISTINCTIVE_FLOOR = Math.log(2);
21
42
  const VALUE_SUFFIX = /-(?:\d[^-]*|xs|sm|md|lg|xl|\dxl|auto|full|none|screen|px)$/;
43
+ /** Tailwind config filenames — presence gates the inline-rebuild scan. */
44
+ const TAILWIND_CONFIGS = [
45
+ "tailwind.config.js",
46
+ "tailwind.config.cjs",
47
+ "tailwind.config.mjs",
48
+ "tailwind.config.ts",
49
+ ];
50
+ /**
51
+ * Whether the project uses Tailwind — the inline-rebuild scan's utility-root
52
+ * grammar is Tailwind-specific, so a non-Tailwind repo skips it rather than
53
+ * misfiring. Detected by config-file presence (root or any workspace's top
54
+ * segment) or a `tailwindcss` dependency, never assumed.
55
+ */
56
+ function usesTailwind(repoRoot, config) {
57
+ const dirs = new Set([""]);
58
+ for (const ws of config.workspaces) {
59
+ for (const d of ws.componentDirs) {
60
+ const seg = d.split("/")[0];
61
+ if (seg)
62
+ dirs.add(seg);
63
+ }
64
+ }
65
+ for (const dir of dirs) {
66
+ for (const name of TAILWIND_CONFIGS) {
67
+ if (existsSync(join(repoRoot, dir, name)))
68
+ return true;
69
+ }
70
+ }
71
+ try {
72
+ const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
73
+ if (pkg.dependencies?.["tailwindcss"] ?? pkg.devDependencies?.["tailwindcss"])
74
+ return true;
75
+ }
76
+ catch {
77
+ /* no / invalid package.json */
78
+ }
79
+ return false;
80
+ }
22
81
  const SKIP_DIRS = new Set([".git", ".cairn", "node_modules", "dist", ".next", "build", "coverage"]);
23
82
  /** "max-w-2xl" → "max-w", "px-4" → "px", "items-center" → unchanged. */
24
83
  function utilityRoot(cls) {
@@ -30,54 +89,137 @@ function utilityRoot(cls) {
30
89
  } while (cur !== prev && cur.includes("-"));
31
90
  return cur || prev;
32
91
  }
33
- function classLists(source) {
92
+ /**
93
+ * Class lists found via the file's language profile style-attribute regexes
94
+ * (`className=` for JSX, `class=` / `:class` for Vue/Svelte/HTML, …). The
95
+ * captured class string is whichever of the alternation's groups matched.
96
+ */
97
+ function classLists(source, styleAttrs) {
34
98
  const lists = [];
35
- CLASS_ATTR_RE.lastIndex = 0;
36
- let m;
37
- while ((m = CLASS_ATTR_RE.exec(source)) !== null) {
38
- const raw = (m[1] ?? m[2] ?? m[3] ?? "").replace(/\$\{[^}]*\}/g, " ");
39
- const classes = raw.split(/\s+/).filter(Boolean);
40
- if (classes.length >= MIN_CLASSES) {
41
- lists.push({ classes, line: source.slice(0, m.index).split("\n").length });
99
+ for (const attr of styleAttrs) {
100
+ const re = new RegExp(attr.source, attr.flags.includes("g") ? attr.flags : `${attr.flags}g`);
101
+ let m;
102
+ while ((m = re.exec(source)) !== null) {
103
+ let captured = "";
104
+ for (let i = 1; i < m.length; i++) {
105
+ if (m[i] !== undefined) {
106
+ captured = m[i];
107
+ break;
108
+ }
109
+ }
110
+ const raw = captured.replace(/\$\{[^}]*\}/g, " ");
111
+ const classes = raw.split(/\s+/).filter(Boolean);
112
+ if (classes.length >= MIN_CLASSES) {
113
+ lists.push({ classes, line: source.slice(0, m.index).split("\n").length });
114
+ }
115
+ if (re.lastIndex === m.index)
116
+ re.lastIndex += 1;
42
117
  }
43
118
  }
44
119
  return lists;
45
120
  }
46
- function rootSimilarity(a, b) {
47
- const ra = new Set(a.map(utilityRoot));
48
- const rb = new Set(b.map(utilityRoot));
49
- let shared = 0;
50
- for (const r of ra)
51
- if (rb.has(r))
52
- shared += 1;
53
- return { score: jaccard(ra, rb), shared };
121
+ /** Style-attribute regexes for a file, or `[]` when the language is unknown. */
122
+ function styleAttrsFor(rel) {
123
+ return profileForFile(rel)?.styleAttrs ?? [];
124
+ }
125
+ /** Distinct utility roots of a class list. */
126
+ function rootSet(classes) {
127
+ return new Set(classes.map(utilityRoot));
54
128
  }
55
129
  function inComponentDirs(rel, config) {
56
130
  return config.workspaces.some((ws) => ws.componentDirs.some((d) => rel === d || rel.startsWith(`${d}/`)));
57
131
  }
132
+ /**
133
+ * If a file outside the component dirs looks like a misplaced reusable UNIT
134
+ * (rather than a hook/util/route entry or markup duplicated inside
135
+ * non-component code), return its exported names; otherwise null. The
136
+ * "looks like a unit" test is the file's language profile `isUnitShaped` —
137
+ * React PascalCase + JSX, a Vue/Svelte SFC, a SwiftUI `View`, a Flutter
138
+ * `Widget`, a Compose `@Composable` — never a hardcoded framework list.
139
+ */
140
+ function misplacedUnitExports(rel, source) {
141
+ const profile = profileForFile(rel);
142
+ if (profile === null || !profile.isUnitShaped(source, rel))
143
+ return null;
144
+ const names = profile.exportSymbols(source, rel);
145
+ return names.length > 0 ? names : null;
146
+ }
58
147
  export function runComponentAudit(repoRoot) {
59
148
  const config = loadComponentsConfig(repoRoot);
60
149
  if (!hasComponentConfig(config))
61
150
  return { findings: [], scanned: 0 };
62
151
  const { components } = collectComponents(repoRoot, config);
63
152
  const extensions = new Set(config.workspaces.flatMap((w) => w.extensions));
64
- const typeExts = new Set([...extensions, ".ts"]);
153
+ // Files scanned for type-name collisions: the configured UI extensions plus
154
+ // their language families, so a `.tsx` project also scans sibling `.ts`
155
+ // type files — derived from the registry, not a hardcoded `.ts`.
156
+ const typeExts = new Set(extensions);
157
+ for (const e of extensions) {
158
+ for (const x of profileForExtension(e)?.extensions ?? [])
159
+ typeExts.add(x);
160
+ }
65
161
  const skipDirs = new Set([...SKIP_DIRS, ...config.workspaces.flatMap((w) => w.exclude)]);
162
+ // The inline-rebuild grammar is Tailwind-specific; non-Tailwind repos skip
163
+ // it (unregistered-component + name-collision still run).
164
+ const tailwind = usesTailwind(repoRoot, config);
66
165
  const findings = [];
67
166
  let scanned = 0;
68
167
  // ── 1. Inline-rebuild scan ────────────────────────────────────────────
69
- const signatures = components
70
- .map((c) => {
71
- let src = "";
72
- try {
73
- src = readFileSync(join(repoRoot, c.file), "utf8");
74
- }
75
- catch {
76
- /* skip unreadable */
168
+ // Each component's class lists (per the file's profile), utility roots
169
+ // precomputed. Skipped entirely when the project isn't on Tailwind.
170
+ const signatures = !tailwind
171
+ ? []
172
+ : components
173
+ .map((c) => {
174
+ let src = "";
175
+ try {
176
+ src = readFileSync(join(repoRoot, c.file), "utf8");
177
+ }
178
+ catch {
179
+ /* skip unreadable */
180
+ }
181
+ const lists = classLists(src, styleAttrsFor(c.file)).map((l) => ({
182
+ line: l.line,
183
+ roots: rootSet(l.classes),
184
+ }));
185
+ return { name: c.tags.cairn ?? "?", file: c.file, lists };
186
+ })
187
+ .filter((c) => c.lists.length > 0);
188
+ // Corpus IDF: how distinctive each utility root is. A root present in many
189
+ // components (df → N) gets idf → 0; a rare root gets a high weight. Document
190
+ // = one component (union of its lists' roots), so repetition within a file
191
+ // doesn't inflate frequency.
192
+ const corpusN = signatures.length;
193
+ const docFreq = new Map();
194
+ for (const c of signatures) {
195
+ const compRoots = new Set();
196
+ for (const l of c.lists)
197
+ for (const r of l.roots)
198
+ compRoots.add(r);
199
+ for (const r of compRoots)
200
+ docFreq.set(r, (docFreq.get(r) ?? 0) + 1);
201
+ }
202
+ const idf = (root) => Math.log((corpusN + 1) / ((docFreq.get(root) ?? 0) + 1));
203
+ // IDF-weighted Jaccard between two root sets, plus the count of shared
204
+ // DISTINCTIVE roots (ubiquitous roots don't count toward the gate).
205
+ const weightedSimilarity = (target, comp) => {
206
+ let inter = 0;
207
+ let union = 0;
208
+ let shared = 0;
209
+ for (const r of target) {
210
+ const w = idf(r);
211
+ union += w;
212
+ if (comp.has(r)) {
213
+ inter += w;
214
+ if (w >= IDF_DISTINCTIVE_FLOOR)
215
+ shared += 1;
216
+ }
77
217
  }
78
- return { name: c.tags.cairn ?? "?", file: c.file, lists: classLists(src) };
79
- })
80
- .filter((c) => c.lists.length > 0);
218
+ for (const r of comp)
219
+ if (!target.has(r))
220
+ union += idf(r);
221
+ return { score: union > 0 ? inter / union : 0, shared };
222
+ };
81
223
  // ── 2. Name-collision prep ────────────────────────────────────────────
82
224
  const nameToComponent = new Map();
83
225
  for (const c of components)
@@ -105,14 +247,35 @@ export function runComponentAudit(repoRoot) {
105
247
  scanned += 1;
106
248
  // Inline rebuild — only in non-component code with a UI extension.
107
249
  if (extensions.has(ext) && !inComponentDirs(rel, config)) {
108
- for (const target of classLists(source)) {
250
+ // A unit-shaped file outside the component dirs is not a rebuild — it
251
+ // is a co-located component the registry can't see. Surface it (with
252
+ // its export names + file) as an offer to relocate/register; skip the
253
+ // rebuild scan for it.
254
+ const exports = misplacedUnitExports(rel, source);
255
+ if (exports !== null) {
256
+ const primary = extractExportName(source, rel) ?? exports[0];
257
+ const others = exports.filter((n) => n !== primary);
258
+ const dirs = config.workspaces.flatMap((w) => w.componentDirs).join(", ");
259
+ findings.push({
260
+ kind: "unregistered-component",
261
+ file: rel,
262
+ component: primary,
263
+ componentFile: rel,
264
+ message: `${primary} (${rel})${others.length > 0 ? ` — also exports ${others.join(", ")}` : ""} ` +
265
+ `looks like a component but lives outside the declared component dir(s) (${dirs}); the registry can't see it`,
266
+ recommendation: `move ${rel} into a component dir, or add its directory to the workspace's componentDirs — then header + index ${primary}`,
267
+ });
268
+ return;
269
+ }
270
+ for (const target of tailwind ? classLists(source, styleAttrsFor(rel)) : []) {
271
+ const troots = rootSet(target.classes);
109
272
  let best = null;
110
273
  for (const comp of signatures) {
111
274
  for (const list of comp.lists) {
112
- const { score, shared } = rootSimilarity(target.classes, list.classes);
275
+ const { score, shared } = weightedSimilarity(troots, list.roots);
113
276
  if (score >= SIMILARITY_THRESHOLD && shared >= MIN_SHARED_ROOTS) {
114
277
  if (best === null || score > best.score) {
115
- best = { name: comp.name, file: comp.file, classes: list.classes, score };
278
+ best = { name: comp.name, file: comp.file, score };
116
279
  }
117
280
  }
118
281
  }
@@ -131,20 +294,26 @@ export function runComponentAudit(repoRoot) {
131
294
  }
132
295
  }
133
296
  }
134
- // Name collision — across all type/interface declarations.
135
- for (const [name, comp] of nameToComponent) {
136
- if (comp.file === rel)
137
- continue;
138
- const re = new RegExp(`\\b(?:interface|type)\\s+${name}\\b`);
139
- if (re.test(source)) {
140
- findings.push({
141
- kind: "name-collision",
142
- file: rel,
143
- component: name,
144
- componentFile: comp.file,
145
- message: `@cairn "${name}" (${comp.file}) collides with a type/interface in ${rel}`,
146
- recommendation: "rename the export (and header with it)",
147
- });
297
+ // Name collision — across the file language's type declarations
298
+ // (TS interface/type/class, Swift struct/protocol, Kotlin class, …).
299
+ // Languages with no nominal types contribute no keywords → skipped.
300
+ const declKeywords = typeDeclKeywordsForFile(rel);
301
+ if (declKeywords.length > 0) {
302
+ const kw = declKeywords.map(escapeRegExp).join("|");
303
+ for (const [name, comp] of nameToComponent) {
304
+ if (comp.file === rel)
305
+ continue;
306
+ const re = new RegExp(`\\b(?:${kw})\\s+${escapeRegExp(name)}\\b`);
307
+ if (re.test(source)) {
308
+ findings.push({
309
+ kind: "name-collision",
310
+ file: rel,
311
+ component: name,
312
+ componentFile: comp.file,
313
+ message: `@cairn "${name}" (${comp.file}) collides with a type declaration in ${rel}`,
314
+ recommendation: "rename the export (and header with it)",
315
+ });
316
+ }
148
317
  }
149
318
  }
150
319
  },
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/components/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/components/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,MAAM,GAGP,MAAM,yBAAyB,CAAC;AAEjC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAE1C,MAAM,YAAY,GAAG,4DAA4D,CAAC;AAElF,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG;IACvB,oBAAoB;IACpB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;CACZ,CAAC;AAEX;;;;;GAKG;AACH,SAAS,YAAY,CAAC,QAAgB,EAAE,MAAkC;IACxE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,aAAa,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,GAAG;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;YACpC,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QACzD,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAG1E,CAAC;QACF,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC,aAAa,CAAC;YAAE,OAAO,IAAI,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AAyBpG,wEAAwE;AACxE,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,IAAY,CAAC;IACjB,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,GAAG,CAAC;QACF,IAAI,GAAG,GAAG,CAAC;QACX,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC,QAAQ,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC5C,OAAO,GAAG,IAAI,IAAI,CAAC;AACrB,CAAC;AAOD;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAc,EAAE,UAA6B;IAC/D,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QAC7F,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACtC,IAAI,QAAQ,GAAG,EAAE,CAAC;YAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAClC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;oBACvB,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;oBACjB,MAAM;gBACR,CAAC;YACH,CAAC;YACD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,OAAO,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;gBAClC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,IAAI,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,KAAK;gBAAE,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,cAAc,CAAC,GAAG,CAAC,EAAE,UAAU,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,8CAA8C;AAC9C,SAAS,OAAO,CAAC,OAAiB;IAChC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,eAAe,CAAC,GAAW,EAAE,MAAkC;IACtE,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CACnC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CACnE,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,oBAAoB,CAAC,GAAW,EAAE,MAAc;IACvD,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAErE,MAAM,EAAE,UAAU,EAAE,GAAG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3E,4EAA4E;IAC5E,wEAAwE;IACxE,iEAAiE;IACjE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,UAAU,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,mBAAmB,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,EAAE;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzF,2EAA2E;IAC3E,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEhD,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAC7C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,yEAAyE;IACzE,uEAAuE;IACvE,oEAAoE;IACpE,MAAM,UAAU,GAAG,CAAC,QAAQ;QAC1B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,UAAU;aACP,GAAG,CAAC,CAAC,CAAkB,EAAE,EAAE;YAC1B,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YACrD,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;YACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/D,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;aAC1B,CAAC,CAAC,CAAC;YACJ,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAC5D,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEzC,2EAA2E;IAC3E,6EAA6E;IAC7E,2EAA2E;IAC3E,6BAA6B;IAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;YAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;gBAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnE,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,IAAY,EAAU,EAAE,CACnC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3D,uEAAuE;IACvE,oEAAoE;IACpE,MAAM,kBAAkB,GAAG,CACzB,MAAmB,EACnB,IAAiB,EACkB,EAAE;QACrC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,KAAK,IAAI,CAAC,CAAC;YACX,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChB,KAAK,IAAI,CAAC,CAAC;gBACX,IAAI,CAAC,IAAI,qBAAqB;oBAAE,MAAM,IAAI,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1D,CAAC,CAAC;IAEF,yEAAyE;IACzE,MAAM,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK;YAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAEnF,MAAM,CAAC;QACL,GAAG,EAAE,QAAQ;QACb,QAAQ;QACR,QAAQ;QACR,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACnB,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO;YAC/B,IAAI,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;gBACjC,kEAAkE;gBAClE,yDAAyD;YAC3D,CAAC;YACD,IAAI,MAAc,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;YACD,OAAO,IAAI,CAAC,CAAC;YAEb,mEAAmE;YACnE,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;gBACzD,sEAAsE;gBACtE,qEAAqE;gBACrE,sEAAsE;gBACtE,uBAAuB;gBACvB,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAClD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAE,CAAC;oBAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;oBACpD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC1E,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,wBAAwB;wBAC9B,IAAI,EAAE,GAAG;wBACT,SAAS,EAAE,OAAO;wBAClB,aAAa,EAAE,GAAG;wBAClB,OAAO,EACL,GAAG,OAAO,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;4BACxF,2EAA2E,IAAI,8BAA8B;wBAC/G,cAAc,EAAE,QAAQ,GAAG,sGAAsG,OAAO,EAAE;qBAC3I,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBACvC,IAAI,IAAI,GAAyD,IAAI,CAAC;oBACtE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;wBAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;4BAC9B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;4BACjE,IAAI,KAAK,IAAI,oBAAoB,IAAI,MAAM,IAAI,gBAAgB,EAAE,CAAC;gCAChE,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;oCACxC,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;gCACrD,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;wBAClB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,gBAAgB;4BACtB,IAAI,EAAE,GAAG;4BACT,IAAI,EAAE,MAAM,CAAC,IAAI;4BACjB,SAAS,EAAE,IAAI,CAAC,IAAI;4BACpB,aAAa,EAAE,IAAI,CAAC,IAAI;4BACxB,KAAK,EAAE,IAAI,CAAC,KAAK;4BACjB,OAAO,EAAE,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,YAAY,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,sBAAsB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;4BAC9G,cAAc,EAAE,UAAU,IAAI,CAAC,IAAI,kDAAkD;yBACtF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAED,gEAAgE;YAChE,qEAAqE;YACrE,oEAAoE;YACpE,MAAM,YAAY,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;YAClD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACpD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;oBAC3C,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG;wBAAE,SAAS;oBAChC,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,QAAQ,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAClE,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;wBACpB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,gBAAgB;4BACtB,IAAI,EAAE,GAAG;4BACT,SAAS,EAAE,IAAI;4BACf,aAAa,EAAE,IAAI,CAAC,IAAI;4BACxB,OAAO,EAAE,WAAW,IAAI,MAAM,IAAI,CAAC,IAAI,yCAAyC,GAAG,EAAE;4BACrF,cAAc,EAAE,wCAAwC;yBACzD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
@@ -11,8 +11,9 @@ import { existsSync, readFileSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import { which } from "../sensors/shell.js";
13
13
  import { parse as parseYaml } from "yaml";
14
- import { buildDecisionsLedger, buildInvariantsLedger, hasComponentConfig, loadComponentsConfig, } from "@isaacriehm/cairn-state";
14
+ import { buildDecisionsLedger, buildInvariantsLedger, hasComponentConfig, loadCairnConfig, loadComponentsConfig, matchGlob, walkFs, } from "@isaacriehm/cairn-state";
15
15
  import { runComponentCheck } from "../components/check.js";
16
+ import { semverCmp } from "../migrate/semver.js";
16
17
  import { VERSION } from "../index.js";
17
18
  import { normalizeProjectName } from "../paths/index.js";
18
19
  import { z } from "zod";
@@ -36,6 +37,7 @@ export function runDoctor(opts) {
36
37
  checks.push(checkCairnVersion(opts.repoRoot));
37
38
  // 2. Ground state
38
39
  checks.push(checkScopeIndex(opts.repoRoot));
40
+ checks.push(...checkConfigGlobs(opts.repoRoot));
39
41
  checks.push(checkLedger(opts.repoRoot, "decisions"));
40
42
  checks.push(checkLedger(opts.repoRoot, "invariants"));
41
43
  // 3. Sensors
@@ -110,12 +112,18 @@ function checkCairnVersion(repoRoot) {
110
112
  };
111
113
  }
112
114
  if (projectVersion !== VERSION) {
115
+ // Pin behind the CLI → the migration runner brings `.cairn/` forward and
116
+ // stamps the pin. Pin ahead → this CLI is the stale one; upgrade it.
117
+ // (Never advise downgrading the CLI to a frozen pin — that was backwards.)
118
+ const pinBehind = semverCmp(projectVersion, VERSION) < 0;
113
119
  return {
114
120
  group: "core",
115
121
  label: "cairn version",
116
122
  status: "warn",
117
- detail: `running cairn ${VERSION}; project pinned to ${projectVersion} — sensor schemas may not match`,
118
- fixCommand: `npm install -g @isaacriehm/cairn@${projectVersion}`,
123
+ detail: pinBehind
124
+ ? `project pinned to ${projectVersion}; running cairn ${VERSION} — run \`cairn migrate\` to bring .cairn/ to current`
125
+ : `project pinned to ${projectVersion}; this CLI is older (${VERSION}) — upgrade the CLI`,
126
+ fixCommand: pinBehind ? "cairn migrate" : "npm install -g @isaacriehm/cairn@latest",
119
127
  };
120
128
  }
121
129
  return {
@@ -191,6 +199,91 @@ function checkScopeIndex(repoRoot) {
191
199
  detail: `${count} file${count === 1 ? "" : "s"} classified`,
192
200
  };
193
201
  }
202
+ /** Dirs the config-glob walk never descends into. */
203
+ const GLOB_WALK_SKIP_DIRS = new Set([
204
+ ".git",
205
+ "node_modules",
206
+ ".pnpm-store",
207
+ "dist",
208
+ "build",
209
+ ".next",
210
+ ".turbo",
211
+ ".cache",
212
+ ".archive",
213
+ "coverage",
214
+ ".cairn",
215
+ ]);
216
+ /**
217
+ * Config-glob staleness — warn when a `config.yaml` scope glob matches zero
218
+ * files in the working tree. These globs (`high_stakes_globs`,
219
+ * `project_globs.{route_handler,dto,generator_source,high_stakes}_globs`) are
220
+ * written once at adoption and never reconciled to the tree, so a later
221
+ * directory refactor silently neuters high-stakes weighting + the
222
+ * route/DTO sensors (they resolve to zero matches with no signal).
223
+ *
224
+ * Returns [] when the project declares no scope globs (nothing to validate)
225
+ * or the tree walk yields nothing. Stale globs collapse into a single warn
226
+ * check so the report stays readable; the detail lists a sample.
227
+ */
228
+ function checkConfigGlobs(repoRoot) {
229
+ const cfg = loadCairnConfig(repoRoot);
230
+ const pairs = [];
231
+ const collect = (source, raw) => {
232
+ if (!Array.isArray(raw))
233
+ return;
234
+ for (const g of raw) {
235
+ if (typeof g === "string" && g.length > 0)
236
+ pairs.push({ source, glob: g });
237
+ }
238
+ };
239
+ collect("high_stakes_globs", cfg["high_stakes_globs"]);
240
+ const projectGlobs = cfg["project_globs"];
241
+ if (typeof projectGlobs === "object" && projectGlobs !== null) {
242
+ const pg = projectGlobs;
243
+ collect("project_globs.route_handler_globs", pg["route_handler_globs"]);
244
+ collect("project_globs.dto_globs", pg["dto_globs"]);
245
+ collect("project_globs.generator_source_globs", pg["generator_source_globs"]);
246
+ collect("project_globs.high_stakes_globs", pg["high_stakes_globs"]);
247
+ }
248
+ if (pairs.length === 0)
249
+ return [];
250
+ const files = [];
251
+ walkFs({
252
+ dir: repoRoot,
253
+ skipDirs: GLOB_WALK_SKIP_DIRS,
254
+ onFile: (rel) => {
255
+ files.push(rel);
256
+ },
257
+ });
258
+ if (files.length === 0)
259
+ return [];
260
+ const stale = pairs.filter((p) => !files.some((f) => matchGlob(f, p.glob)));
261
+ if (stale.length === 0) {
262
+ return [
263
+ {
264
+ group: "ground",
265
+ label: "config globs",
266
+ status: "ok",
267
+ detail: `${pairs.length} scope glob${pairs.length === 1 ? "" : "s"} all match tree files`,
268
+ },
269
+ ];
270
+ }
271
+ const sample = stale
272
+ .slice(0, 3)
273
+ .map((p) => `${p.glob} (${p.source})`)
274
+ .join("; ");
275
+ const more = stale.length > 3 ? ` (+${stale.length - 3} more)` : "";
276
+ return [
277
+ {
278
+ group: "ground",
279
+ label: "config globs",
280
+ status: "warn",
281
+ detail: `${stale.length} of ${pairs.length} scope glob(s) match zero tree files — ` +
282
+ `stale after a directory refactor? high-stakes weighting + route/DTO sensors silently skip these: ${sample}${more}`,
283
+ fixCommand: "cairn init --force",
284
+ },
285
+ ];
286
+ }
194
287
  function checkLedger(repoRoot, kind) {
195
288
  const path = join(repoRoot, ".cairn", "ground", kind, `${kind}.ledger.yaml`);
196
289
  if (!existsSync(path)) {