@intentic/sandbox-contract 1.176.3 → 1.209.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 (334) hide show
  1. package/README.md +65 -0
  2. package/dist/agent-catalog.d.ts +15 -2
  3. package/dist/agent-catalog.d.ts.map +1 -1
  4. package/dist/agent-catalog.js +50 -1
  5. package/dist/agent-catalog.js.map +1 -1
  6. package/dist/agent-run-model.d.ts +3 -0
  7. package/dist/agent-run-model.d.ts.map +1 -0
  8. package/dist/agent-run-model.js +10 -0
  9. package/dist/agent-run-model.js.map +1 -0
  10. package/dist/capability-env.d.ts +2 -0
  11. package/dist/capability-env.d.ts.map +1 -0
  12. package/dist/capability-env.js +2 -0
  13. package/dist/capability-env.js.map +1 -0
  14. package/dist/chores/chores.d.ts +9 -0
  15. package/dist/chores/chores.d.ts.map +1 -1
  16. package/dist/chores/chores.js +257 -18
  17. package/dist/chores/chores.js.map +1 -1
  18. package/dist/chores/digest.js +0 -0
  19. package/dist/chores/digest.js.map +1 -1
  20. package/dist/chores/extension-update.d.ts +9 -0
  21. package/dist/chores/extension-update.d.ts.map +1 -0
  22. package/dist/chores/extension-update.js +15 -0
  23. package/dist/chores/extension-update.js.map +1 -0
  24. package/dist/chores/fix-deps.d.ts +10 -0
  25. package/dist/chores/fix-deps.d.ts.map +1 -0
  26. package/dist/chores/fix-deps.js +18 -0
  27. package/dist/chores/fix-deps.js.map +1 -0
  28. package/dist/chores/index.d.ts +7 -2
  29. package/dist/chores/index.d.ts.map +1 -1
  30. package/dist/chores/index.js +4 -1
  31. package/dist/chores/index.js.map +1 -1
  32. package/dist/chores/probes.d.ts.map +1 -1
  33. package/dist/chores/probes.js +127 -3
  34. package/dist/chores/probes.js.map +1 -1
  35. package/dist/chores/stack.d.ts +27 -0
  36. package/dist/chores/stack.d.ts.map +1 -0
  37. package/dist/chores/stack.js +121 -0
  38. package/dist/chores/stack.js.map +1 -0
  39. package/dist/chores/verdict.d.ts +2 -1
  40. package/dist/chores/verdict.d.ts.map +1 -1
  41. package/dist/chores/verdict.js +53 -6
  42. package/dist/chores/verdict.js.map +1 -1
  43. package/dist/contract-lock.d.ts +3 -0
  44. package/dist/contract-lock.d.ts.map +1 -0
  45. package/dist/contract-lock.js +35 -0
  46. package/dist/contract-lock.js.map +1 -0
  47. package/dist/contracts/activity.contract.d.ts +10 -0
  48. package/dist/contracts/activity.contract.d.ts.map +1 -1
  49. package/dist/contracts/agent.contract.d.ts +230 -20
  50. package/dist/contracts/agent.contract.d.ts.map +1 -1
  51. package/dist/contracts/agent.contract.js +2 -1
  52. package/dist/contracts/agent.contract.js.map +1 -1
  53. package/dist/contracts/agents.contract.d.ts +847 -1
  54. package/dist/contracts/agents.contract.d.ts.map +1 -1
  55. package/dist/contracts/agents.contract.js +3 -1
  56. package/dist/contracts/agents.contract.js.map +1 -1
  57. package/dist/contracts/automations.contract.d.ts +89 -0
  58. package/dist/contracts/automations.contract.d.ts.map +1 -1
  59. package/dist/contracts/automations.contract.js +3 -1
  60. package/dist/contracts/automations.contract.js.map +1 -1
  61. package/dist/contracts/capabilities.contract.d.ts +108 -32
  62. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  63. package/dist/contracts/capabilities.contract.js +5 -1
  64. package/dist/contracts/capabilities.contract.js.map +1 -1
  65. package/dist/contracts/chores.contract.d.ts +43 -0
  66. package/dist/contracts/chores.contract.d.ts.map +1 -1
  67. package/dist/contracts/ci.contract.d.ts +5 -0
  68. package/dist/contracts/ci.contract.d.ts.map +1 -1
  69. package/dist/contracts/ci.contract.js +2 -2
  70. package/dist/contracts/ci.contract.js.map +1 -1
  71. package/dist/contracts/claude.contract.d.ts +3 -14
  72. package/dist/contracts/claude.contract.d.ts.map +1 -1
  73. package/dist/contracts/claude.contract.js +2 -3
  74. package/dist/contracts/claude.contract.js.map +1 -1
  75. package/dist/contracts/drafts.contract.d.ts +4 -0
  76. package/dist/contracts/drafts.contract.d.ts.map +1 -1
  77. package/dist/contracts/endpoints.contract.d.ts +28 -12
  78. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  79. package/dist/contracts/endpoints.contract.js +9 -0
  80. package/dist/contracts/endpoints.contract.js.map +1 -1
  81. package/dist/contracts/extensions.contract.d.ts +399 -69
  82. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  83. package/dist/contracts/extensions.contract.js +15 -1
  84. package/dist/contracts/extensions.contract.js.map +1 -1
  85. package/dist/contracts/git.contract.d.ts +209 -11
  86. package/dist/contracts/git.contract.d.ts.map +1 -1
  87. package/dist/contracts/git.contract.js +12 -2
  88. package/dist/contracts/git.contract.js.map +1 -1
  89. package/dist/contracts/grok.contract.d.ts +0 -13
  90. package/dist/contracts/grok.contract.d.ts.map +1 -1
  91. package/dist/contracts/grok.contract.js +1 -2
  92. package/dist/contracts/grok.contract.js.map +1 -1
  93. package/dist/contracts/host.contract.d.ts +39 -0
  94. package/dist/contracts/host.contract.d.ts.map +1 -1
  95. package/dist/contracts/host.contract.js +3 -2
  96. package/dist/contracts/host.contract.js.map +1 -1
  97. package/dist/contracts/loops.contract.d.ts +343 -0
  98. package/dist/contracts/loops.contract.d.ts.map +1 -0
  99. package/dist/contracts/loops.contract.js +11 -0
  100. package/dist/contracts/loops.contract.js.map +1 -0
  101. package/dist/contracts/panels.contract.d.ts +6 -0
  102. package/dist/contracts/panels.contract.d.ts.map +1 -1
  103. package/dist/contracts/personas.contract.d.ts +112 -0
  104. package/dist/contracts/personas.contract.d.ts.map +1 -0
  105. package/dist/contracts/personas.contract.js +13 -0
  106. package/dist/contracts/personas.contract.js.map +1 -0
  107. package/dist/contracts/ports.contract.d.ts +1 -0
  108. package/dist/contracts/ports.contract.d.ts.map +1 -1
  109. package/dist/contracts/prepush.contract.d.ts +1 -0
  110. package/dist/contracts/prepush.contract.d.ts.map +1 -1
  111. package/dist/contracts/prepush.contract.js.map +1 -1
  112. package/dist/contracts/{codex.contract.d.ts → providers.contract.d.ts} +11 -3
  113. package/dist/contracts/providers.contract.d.ts.map +1 -0
  114. package/dist/contracts/providers.contract.js +6 -0
  115. package/dist/contracts/providers.contract.js.map +1 -0
  116. package/dist/contracts/public.contract.d.ts +24 -0
  117. package/dist/contracts/public.contract.d.ts.map +1 -0
  118. package/dist/contracts/public.contract.js +8 -0
  119. package/dist/contracts/public.contract.js.map +1 -0
  120. package/dist/contracts/push.contract.d.ts +3 -3
  121. package/dist/contracts/secrets.contract.d.ts +9 -0
  122. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  123. package/dist/contracts/sessions.contract.d.ts +16 -1
  124. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  125. package/dist/contracts/sessions.contract.js +1 -1
  126. package/dist/contracts/sessions.contract.js.map +1 -1
  127. package/dist/contracts/settings.contract.d.ts +352 -58
  128. package/dist/contracts/settings.contract.d.ts.map +1 -1
  129. package/dist/contracts/settings.contract.js +2 -1
  130. package/dist/contracts/settings.contract.js.map +1 -1
  131. package/dist/contracts/share.contract.d.ts +55 -0
  132. package/dist/contracts/share.contract.d.ts.map +1 -0
  133. package/dist/contracts/share.contract.js +9 -0
  134. package/dist/contracts/share.contract.js.map +1 -0
  135. package/dist/contracts/skills.contract.d.ts +41 -0
  136. package/dist/contracts/skills.contract.d.ts.map +1 -0
  137. package/dist/contracts/skills.contract.js +9 -0
  138. package/dist/contracts/skills.contract.js.map +1 -0
  139. package/dist/contracts/system.contract.d.ts +204 -8
  140. package/dist/contracts/system.contract.d.ts.map +1 -1
  141. package/dist/contracts/system.contract.js +10 -2
  142. package/dist/contracts/system.contract.js.map +1 -1
  143. package/dist/contracts/workflows.contract.d.ts +575 -0
  144. package/dist/contracts/workflows.contract.d.ts.map +1 -0
  145. package/dist/contracts/workflows.contract.js +13 -0
  146. package/dist/contracts/workflows.contract.js.map +1 -0
  147. package/dist/contracts/workspace.contract.d.ts +18 -4
  148. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  149. package/dist/contracts/workspace.contract.js +6 -3
  150. package/dist/contracts/workspace.contract.js.map +1 -1
  151. package/dist/conversation-ids.d.ts +2 -0
  152. package/dist/conversation-ids.d.ts.map +1 -0
  153. package/dist/conversation-ids.js +113 -0
  154. package/dist/conversation-ids.js.map +1 -0
  155. package/dist/events.d.ts +523 -17
  156. package/dist/events.d.ts.map +1 -1
  157. package/dist/events.js +106 -13
  158. package/dist/events.js.map +1 -1
  159. package/dist/history-state.d.ts.map +1 -1
  160. package/dist/history-state.js +10 -0
  161. package/dist/history-state.js.map +1 -1
  162. package/dist/hostnames.d.ts +4 -0
  163. package/dist/hostnames.d.ts.map +1 -1
  164. package/dist/hostnames.js +7 -2
  165. package/dist/hostnames.js.map +1 -1
  166. package/dist/index.d.ts +3791 -478
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +39 -17
  169. package/dist/index.js.map +1 -1
  170. package/dist/listener-protocol.d.ts +77 -0
  171. package/dist/listener-protocol.d.ts.map +1 -0
  172. package/dist/listener-protocol.js +33 -0
  173. package/dist/listener-protocol.js.map +1 -0
  174. package/dist/model-order.d.ts +1 -0
  175. package/dist/model-order.d.ts.map +1 -1
  176. package/dist/model-order.js +16 -0
  177. package/dist/model-order.js.map +1 -1
  178. package/dist/output-fields.d.ts +30 -0
  179. package/dist/output-fields.d.ts.map +1 -0
  180. package/dist/output-fields.js +57 -0
  181. package/dist/output-fields.js.map +1 -0
  182. package/dist/path-refs.d.ts.map +1 -1
  183. package/dist/path-refs.js.map +1 -1
  184. package/dist/publish-drafts.d.ts +7 -0
  185. package/dist/publish-drafts.d.ts.map +1 -0
  186. package/dist/publish-drafts.js +20 -0
  187. package/dist/publish-drafts.js.map +1 -0
  188. package/dist/quick-model.d.ts +2 -1
  189. package/dist/quick-model.d.ts.map +1 -1
  190. package/dist/quick-model.js +18 -14
  191. package/dist/quick-model.js.map +1 -1
  192. package/dist/routes.d.ts +3 -0
  193. package/dist/routes.d.ts.map +1 -1
  194. package/dist/routes.js +63 -0
  195. package/dist/routes.js.map +1 -1
  196. package/dist/runtime-state.d.ts +33 -0
  197. package/dist/runtime-state.d.ts.map +1 -0
  198. package/dist/runtime-state.js +14 -0
  199. package/dist/runtime-state.js.map +1 -0
  200. package/dist/schemas.d.ts +4886 -851
  201. package/dist/schemas.d.ts.map +1 -1
  202. package/dist/schemas.js +993 -192
  203. package/dist/schemas.js.map +1 -1
  204. package/dist/search-globs.d.ts +6 -0
  205. package/dist/search-globs.d.ts.map +1 -0
  206. package/dist/search-globs.js +39 -0
  207. package/dist/search-globs.js.map +1 -0
  208. package/dist/share-paths.d.ts +8 -0
  209. package/dist/share-paths.d.ts.map +1 -0
  210. package/dist/share-paths.js +21 -0
  211. package/dist/share-paths.js.map +1 -0
  212. package/dist/title.d.ts.map +1 -1
  213. package/dist/title.js +1 -1
  214. package/dist/title.js.map +1 -1
  215. package/dist/tunnel-ids.d.ts +1 -0
  216. package/dist/tunnel-ids.d.ts.map +1 -1
  217. package/dist/tunnel-ids.js +1 -0
  218. package/dist/tunnel-ids.js.map +1 -1
  219. package/dist/versions.d.ts +4 -0
  220. package/dist/versions.d.ts.map +1 -0
  221. package/dist/versions.js +15 -0
  222. package/dist/versions.js.map +1 -0
  223. package/dist/workflow-faults.d.ts +4 -0
  224. package/dist/workflow-faults.d.ts.map +1 -0
  225. package/dist/workflow-faults.js +115 -0
  226. package/dist/workflow-faults.js.map +1 -0
  227. package/dist/workspace-state.d.ts +280 -1
  228. package/dist/workspace-state.d.ts.map +1 -1
  229. package/dist/workspace-state.js +163 -21
  230. package/dist/workspace-state.js.map +1 -1
  231. package/package.json +30 -6
  232. package/src/agent-catalog.test.ts +147 -7
  233. package/src/agent-catalog.ts +233 -21
  234. package/src/agent-run-model.test.ts +60 -0
  235. package/src/agent-run-model.ts +51 -0
  236. package/src/capability-env.ts +10 -0
  237. package/src/capability-ledger.test.ts +110 -0
  238. package/src/chores/chores.test.ts +32 -0
  239. package/src/chores/chores.ts +456 -32
  240. package/src/chores/digest.ts +0 -0
  241. package/src/chores/extension-update.ts +37 -0
  242. package/src/chores/fix-deps.ts +40 -0
  243. package/src/chores/index.ts +7 -2
  244. package/src/chores/probes.test.ts +155 -3
  245. package/src/chores/probes.ts +200 -4
  246. package/src/chores/stack.test.ts +126 -0
  247. package/src/chores/stack.ts +244 -0
  248. package/src/chores/verdict.test.ts +357 -19
  249. package/src/chores/verdict.ts +95 -21
  250. package/src/contract-lock.test.ts +17 -0
  251. package/src/contract-lock.ts +66 -0
  252. package/src/contracts/agent.contract.ts +6 -0
  253. package/src/contracts/agents.contract.ts +12 -0
  254. package/src/contracts/automations.contract.ts +17 -1
  255. package/src/contracts/capabilities.contract.ts +22 -0
  256. package/src/contracts/ci.contract.ts +10 -2
  257. package/src/contracts/claude.contract.ts +8 -4
  258. package/src/contracts/endpoints.contract.ts +21 -0
  259. package/src/contracts/extensions.contract.ts +42 -0
  260. package/src/contracts/git.contract.ts +33 -6
  261. package/src/contracts/grok.contract.ts +4 -3
  262. package/src/contracts/host.contract.ts +13 -2
  263. package/src/contracts/loops.contract.ts +62 -0
  264. package/src/contracts/personas.contract.ts +56 -0
  265. package/src/contracts/prepush.contract.ts +4 -3
  266. package/src/contracts/providers.contract.ts +23 -0
  267. package/src/contracts/public.contract.ts +14 -0
  268. package/src/contracts/sessions.contract.ts +5 -1
  269. package/src/contracts/settings.contract.ts +5 -0
  270. package/src/contracts/share.contract.ts +20 -0
  271. package/src/contracts/skills.contract.ts +24 -0
  272. package/src/contracts/system.contract.ts +27 -3
  273. package/src/contracts/workflows.contract.ts +78 -0
  274. package/src/contracts/workspace.contract.ts +10 -2
  275. package/src/conversation-ids.test.ts +25 -0
  276. package/src/conversation-ids.ts +160 -0
  277. package/src/events.test.ts +20 -1
  278. package/src/events.ts +404 -43
  279. package/src/history-state.ts +30 -1
  280. package/src/hostnames.ts +20 -6
  281. package/src/index.ts +58 -17
  282. package/src/listener-protocol.ts +96 -0
  283. package/src/model-order.test.ts +32 -1
  284. package/src/model-order.ts +56 -3
  285. package/src/output-fields.ts +111 -0
  286. package/src/path-refs.test.ts +4 -4
  287. package/src/path-refs.ts +5 -5
  288. package/src/publish-drafts.ts +59 -0
  289. package/src/quick-model.test.ts +70 -27
  290. package/src/quick-model.ts +76 -40
  291. package/src/routes.test.ts +91 -3
  292. package/src/routes.ts +135 -0
  293. package/src/runtime-state.test.ts +45 -0
  294. package/src/runtime-state.ts +124 -0
  295. package/src/schemas.test.ts +16 -29
  296. package/src/schemas.ts +3475 -552
  297. package/src/search-globs.test.ts +46 -0
  298. package/src/search-globs.ts +76 -0
  299. package/src/share-paths.test.ts +48 -0
  300. package/src/share-paths.ts +68 -0
  301. package/src/state-portability.ts +8 -8
  302. package/src/title.test.ts +20 -1
  303. package/src/title.ts +12 -3
  304. package/src/tunnel-ids.ts +15 -1
  305. package/src/versions.test.ts +39 -0
  306. package/src/versions.ts +48 -0
  307. package/src/workflow-faults.test.ts +127 -0
  308. package/src/workflow-faults.ts +188 -0
  309. package/src/workspace-state.test.ts +169 -15
  310. package/src/workspace-state.ts +427 -35
  311. package/dist/contracts/codex.contract.d.ts.map +0 -1
  312. package/dist/contracts/codex.contract.js +0 -6
  313. package/dist/contracts/codex.contract.js.map +0 -1
  314. package/dist/contracts/gemini.contract.d.ts +0 -16
  315. package/dist/contracts/gemini.contract.d.ts.map +0 -1
  316. package/dist/contracts/gemini.contract.js +0 -6
  317. package/dist/contracts/gemini.contract.js.map +0 -1
  318. package/dist/contracts/kimi.contract.d.ts +0 -16
  319. package/dist/contracts/kimi.contract.d.ts.map +0 -1
  320. package/dist/contracts/kimi.contract.js +0 -6
  321. package/dist/contracts/kimi.contract.js.map +0 -1
  322. package/dist/contracts/komodo.contract.d.ts +0 -125
  323. package/dist/contracts/komodo.contract.d.ts.map +0 -1
  324. package/dist/contracts/komodo.contract.js +0 -14
  325. package/dist/contracts/komodo.contract.js.map +0 -1
  326. package/dist/contracts/memory.contract.d.ts +0 -34
  327. package/dist/contracts/memory.contract.d.ts.map +0 -1
  328. package/dist/contracts/memory.contract.js +0 -9
  329. package/dist/contracts/memory.contract.js.map +0 -1
  330. package/src/contracts/codex.contract.ts +0 -10
  331. package/src/contracts/gemini.contract.ts +0 -11
  332. package/src/contracts/kimi.contract.ts +0 -9
  333. package/src/contracts/komodo.contract.ts +0 -36
  334. package/src/contracts/memory.contract.ts +0 -13
@@ -0,0 +1,244 @@
1
+ /* WHAT THIS REPOSITORY IS BUILT WITH, and the patterns that follow from it — the table the UI chores are written
2
+ * against, kept apart from both the probe that runs it and the chores that read it.
3
+ *
4
+ * It sits in the middle on purpose. probes.ts composes ONE ripgrep sweep out of the rules below, and chores.ts
5
+ * turns the counts that come back into findings and prose. Put the rules in either of those and the other one
6
+ * grows a copy: the probe would hard-code patterns the chore has to describe in words, or the chore would restate
7
+ * globs the probe already walked. Both copies would then be free to disagree about what a component is.
8
+ *
9
+ * NOTHING HERE IS A VERDICT. A framework is a set of dependency names, an idiom is a regex and the name of what
10
+ * replaced it. Which of those amount to work worth doing is the chore book's judgement, made in chores.ts against
11
+ * the same three rules everything else there obeys.
12
+ *
13
+ * THE PATTERNS ARE RIPGREP'S DIALECT, and they carry two constraints that are not obvious from reading them:
14
+ *
15
+ * No literal apostrophe, ever. The scan command wraps each pattern in shell single quotes, so a `'` inside one
16
+ * would end the quoting and hand the rest of the regex to the shell. Match quotes as `[\x22\x27]` instead —
17
+ * Rust's regex crate reads those escapes, and the shell never sees a quote character at all. stack.test.ts
18
+ * enforces this, because the failure is a probe that dies at three in the morning in someone else's workspace
19
+ * rather than anything a reader would notice here.
20
+ *
21
+ * No lookaround. Rust's regex crate has none, and reaching for ripgrep's PCRE2 mode to get it would make the
22
+ * sweep depend on how the box's ripgrep was compiled. A rule that seems to need it is usually asking a question
23
+ * about the FILE rather than about a line — see `absent` below, which is what that question actually is. */
24
+
25
+ export interface UiFramework {
26
+ readonly id: string;
27
+ readonly label: string;
28
+ // The dependency names that mean "this repository is built with it". Any one of them is enough.
29
+ readonly packages: readonly string[];
30
+ }
31
+
32
+ /* The three the maintenance surface knows how to say something specific about. Deliberately not "every framework
33
+ * with a npm package": a framework earns a row here by having idiom rules underneath it, and a table entry with
34
+ * no rules would let a chore announce it recognised Svelte and then have nothing to report. */
35
+ export const UI_FRAMEWORKS: readonly UiFramework[] = [
36
+ { id: `react`, label: `React`, packages: [`react`] },
37
+ { id: `vue`, label: `Vue`, packages: [`vue`] },
38
+ { id: `angular`, label: `Angular`, packages: [`@angular/core`] },
39
+ ];
40
+
41
+ // Tailwind is not in the table above because it is not a UI framework and does not own any idiom rules — it is a
42
+ // styling system that any of the three can be wearing, and it gates exactly one chore.
43
+ export const TAILWIND_PACKAGES: readonly string[] = [`tailwindcss`];
44
+
45
+ export const frameworksOf = (deps: readonly string[]): UiFramework[] =>
46
+ UI_FRAMEWORKS.filter((framework) => framework.packages.some((name) => deps.includes(name)));
47
+
48
+ export const usesTailwind = (deps: readonly string[]): boolean => TAILWIND_PACKAGES.some((name) => deps.includes(name));
49
+
50
+ /* Directories a UI scan must never walk, spelled out rather than left to the repository's .gitignore. ripgrep
51
+ * does honour .gitignore, and relying on that is how a repo whose build output is not ignored gets its own
52
+ * minified bundle reported back to it as a thousand hard-coded colours. Tests, specs and stories are excluded for
53
+ * a different reason: they are component-shaped files that are not components, and counting them would put a
54
+ * fixture at the top of every finding. */
55
+ export const SCAN_IGNORES: readonly string[] = [
56
+ `!**/node_modules/**`,
57
+ `!**/dist/**`,
58
+ `!**/build/**`,
59
+ `!**/.next/**`,
60
+ `!**/out/**`,
61
+ `!**/coverage/**`,
62
+ `!**/vendor/**`,
63
+ `!**/generated/**`,
64
+ `!**/*.{test,spec,stories}.*`,
65
+ ];
66
+
67
+ // What counts as a component file, across all three frameworks at once. The sweep cannot vary by repository — a
68
+ // probe's command is a fixed string — so it asks for all of them and a Vue-only repo simply has no `.tsx` files.
69
+ export const COMPONENT_GLOBS: readonly string[] = [`*.vue`, `*.tsx`, `*.jsx`, `*.component.ts`];
70
+
71
+ // Where a Tailwind class can appear. Wider than COMPONENT_GLOBS because a class list lives in markup as often as
72
+ // in a component — an Angular template and a plain .html page both style with the same utilities.
73
+ export const MARKUP_GLOBS: readonly string[] = [`*.vue`, `*.tsx`, `*.jsx`, `*.html`, `*.svelte`, `*.astro`];
74
+
75
+ /* THE DESIGN SYSTEM BYPASS. Not "any arbitrary value" — `grid-cols-[1fr_auto]` and `w-[calc(100%-2rem)]` are
76
+ * Tailwind working as designed, and a chore that counted them would be objecting to the feature rather than to
77
+ * anything wrong. What this matches is the two arbitrary values that route around a decision the theme already
78
+ * made: a colour that is not in the palette, and a pixel size that is not on the spacing or type scale.
79
+ *
80
+ * The leading `-` is load-bearing. It anchors the match to a utility prefix (`bg-`, `text-`, `w-`), so a bare
81
+ * `[...]` in ordinary prose or an array index cannot be mistaken for a class. */
82
+ export const BYPASS_PATTERN = `-\\[(#[0-9a-fA-F]{3,8}|(rgb|hsl)a?\\(|[0-9]+(\\.[0-9]+)?px)`;
83
+
84
+ export interface IdiomRule {
85
+ readonly id: string;
86
+ // Which framework's migration this belongs to, so a finding can be grouped under the thing that moved on.
87
+ readonly framework: string;
88
+ // What the repository still has, named as the reader would name it.
89
+ readonly label: string;
90
+ // What replaced it. Carried so the prompt can say where to go rather than only what to leave — an agent told
91
+ // "you still use NgModule" and nothing else will pick a destination, and it may not pick this one.
92
+ readonly replacement: string;
93
+ readonly pattern: string;
94
+ readonly globs: readonly string[];
95
+ /* THE IDIOM IS THE PATTERN BEING MISSING, not present — `pattern` names the NEW way, and the file is on the
96
+ * old one precisely because the new one does not appear in it anywhere. The sweep spells this
97
+ * `--files-without-match`.
98
+ *
99
+ * This exists because the alternative got it wrong in a way that is worth remembering. "A Vue file not using
100
+ * <script setup>" was first written as a lookahead over `<script`, which matches per LINE: a migrated
101
+ * component with a second plain `<script>` block for defineOptions, or one that merely mentions `<script` in
102
+ * a comment, both read as un-migrated. It reported five files in an application whose 167 SFCs are every one
103
+ * of them migrated. The question was never "is there a line like this" — it is "does this file contain the
104
+ * new idiom at all", which is one flag rather than a cleverer regex, and it costs no PCRE2.
105
+ *
106
+ * The globs carry more weight on an absent rule than on a normal one, and narrowly is the only safe way to
107
+ * write them: the population is every file they match, so `*.ts` on an absent rule reports the entire
108
+ * repository. Keep them to the file type the migration is actually about. */
109
+ readonly absent?: true;
110
+ }
111
+
112
+ /* THE IDIOMS THEIR OWN MAINTAINERS HAVE MOVED ON FROM. Every rule here names something the framework's own
113
+ * documentation now steers people away from, and every one of them still works — which is exactly why they
114
+ * accumulate, and why no editor and no linter will bring them up unprompted.
115
+ *
116
+ * High confidence over coverage. Each pattern is one a reader can check by eye against a file, and the ones that
117
+ * would need real parsing to get right are left out rather than approximated: a rule that is wrong a third of the
118
+ * time trains people to stop reading the row it appears in. */
119
+ export const IDIOM_RULES: readonly IdiomRule[] = [
120
+ {
121
+ id: `react-class-component`,
122
+ framework: `react`,
123
+ label: `class components`,
124
+ replacement: `function components with hooks`,
125
+ pattern: `extends\\s+(React\\.)?(Pure)?Component\\b`,
126
+ globs: [`*.tsx`, `*.jsx`],
127
+ },
128
+ {
129
+ id: `react-legacy-render`,
130
+ framework: `react`,
131
+ label: `the legacy ReactDOM.render entry point`,
132
+ replacement: `createRoot from react-dom/client`,
133
+ pattern: `ReactDOM\\.render\\(`,
134
+ globs: [`*.tsx`, `*.jsx`, `*.ts`, `*.js`],
135
+ },
136
+ {
137
+ id: `react-unsafe-lifecycle`,
138
+ framework: `react`,
139
+ // Named as what they are rather than by the UNSAFE_ prefix: a reader who has never renamed one would not
140
+ // recognise "UNSAFE_componentWillMount" as describing their own file.
141
+ label: `the pre-16.3 lifecycle methods`,
142
+ replacement: `effects, or the UNSAFE_ prefixed names if the behaviour is genuinely wanted`,
143
+ pattern: `\\bcomponentWill(Mount|ReceiveProps|Update)\\b`,
144
+ globs: [`*.tsx`, `*.jsx`],
145
+ },
146
+ {
147
+ id: `react-prop-types`,
148
+ framework: `react`,
149
+ label: `runtime prop-types`,
150
+ replacement: `the component's own TypeScript props type`,
151
+ pattern: `from\\s+[\\x22\\x27]prop-types[\\x22\\x27]`,
152
+ globs: [`*.tsx`, `*.jsx`],
153
+ },
154
+ {
155
+ id: `vue-options-api`,
156
+ framework: `vue`,
157
+ label: `the Options API`,
158
+ replacement: `<script setup> with the Composition API`,
159
+ // The new idiom, inverted by `absent` below — an SFC that never opens a `<script setup>` tag is still on
160
+ // the old one. A file with no script block at all is swept up too, and that is the honest reading: it has
161
+ // not been migrated because there was nothing there to migrate.
162
+ pattern: `<script[^>]*\\bsetup\\b`,
163
+ globs: [`*.vue`],
164
+ absent: true,
165
+ },
166
+ {
167
+ id: `vue-2-lifecycle`,
168
+ framework: `vue`,
169
+ label: `the Vue 2 teardown hooks`,
170
+ replacement: `beforeUnmount and unmounted`,
171
+ pattern: `\\b(beforeDestroy|destroyed)\\s*[(:]`,
172
+ globs: [`*.vue`, `*.ts`, `*.js`],
173
+ },
174
+ {
175
+ id: `vue-global-api`,
176
+ framework: `vue`,
177
+ label: `the Vue 2 global constructor`,
178
+ replacement: `createApp and defineComponent`,
179
+ pattern: `\\b(new\\s+Vue\\(|Vue\\.extend\\()`,
180
+ globs: [`*.vue`, `*.ts`, `*.js`],
181
+ },
182
+ {
183
+ id: `angular-ngmodule`,
184
+ framework: `angular`,
185
+ label: `NgModule declarations`,
186
+ replacement: `standalone components`,
187
+ pattern: `@NgModule\\(`,
188
+ globs: [`*.ts`],
189
+ },
190
+ {
191
+ id: `angular-structural-directives`,
192
+ framework: `angular`,
193
+ label: `the structural directives`,
194
+ replacement: `the built-in control flow blocks`,
195
+ pattern: `\\*ng(If|For|Switch)\\b`,
196
+ globs: [`*.html`, `*.ts`],
197
+ },
198
+ {
199
+ id: `angular-module-providers`,
200
+ framework: `angular`,
201
+ label: `the module-based providers`,
202
+ replacement: `the provide* functions in the application config`,
203
+ pattern: `\\b(HttpClientModule|BrowserAnimationsModule|RouterModule\\.forRoot)\\b`,
204
+ globs: [`*.ts`],
205
+ },
206
+ ];
207
+
208
+ export const idiomRule = (id: string): IdiomRule | undefined => IDIOM_RULES.find((rule) => rule.id === id);
209
+
210
+ /* Two tools naming the same file two ways. The scan reports repo-relative paths because that is what ripgrep
211
+ * prints; jscpd prints whatever it was handed, which for a `.` scan is the same path behind a `./`. Normalising
212
+ * here rather than at each comparison keeps the component-overlap chore from quietly matching nothing because one
213
+ * side had two extra characters. */
214
+ export const normalizePath = (path: string): string => path.replace(/^\.\//, ``);
215
+
216
+ // Below this a stem is too short to have survived the stripping above with its meaning intact — `H1` and `H2`
217
+ // would both reduce to `h` and read as one family of heading components that are not duplicates of anything.
218
+ const MIN_STEM = 3;
219
+
220
+ const QUALIFIER_PREFIX = /^(base|the)/;
221
+ const QUALIFIER_SUFFIX = /(v[0-9]+|new|old|legacy|copy|component|[0-9]+)$/;
222
+
223
+ /* THE NAME TWO COMPONENTS SHARE WHEN THEY ARE THE SAME COMPONENT TWICE — or `undefined` when the file has no
224
+ * name worth comparing.
225
+ *
226
+ * This is a normaliser, not a similarity score, and that is the point: it answers a question the reader can check
227
+ * by eye. `BaseButton.vue` and `ButtonV2.tsx` both reduce to `button`, so a panel claiming they are the same
228
+ * component is making a claim anyone can agree or disagree with in a second. A fuzzy distance would be right more
229
+ * often and checkable never, and an unarguable finding is one nobody can improve.
230
+ *
231
+ * `index` is dropped rather than normalised. Every barrel file in the repository is called it, and a family of
232
+ * forty index files is a finding about the naming convention rather than about any duplication. */
233
+ export const componentStem = (path: string): string | undefined => {
234
+ const file = normalizePath(path).split(`/`).pop() ?? ``;
235
+ // `.component.ts` loses both suffixes, `.vue` loses one — taking everything before the first dot handles both
236
+ // without a table, since a component's name is never the part after a dot.
237
+ const base = (file.split(`.`)[0] ?? ``).toLowerCase().replace(/[^a-z0-9]/g, ``);
238
+ if (base === `` || base === `index`) {
239
+ return undefined;
240
+ }
241
+ const withoutSuffix = base.replace(QUALIFIER_SUFFIX, ``);
242
+ const stem = (withoutSuffix.length >= MIN_STEM ? withoutSuffix : base).replace(QUALIFIER_PREFIX, ``);
243
+ return stem.length >= MIN_STEM ? stem : base;
244
+ };