quiver-cli 1.3.0 → 2.0.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 (384) hide show
  1. package/README.md +197 -239
  2. package/dist/cli.js +3163 -2040
  3. package/package.json +10 -11
  4. package/template/.agents/AGENTS.md +0 -26
  5. package/template/.agents/commands/cp.md +0 -108
  6. package/template/.agents/commands/next-setup.md +0 -1064
  7. package/template/.agents/commands/review.md +0 -68
  8. package/template/.agents/commands/terraform-readme.md +0 -38
  9. package/template/.agents/config.json +0 -80
  10. package/template/.agents/plugins/opencode/rtk.ts +0 -34
  11. package/template/.agents/skills/agent-browser/SKILL.md +0 -51
  12. package/template/.agents/skills/apps/skybridge/SKILL.md +0 -50
  13. package/template/.agents/skills/code/cleanup/SKILL.md +0 -26
  14. package/template/.agents/skills/code/improve/SKILL.md +0 -122
  15. package/template/.agents/skills/code/improve/references/audit-playbook.md +0 -130
  16. package/template/.agents/skills/code/improve/references/closing-the-loop.md +0 -96
  17. package/template/.agents/skills/code/improve/references/plan-template.md +0 -197
  18. package/template/.agents/skills/code/vercel-react-best-practices/AGENTS.md +0 -3810
  19. package/template/.agents/skills/code/vercel-react-best-practices/README.md +0 -123
  20. package/template/.agents/skills/code/vercel-react-best-practices/SKILL.md +0 -149
  21. package/template/.agents/skills/code/vercel-react-best-practices/metadata.json +0 -15
  22. package/template/.agents/skills/code/vercel-react-best-practices/rules/_sections.md +0 -46
  23. package/template/.agents/skills/code/vercel-react-best-practices/rules/_template.md +0 -28
  24. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-effect-event-deps.md +0 -56
  25. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  26. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
  27. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
  28. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-api-routes.md +0 -38
  29. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +0 -37
  30. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-defer-await.md +0 -82
  31. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-dependencies.md +0 -51
  32. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-parallel.md +0 -28
  33. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
  34. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-analyzable-paths.md +0 -63
  35. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -60
  36. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
  37. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
  38. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  39. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-preload.md +0 -50
  40. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
  41. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
  42. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
  43. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
  44. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
  45. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
  46. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
  47. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
  48. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
  49. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-early-exit.md +0 -50
  50. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
  51. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
  52. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-index-maps.md +0 -37
  53. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
  54. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
  55. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-request-idle-callback.md +0 -105
  56. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
  57. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
  58. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-activity.md +0 -26
  59. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  60. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
  61. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
  62. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  63. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  64. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
  65. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
  66. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
  67. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
  68. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
  69. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
  70. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
  71. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
  72. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
  73. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
  74. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  75. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
  76. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo.md +0 -44
  77. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
  78. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
  79. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
  80. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +0 -64
  81. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
  82. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-deferred-value.md +0 -59
  83. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
  84. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
  85. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
  86. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
  87. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-react.md +0 -76
  88. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
  89. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -149
  90. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-no-shared-module-state.md +0 -50
  91. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
  92. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +0 -34
  93. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-serialization.md +0 -38
  94. package/template/.agents/skills/data/prisma-cli/SKILL.md +0 -247
  95. package/template/.agents/skills/data/prisma-cli/references/db-execute.md +0 -78
  96. package/template/.agents/skills/data/prisma-cli/references/db-pull.md +0 -185
  97. package/template/.agents/skills/data/prisma-cli/references/db-push.md +0 -148
  98. package/template/.agents/skills/data/prisma-cli/references/db-seed.md +0 -188
  99. package/template/.agents/skills/data/prisma-cli/references/debug.md +0 -46
  100. package/template/.agents/skills/data/prisma-cli/references/dev.md +0 -157
  101. package/template/.agents/skills/data/prisma-cli/references/format.md +0 -48
  102. package/template/.agents/skills/data/prisma-cli/references/generate.md +0 -173
  103. package/template/.agents/skills/data/prisma-cli/references/init.md +0 -136
  104. package/template/.agents/skills/data/prisma-cli/references/mcp.md +0 -38
  105. package/template/.agents/skills/data/prisma-cli/references/migrate-deploy.md +0 -127
  106. package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +0 -145
  107. package/template/.agents/skills/data/prisma-cli/references/migrate-diff.md +0 -89
  108. package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +0 -78
  109. package/template/.agents/skills/data/prisma-cli/references/migrate-resolve.md +0 -57
  110. package/template/.agents/skills/data/prisma-cli/references/migrate-status.md +0 -65
  111. package/template/.agents/skills/data/prisma-cli/references/studio.md +0 -137
  112. package/template/.agents/skills/data/prisma-cli/references/validate.md +0 -53
  113. package/template/.agents/skills/data/prisma-client-api/SKILL.md +0 -216
  114. package/template/.agents/skills/data/prisma-client-api/references/client-methods.md +0 -223
  115. package/template/.agents/skills/data/prisma-client-api/references/constructor.md +0 -208
  116. package/template/.agents/skills/data/prisma-client-api/references/filters.md +0 -256
  117. package/template/.agents/skills/data/prisma-client-api/references/model-queries.md +0 -281
  118. package/template/.agents/skills/data/prisma-client-api/references/query-options.md +0 -276
  119. package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +0 -194
  120. package/template/.agents/skills/data/prisma-client-api/references/relations.md +0 -308
  121. package/template/.agents/skills/data/prisma-client-api/references/transactions.md +0 -184
  122. package/template/.agents/skills/design/impeccable/SKILL.md +0 -84
  123. package/template/.agents/skills/design/impeccable/reference/adapt.md +0 -312
  124. package/template/.agents/skills/design/impeccable/reference/adapt.native.md +0 -58
  125. package/template/.agents/skills/design/impeccable/reference/android.md +0 -40
  126. package/template/.agents/skills/design/impeccable/reference/animate.md +0 -86
  127. package/template/.agents/skills/design/impeccable/reference/audit.md +0 -136
  128. package/template/.agents/skills/design/impeccable/reference/audit.native.md +0 -139
  129. package/template/.agents/skills/design/impeccable/reference/bolder.md +0 -31
  130. package/template/.agents/skills/design/impeccable/reference/clarify.md +0 -94
  131. package/template/.agents/skills/design/impeccable/reference/colorize.md +0 -86
  132. package/template/.agents/skills/design/impeccable/reference/craft-floor.md +0 -42
  133. package/template/.agents/skills/design/impeccable/reference/craft.md +0 -5
  134. package/template/.agents/skills/design/impeccable/reference/critique.md +0 -788
  135. package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +0 -90
  136. package/template/.agents/skills/design/impeccable/reference/degraded/documenter.md +0 -24
  137. package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +0 -37
  138. package/template/.agents/skills/design/impeccable/reference/degraded/manual-edit-applier.md +0 -92
  139. package/template/.agents/skills/design/impeccable/reference/delight.md +0 -70
  140. package/template/.agents/skills/design/impeccable/reference/distill.md +0 -111
  141. package/template/.agents/skills/design/impeccable/reference/doctor.md +0 -53
  142. package/template/.agents/skills/design/impeccable/reference/document.md +0 -416
  143. package/template/.agents/skills/design/impeccable/reference/extract.md +0 -69
  144. package/template/.agents/skills/design/impeccable/reference/harden.md +0 -336
  145. package/template/.agents/skills/design/impeccable/reference/hooks.md +0 -105
  146. package/template/.agents/skills/design/impeccable/reference/init.md +0 -125
  147. package/template/.agents/skills/design/impeccable/reference/ios.md +0 -45
  148. package/template/.agents/skills/design/impeccable/reference/layout.md +0 -84
  149. package/template/.agents/skills/design/impeccable/reference/live-setup.md +0 -102
  150. package/template/.agents/skills/design/impeccable/reference/live.md +0 -323
  151. package/template/.agents/skills/design/impeccable/reference/new-work.md +0 -105
  152. package/template/.agents/skills/design/impeccable/reference/onboard.md +0 -234
  153. package/template/.agents/skills/design/impeccable/reference/operate.md +0 -61
  154. package/template/.agents/skills/design/impeccable/reference/optimize.md +0 -258
  155. package/template/.agents/skills/design/impeccable/reference/overdrive.md +0 -127
  156. package/template/.agents/skills/design/impeccable/reference/polish.md +0 -97
  157. package/template/.agents/skills/design/impeccable/reference/quieter.md +0 -99
  158. package/template/.agents/skills/design/impeccable/reference/routing.md +0 -18
  159. package/template/.agents/skills/design/impeccable/reference/shape.md +0 -59
  160. package/template/.agents/skills/design/impeccable/reference/typeset.md +0 -80
  161. package/template/.agents/skills/design/impeccable/reference/visualize.md +0 -47
  162. package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +0 -94
  163. package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +0 -558
  164. package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +0 -334
  165. package/template/.agents/skills/design/impeccable/scripts/context.mjs +0 -1450
  166. package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +0 -213
  167. package/template/.agents/skills/design/impeccable/scripts/detect-csp.mjs +0 -198
  168. package/template/.agents/skills/design/impeccable/scripts/detect.mjs +0 -21
  169. package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +0 -2023
  170. package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +0 -438
  171. package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +0 -983
  172. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +0 -8283
  173. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +0 -50
  174. package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +0 -372
  175. package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +0 -768
  176. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +0 -1186
  177. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +0 -264
  178. package/template/.agents/skills/design/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +0 -189
  179. package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +0 -18
  180. package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +0 -212
  181. package/template/.agents/skills/design/impeccable/scripts/detector/profile/profiler.mjs +0 -166
  182. package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +0 -617
  183. package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +0 -5580
  184. package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +0 -124
  185. package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +0 -112
  186. package/template/.agents/skills/design/impeccable/scripts/detector/shared/fonts.mjs +0 -30
  187. package/template/.agents/skills/design/impeccable/scripts/detector/shared/inline-ignores.mjs +0 -148
  188. package/template/.agents/skills/design/impeccable/scripts/detector/shared/page.mjs +0 -7
  189. package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +0 -336
  190. package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +0 -133
  191. package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +0 -240
  192. package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +0 -741
  193. package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +0 -516
  194. package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +0 -2100
  195. package/template/.agents/skills/design/impeccable/scripts/hook.mjs +0 -78
  196. package/template/.agents/skills/design/impeccable/scripts/lib/artifact-schema.mjs +0 -93
  197. package/template/.agents/skills/design/impeccable/scripts/lib/composition-catalog.mjs +0 -200
  198. package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +0 -357
  199. package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +0 -842
  200. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +0 -658
  201. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +0 -137
  202. package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +0 -69
  203. package/template/.agents/skills/design/impeccable/scripts/lib/provider.mjs +0 -5
  204. package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +0 -362
  205. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +0 -457
  206. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-notice.mjs +0 -169
  207. package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +0 -457
  208. package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +0 -151
  209. package/template/.agents/skills/design/impeccable/scripts/lib/target-args.mjs +0 -42
  210. package/template/.agents/skills/design/impeccable/scripts/lib/target-slug.mjs +0 -33
  211. package/template/.agents/skills/design/impeccable/scripts/lib/template-extensions.mjs +0 -146
  212. package/template/.agents/skills/design/impeccable/scripts/live/accept-css.mjs +0 -617
  213. package/template/.agents/skills/design/impeccable/scripts/live/accept-verify.mjs +0 -60
  214. package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +0 -55
  215. package/template/.agents/skills/design/impeccable/scripts/live/completion.mjs +0 -28
  216. package/template/.agents/skills/design/impeccable/scripts/live/event-validation.mjs +0 -199
  217. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/astro.mjs +0 -47
  218. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/detect-utils.mjs +0 -73
  219. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/index.mjs +0 -143
  220. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/journal.mjs +0 -197
  221. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nextjs.mjs +0 -49
  222. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nuxt.mjs +0 -161
  223. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/script-src.mjs +0 -17
  224. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/static-html.mjs +0 -26
  225. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/sveltekit.mjs +0 -71
  226. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tag-strategy.mjs +0 -247
  227. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tanstack-start.mjs +0 -70
  228. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/vite-generic.mjs +0 -42
  229. package/template/.agents/skills/design/impeccable/scripts/live/generation-preflight.mjs +0 -149
  230. package/template/.agents/skills/design/impeccable/scripts/live/insert-ui.mjs +0 -458
  231. package/template/.agents/skills/design/impeccable/scripts/live/instructions.mjs +0 -142
  232. package/template/.agents/skills/design/impeccable/scripts/live/manual-apply.mjs +0 -939
  233. package/template/.agents/skills/design/impeccable/scripts/live/manual-edit-routes.mjs +0 -357
  234. package/template/.agents/skills/design/impeccable/scripts/live/manual-edits-buffer.mjs +0 -152
  235. package/template/.agents/skills/design/impeccable/scripts/live/poll-lanes.mjs +0 -14
  236. package/template/.agents/skills/design/impeccable/scripts/live/roots.mjs +0 -508
  237. package/template/.agents/skills/design/impeccable/scripts/live/session-store.mjs +0 -563
  238. package/template/.agents/skills/design/impeccable/scripts/live/source-lock.mjs +0 -105
  239. package/template/.agents/skills/design/impeccable/scripts/live/source-search.mjs +0 -105
  240. package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +0 -961
  241. package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +0 -1342
  242. package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +0 -316
  243. package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +0 -280
  244. package/template/.agents/skills/design/impeccable/scripts/live/ui-core.mjs +0 -180
  245. package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +0 -171
  246. package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +0 -954
  247. package/template/.agents/skills/design/impeccable/scripts/live-browser-dom.js +0 -146
  248. package/template/.agents/skills/design/impeccable/scripts/live-browser-session.js +0 -123
  249. package/template/.agents/skills/design/impeccable/scripts/live-browser.js +0 -12512
  250. package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +0 -1244
  251. package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +0 -107
  252. package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +0 -683
  253. package/template/.agents/skills/design/impeccable/scripts/live-discard-manual-edits.mjs +0 -51
  254. package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +0 -503
  255. package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +0 -292
  256. package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +0 -368
  257. package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +0 -429
  258. package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +0 -123
  259. package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +0 -1661
  260. package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +0 -71
  261. package/template/.agents/skills/design/impeccable/scripts/live-target.mjs +0 -30
  262. package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +0 -927
  263. package/template/.agents/skills/design/impeccable/scripts/live.mjs +0 -359
  264. package/template/.agents/skills/design/impeccable/scripts/modern-screenshot.umd.js +0 -14
  265. package/template/.agents/skills/design/impeccable/scripts/palette.mjs +0 -628
  266. package/template/.agents/skills/design/impeccable/scripts/pin.mjs +0 -221
  267. package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +0 -932
  268. package/template/.agents/skills/design/impeccable/scripts/surface-brief.mjs +0 -74
  269. package/template/.agents/skills/design/shadcn/SKILL.md +0 -277
  270. package/template/.agents/skills/design/shadcn/agents/openai.yml +0 -5
  271. package/template/.agents/skills/design/shadcn/assets/shadcn-small.png +0 -0
  272. package/template/.agents/skills/design/shadcn/assets/shadcn.png +0 -0
  273. package/template/.agents/skills/design/shadcn/cli.md +0 -290
  274. package/template/.agents/skills/design/shadcn/customization.md +0 -209
  275. package/template/.agents/skills/design/shadcn/evals/evals.json +0 -77
  276. package/template/.agents/skills/design/shadcn/mcp.md +0 -105
  277. package/template/.agents/skills/design/shadcn/registry.md +0 -277
  278. package/template/.agents/skills/design/shadcn/rules/base-vs-radix.md +0 -306
  279. package/template/.agents/skills/design/shadcn/rules/chat.md +0 -224
  280. package/template/.agents/skills/design/shadcn/rules/composition.md +0 -213
  281. package/template/.agents/skills/design/shadcn/rules/forms.md +0 -192
  282. package/template/.agents/skills/design/shadcn/rules/icons.md +0 -101
  283. package/template/.agents/skills/design/shadcn/rules/styling.md +0 -185
  284. package/template/.agents/skills/find-skills/SKILL.md +0 -141
  285. package/template/.agents/skills/hono/SKILL.md +0 -579
  286. package/template/.agents/skills/integrations/langfuse/SKILL.md +0 -146
  287. package/template/.agents/skills/integrations/langfuse/references/ci-cd.md +0 -41
  288. package/template/.agents/skills/integrations/langfuse/references/cli.md +0 -60
  289. package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +0 -88
  290. package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +0 -121
  291. package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +0 -291
  292. package/template/.agents/skills/integrations/langfuse/references/prompt-engineering.md +0 -35
  293. package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +0 -79
  294. package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +0 -55
  295. package/template/.agents/skills/integrations/langfuse/references/trace-evaluator-upgrade.md +0 -76
  296. package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +0 -92
  297. package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +0 -73
  298. package/template/.agents/skills/integrations/posthog/SKILL.md +0 -102
  299. package/template/.agents/skills/integrations/posthog/references/error-tracking-alerts.md +0 -63
  300. package/template/.agents/skills/integrations/posthog/references/error-tracking-assigning-issues.md +0 -77
  301. package/template/.agents/skills/integrations/posthog/references/error-tracking-fingerprints.md +0 -57
  302. package/template/.agents/skills/integrations/posthog/references/error-tracking-monitoring.md +0 -140
  303. package/template/.agents/skills/integrations/posthog/references/error-tracking-nextjs.md +0 -490
  304. package/template/.agents/skills/integrations/posthog/references/error-tracking-source-maps.md +0 -45
  305. package/template/.agents/skills/integrations/posthog/references/feature-flags-best-practices.md +0 -139
  306. package/template/.agents/skills/integrations/posthog/references/feature-flags-react.md +0 -302
  307. package/template/.agents/skills/integrations/posthog/references/identify-users.md +0 -202
  308. package/template/.agents/skills/integrations/posthog/references/integration-example.md +0 -706
  309. package/template/.agents/skills/integrations/posthog/references/integration-nextjs.md +0 -385
  310. package/template/.agents/skills/integrations/posthog/references/integration-step-1-begin.md +0 -43
  311. package/template/.agents/skills/integrations/posthog/references/integration-step-2-edit.md +0 -37
  312. package/template/.agents/skills/integrations/posthog/references/integration-step-3-revise.md +0 -22
  313. package/template/.agents/skills/integrations/posthog/references/integration-step-4-conclude.md +0 -38
  314. package/template/.agents/skills/integrations/posthog/references/llm-analytics-anthropic.md +0 -200
  315. package/template/.agents/skills/integrations/posthog/references/llm-analytics-basics.md +0 -62
  316. package/template/.agents/skills/integrations/posthog/references/llm-analytics-costs.md +0 -197
  317. package/template/.agents/skills/integrations/posthog/references/llm-analytics-manual-capture.md +0 -397
  318. package/template/.agents/skills/integrations/posthog/references/llm-analytics-traces.md +0 -98
  319. package/template/.agents/skills/integrations/posthog/references/llm-analytics-vercel-ai.md +0 -120
  320. package/template/.agents/skills/repo/repo-ci/SKILL.md +0 -265
  321. package/template/.agents/skills/repo/repo-init-next-js/SKILL.md +0 -129
  322. package/template/.agents/skills/repo/repo-init-next-js/references/file-contents.md +0 -800
  323. package/template/.agents/skills/repo/repo-init-next-js/scripts/setup.sh +0 -47
  324. package/template/.agents/skills/repo/repo-init-node/SKILL.md +0 -196
  325. package/template/.agents/skills/skill-creator/LICENSE.txt +0 -202
  326. package/template/.agents/skills/skill-creator/SKILL.md +0 -485
  327. package/template/.agents/skills/skill-creator/agents/analyzer.md +0 -274
  328. package/template/.agents/skills/skill-creator/agents/comparator.md +0 -202
  329. package/template/.agents/skills/skill-creator/agents/grader.md +0 -223
  330. package/template/.agents/skills/skill-creator/assets/eval_review.html +0 -146
  331. package/template/.agents/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  332. package/template/.agents/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  333. package/template/.agents/skills/skill-creator/references/schemas.md +0 -430
  334. package/template/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  335. package/template/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  336. package/template/.agents/skills/skill-creator/scripts/generate_report.py +0 -326
  337. package/template/.agents/skills/skill-creator/scripts/improve_description.py +0 -247
  338. package/template/.agents/skills/skill-creator/scripts/package_skill.py +0 -136
  339. package/template/.agents/skills/skill-creator/scripts/quick_validate.py +0 -103
  340. package/template/.agents/skills/skill-creator/scripts/run_eval.py +0 -310
  341. package/template/.agents/skills/skill-creator/scripts/run_loop.py +0 -328
  342. package/template/.agents/skills/skill-creator/scripts/utils.py +0 -47
  343. package/template/.agents/skills/supabase/CHANGELOG.md +0 -71
  344. package/template/.agents/skills/supabase/SKILL.md +0 -145
  345. package/template/.agents/skills/supabase/assets/feedback-issue-template.md +0 -17
  346. package/template/.agents/skills/supabase/references/skill-feedback.md +0 -17
  347. package/template/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +0 -73
  348. package/template/.agents/skills/supabase-postgres-best-practices/SKILL.md +0 -64
  349. package/template/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +0 -170
  350. package/template/.agents/skills/supabase-postgres-best-practices/references/_sections.md +0 -39
  351. package/template/.agents/skills/supabase-postgres-best-practices/references/_template.md +0 -34
  352. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +0 -55
  353. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +0 -49
  354. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +0 -46
  355. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +0 -44
  356. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +0 -41
  357. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +0 -46
  358. package/template/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +0 -54
  359. package/template/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +0 -53
  360. package/template/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +0 -50
  361. package/template/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +0 -50
  362. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +0 -56
  363. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +0 -68
  364. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +0 -50
  365. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +0 -54
  366. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +0 -45
  367. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +0 -55
  368. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +0 -55
  369. package/template/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +0 -44
  370. package/template/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +0 -40
  371. package/template/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +0 -48
  372. package/template/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +0 -43
  373. package/template/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +0 -45
  374. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +0 -80
  375. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +0 -46
  376. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +0 -59
  377. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +0 -55
  378. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +0 -55
  379. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +0 -61
  380. package/template/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +0 -54
  381. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +0 -50
  382. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +0 -63
  383. package/template/.agents/skills/writing/humanizer/SKILL.md +0 -621
  384. package/template/.agents/upstreams.json +0 -116
@@ -1,768 +0,0 @@
1
- import { GENERIC_FONTS, OVERUSED_FONTS, EM_DASH_FLOOR, EM_DASH_CHARS_PER_DASH } from '../../shared/constants.mjs';
2
- import { isNeutralColor } from '../../shared/color.mjs';
3
- import { extractGoogleFontFamilies } from '../../shared/fonts.mjs';
4
- import { checkSourceDesignSystem } from '../../design-system.mjs';
5
- import { scanCssTextForGlow, scanCssTextForGridBackground, scanCssTextForMarquee, scanCssTextForPseudoStripe, scanCssTextForRadialHalo } from '../../rules/checks.mjs';
6
- import { isFullPage } from '../../shared/page.mjs';
7
- import { applyInlineIgnores } from '../../shared/inline-ignores.mjs';
8
- import { finding } from '../../findings.mjs';
9
- import { profileFindings, profileStep } from '../../profile/profiler.mjs';
10
-
11
- // ---------------------------------------------------------------------------
12
- // Regex fallback (non-HTML files: CSS, JSX, TSX, etc.)
13
- // ---------------------------------------------------------------------------
14
-
15
- const hasRounded = (line) =>
16
- /\brounded(?:-\w+)?\b/.test(line.replace(/\brounded-none\b/g, ''));
17
- const hasBorderRadius = (line) => /border-radius/i.test(line);
18
- const isSafeElement = (line) => /<(?:blockquote|nav[\s>]|pre[\s>]|code[\s>]|a\s|input[\s>]|span[\s>])/i.test(line);
19
-
20
-
21
- /** Strip HTML to plain text — drops script/style/comments/tags so
22
- * content-text analyzers don't false-positive on code or CSS. */
23
- function stripHtmlToText(html) {
24
- return html
25
- .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, ' ')
26
- .replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, ' ')
27
- .replace(/<!--[\s\S]*?-->/g, ' ')
28
- .replace(/<[^>]+>/g, ' ')
29
- .replace(/\s+/g, ' ');
30
- }
31
-
32
- const PAGE_ANALYZER_EXTS = new Set(['.html', '.htm', '.astro', '.vue', '.svelte']);
33
-
34
- function extFromFilePath(filePath) {
35
- return filePath ? (filePath.match(/\.\w+$/)?.[0] || '').toLowerCase() : '';
36
- }
37
-
38
- function shouldRunPageAnalyzers(content, filePath) {
39
- if (!isFullPage(content)) return false;
40
- const ext = extFromFilePath(filePath);
41
- return !ext || PAGE_ANALYZER_EXTS.has(ext);
42
- }
43
-
44
- function firstOverusedGoogleFont(text) {
45
- return extractGoogleFontFamilies(text).find(f => OVERUSED_FONTS.has(f)) || '';
46
- }
47
-
48
- // CSS named colors whose channels are equal (achromatic). Anything outside
49
- // this set falls through to the format parsers, and an unrecognized spelling
50
- // stays non-neutral so a real accent is never skipped.
51
- const NEUTRAL_COLOR_KEYWORDS = new Set([
52
- 'transparent', 'currentcolor',
53
- 'black', 'white', 'gray', 'grey', 'silver',
54
- 'dimgray', 'dimgrey', 'darkgray', 'darkgrey', 'lightgray', 'lightgrey',
55
- 'gainsboro', 'whitesmoke',
56
- ]);
57
-
58
- function hexChannels(color) {
59
- const long = color.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})(?:[0-9a-f]{2})?$/i);
60
- if (long) return [parseInt(long[1], 16), parseInt(long[2], 16), parseInt(long[3], 16)];
61
- const short = color.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])(?:[0-9a-f])?$/i);
62
- if (short) return [1, 2, 3].map((i) => parseInt(short[i] + short[i], 16));
63
- return null;
64
- }
65
-
66
- /**
67
- * Split one box-shadow layer into top-level tokens.
68
- *
69
- * Whitespace inside parens does not separate tokens: `rgb(0 0 0)` and
70
- * `var(--x, 4px)` are each a single value, and splitting them on spaces would
71
- * read their innards as separate lengths.
72
- */
73
- function tokenizeShadowLayer(layer) {
74
- const tokens = [];
75
- let depth = 0;
76
- let current = '';
77
- for (const char of String(layer || '')) {
78
- if (char === '(') depth++;
79
- else if (char === ')') depth--;
80
- else if (depth === 0 && /\s/.test(char)) {
81
- if (current) tokens.push(current);
82
- current = '';
83
- continue;
84
- }
85
- current += char;
86
- }
87
- if (current) tokens.push(current);
88
- return tokens;
89
- }
90
-
91
- function lastMatch(text, re) {
92
- const all = [...String(text || '').matchAll(re)];
93
- return all.length ? all[all.length - 1] : null;
94
- }
95
-
96
- function isShadowLength(token) {
97
- return /^-?\d*\.?\d+(?:px)?$/i.test(String(token || ''));
98
- }
99
-
100
- /**
101
- * Neutrality test for colors as written in source CSS.
102
- *
103
- * shared/color.mjs's isNeutralColor only parses the computed function forms a
104
- * browser or jsdom emits (rgb/oklch/lab/...) and deliberately reports every
105
- * other spelling as chromatic so an unknown format is never silently skipped.
106
- * That default is wrong for authored CSS, where `#000` and `black` are the
107
- * normal spellings: calling it directly reports a plain black hairline as a
108
- * colored stripe. Handle hex and named neutrals here, then defer.
109
- */
110
- function isNeutralAuthoredColor(rawColor) {
111
- const c = String(rawColor || '').trim().toLowerCase();
112
- if (!c) return false;
113
- if (NEUTRAL_COLOR_KEYWORDS.has(c)) return true;
114
- // Modern rgb() takes space-separated channels (`rgb(0 0 0)`). shared/color.mjs
115
- // parses only the comma form a browser's getComputedStyle emits, so authored
116
- // space-separated neutrals fell through it and reported as chromatic — the
117
- // exemption this function exists for, missed. Normalize before delegating.
118
- if (/^rgba?\(/i.test(c)) {
119
- const channels = c.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i);
120
- if (channels) {
121
- const values = [1, 2, 3].map((i) => Number(channels[i]));
122
- return (Math.max(...values) - Math.min(...values)) < 30;
123
- }
124
- return isNeutralColor(c);
125
- }
126
- if (/^(?:hsla?|oklch|oklab|lab|lch|hwb)\(/i.test(c)) return isNeutralColor(c);
127
- const channels = hexChannels(c);
128
- if (channels) return (Math.max(...channels) - Math.min(...channels)) < 30;
129
- return false;
130
- }
131
-
132
- function isNeutralBorderColor(str) {
133
- const m = str.match(/solid\s+((?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color)\([^)]*\)|#[0-9a-f]{3,8}\b|[a-z]+)/i);
134
- if (!m) return false;
135
- return isNeutralAuthoredColor(m[1]);
136
- }
137
-
138
- const REGEX_MATCHERS = [
139
- // --- Side-tab ---
140
- { id: 'side-tab', regex: /\bborder-[lrse]-(\d+)\b/g,
141
- test: (m, line) => { const n = +m[1]; return hasRounded(line) ? n >= 2 : n >= 4; },
142
- fmt: (m) => m[0] },
143
- { id: 'side-tab', regex: /border-(?:left|right)\s*:\s*(\d+)px\s+solid[^;]*/gi,
144
- test: (m, line) => { if (isSafeElement(line)) return false; if (isNeutralBorderColor(m[0])) return false; const n = +m[1]; return hasBorderRadius(line) ? n >= 2 : n >= 3; },
145
- fmt: (m) => m[0].replace(/\s*;?\s*$/, '') },
146
- { id: 'side-tab', regex: /border-(?:left|right)-width\s*:\s*(\d+)px/gi,
147
- test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
148
- fmt: (m) => m[0] },
149
- { id: 'side-tab', regex: /border-inline-(?:start|end)\s*:\s*(\d+)px\s+solid/gi,
150
- test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
151
- fmt: (m) => m[0] },
152
- { id: 'side-tab', regex: /border-inline-(?:start|end)-width\s*:\s*(\d+)px/gi,
153
- test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
154
- fmt: (m) => m[0] },
155
- { id: 'side-tab', regex: /border(?:Left|Right)\s*[:=]\s*["'`](\d+)px\s+solid/g,
156
- test: (m) => +m[1] >= 3,
157
- fmt: (m) => m[0] },
158
- // --- Border accent on rounded ---
159
- { id: 'border-accent-on-rounded', regex: /\bborder-[tb]-(\d+)\b/g,
160
- test: (m, line) => hasRounded(line) && +m[1] >= 1,
161
- fmt: (m) => m[0] },
162
- { id: 'border-accent-on-rounded', regex: /border-(?:top|bottom)\s*:\s*(\d+)px\s+solid/gi,
163
- test: (m, line) => +m[1] >= 3 && hasBorderRadius(line),
164
- fmt: (m) => m[0] },
165
- // --- Overused font ---
166
- { id: 'overused-font', regex: /font-family\s*:\s*['"]?(Inter|Roboto|Open Sans|Lato|Montserrat|Arial|Helvetica|Fraunces|Geist Sans|Geist Mono|Geist|Mona Sans|Plus Jakarta Sans|Space Grotesk|Recoleta|Instrument Sans|Instrument Serif)\b/gi,
167
- test: () => true,
168
- fmt: (m) => m[0] },
169
- { id: 'overused-font', regex: /fonts\.googleapis\.com\/css2?\?[^"'\s)<>]*/gi,
170
- test: (m) => {
171
- m.overusedGoogleFont = firstOverusedGoogleFont(m[0]);
172
- return Boolean(m.overusedGoogleFont);
173
- },
174
- fmt: (m) => `Google Fonts: ${m.overusedGoogleFont || firstOverusedGoogleFont(m[0])}` },
175
- // --- Gradient text ---
176
- { id: 'gradient-text', regex: /background-clip\s*:\s*text|-webkit-background-clip\s*:\s*text/gi,
177
- test: (m, line) => /gradient/i.test(line),
178
- fmt: () => 'background-clip: text + gradient' },
179
- // --- Gradient text (Tailwind) ---
180
- { id: 'gradient-text', regex: /\bbg-clip-text\b/g,
181
- test: (m, line) => /\bbg-gradient-to-/i.test(line),
182
- fmt: () => 'bg-clip-text + bg-gradient' },
183
- // --- Tailwind gray on colored bg ---
184
- { id: 'gray-on-color', regex: /\btext-(?:gray|slate|zinc|neutral|stone)-(\d+)\b/g,
185
- test: (m, line) => /\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/.test(line),
186
- fmt: (m, line) => { const bg = line.match(/\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/); return `${m[0]} on ${bg?.[0] || '?'}`; } },
187
- // --- Tailwind AI palette ---
188
- { id: 'ai-color-palette', regex: /\btext-(?:purple|violet|indigo)-(\d+)\b/g,
189
- test: (m, line) => /\btext-(?:[2-9]xl|[3-9]xl)\b|<h[1-3]/i.test(line),
190
- fmt: (m) => `${m[0]} on heading` },
191
- { id: 'ai-color-palette', regex: /\bfrom-(?:purple|violet|indigo)-(\d+)\b/g,
192
- test: (m, line) => /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(line),
193
- fmt: (m) => `${m[0]} gradient` },
194
- // --- Bounce/elastic easing ---
195
- { id: 'bounce-easing', regex: /\banimate-bounce\b/g,
196
- test: () => true,
197
- fmt: () => 'animate-bounce (Tailwind)' },
198
- { id: 'bounce-easing', regex: /animation(?:-name)?\s*:\s*([^;{}]*(?:bounce|elastic|wobble|jiggle|spring)[^;{}]*)/gi,
199
- test: () => true,
200
- fmt: (m) => {
201
- const token = m[1]
202
- .split(/[,\s]+/)
203
- .find((part) => /bounce|elastic|wobble|jiggle|spring/i.test(part));
204
- return `animation: ${token || m[1].trim()}`;
205
- } },
206
- { id: 'bounce-easing', regex: /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g,
207
- test: (m) => {
208
- const y1 = parseFloat(m[2]), y2 = parseFloat(m[4]);
209
- return y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1;
210
- },
211
- fmt: (m) => `cubic-bezier(${m[1]}, ${m[2]}, ${m[3]}, ${m[4]})` },
212
- // --- Layout property transition ---
213
- { id: 'layout-transition', regex: /transition\s*:\s*([^;{}]+)/gi,
214
- test: (m) => {
215
- const val = m[1].toLowerCase();
216
- if (/\ball\b/.test(val)) return false;
217
- return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
218
- },
219
- fmt: (m) => {
220
- const found = m[1].match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
221
- return `transition: ${found ? found.join(', ') : m[1].trim()}`;
222
- } },
223
- { id: 'layout-transition', regex: /transition-property\s*:\s*([^;{}]+)/gi,
224
- test: (m) => {
225
- const val = m[1].toLowerCase();
226
- if (/\ball\b/.test(val)) return false;
227
- return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
228
- },
229
- fmt: (m) => {
230
- const found = m[1].match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
231
- return `transition-property: ${found ? found.join(', ') : m[1].trim()}`;
232
- } },
233
- // --- Broken image: src="" or src="#" or src=" " ---
234
- { id: 'broken-image', regex: /<img\b[^>]*?\bsrc\s*=\s*(?:""|''|"\s+"|'\s+'|"#"|'#')/gi,
235
- test: () => true,
236
- fmt: (m) => m[0].slice(0, 100) },
237
- // --- Broken image: <img> with no src attribute at all ---
238
- { id: 'broken-image', regex: /<img\b(?:(?!\bsrc\s*=)[^>])*>/gi,
239
- test: (m) => !/\bsrc\s*=/i.test(m[0]),
240
- fmt: (m) => m[0].slice(0, 100) },
241
- ];
242
-
243
- const REGEX_ANALYZERS = [
244
- // Flat type hierarchy
245
- (content, filePath) => {
246
- const sizes = new Set();
247
- const REM = 16;
248
- let m;
249
- const sizeRe = /font-size\s*:\s*([\d.]+)(px|rem|em)\b/gi;
250
- while ((m = sizeRe.exec(content)) !== null) {
251
- const px = m[2] === 'px' ? +m[1] : +m[1] * REM;
252
- if (px > 0 && px < 200) sizes.add(Math.round(px * 10) / 10);
253
- }
254
- const clampRe = /font-size\s*:\s*clamp\(\s*([\d.]+)(px|rem|em)\s*,\s*[^,]+,\s*([\d.]+)(px|rem|em)\s*\)/gi;
255
- while ((m = clampRe.exec(content)) !== null) {
256
- sizes.add(Math.round((m[2] === 'px' ? +m[1] : +m[1] * REM) * 10) / 10);
257
- sizes.add(Math.round((m[4] === 'px' ? +m[3] : +m[3] * REM) * 10) / 10);
258
- }
259
- const TW = { 'text-xs': 12, 'text-sm': 14, 'text-base': 16, 'text-lg': 18, 'text-xl': 20, 'text-2xl': 24, 'text-3xl': 30, 'text-4xl': 36, 'text-5xl': 48, 'text-6xl': 60, 'text-7xl': 72, 'text-8xl': 96, 'text-9xl': 128 };
260
- for (const [cls, px] of Object.entries(TW)) { if (new RegExp(`\\b${cls}\\b`).test(content)) sizes.add(px); }
261
- if (sizes.size < 3) return [];
262
- const sorted = [...sizes].sort((a, b) => a - b);
263
- const ratio = sorted[sorted.length - 1] / sorted[0];
264
- if (ratio >= 2.0) return [];
265
- const lines = content.split('\n');
266
- let line = 1;
267
- for (let i = 0; i < lines.length; i++) { if (/font-size/i.test(lines[i]) || /\btext-(?:xs|sm|base|lg|xl|\d)/i.test(lines[i])) { line = i + 1; break; } }
268
- return [finding('flat-type-hierarchy', filePath, `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)`, line)];
269
- },
270
- // Monotonous spacing (regex)
271
- (content, filePath) => {
272
- const vals = [];
273
- let m;
274
- const pxRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi;
275
- while ((m = pxRe.exec(content)) !== null) { const v = +m[1]; if (v > 0 && v < 200) vals.push(v); }
276
- const remRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi;
277
- while ((m = remRe.exec(content)) !== null) { const v = Math.round(parseFloat(m[1]) * 16); if (v > 0 && v < 200) vals.push(v); }
278
- const gapRe = /gap\s*:\s*(\d+)px/gi;
279
- while ((m = gapRe.exec(content)) !== null) vals.push(+m[1]);
280
- const twRe = /\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g;
281
- while ((m = twRe.exec(content)) !== null) vals.push(+m[1] * 4);
282
- const rounded = vals.map(v => Math.round(v / 4) * 4);
283
- if (rounded.length < 10) return [];
284
- const counts = {};
285
- for (const v of rounded) counts[v] = (counts[v] || 0) + 1;
286
- const maxCount = Math.max(...Object.values(counts));
287
- const pct = maxCount / rounded.length;
288
- const unique = [...new Set(rounded)].filter(v => v > 0);
289
- if (pct <= 0.6 || unique.length > 3) return [];
290
- const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
291
- return [finding('monotonous-spacing', filePath, `~${dominant}px used ${maxCount}/${rounded.length} times (${Math.round(pct * 100)}%)`)];
292
- },
293
- // Em-dash overuse (ADVISORY): the AI cadence tell is em-dash *saturation*,
294
- // not the occasional dash. Humans use em-dashes legitimately, so this rule is
295
- // advisory (surfaced separately, never a failure, hook-skipped by default) and
296
- // its threshold is deliberately conservative. Two gates must both hold:
297
- // 1. Absolute floor of EM_DASH_FLOOR (8) dashes — a page with a handful
298
- // never fires, no matter how short.
299
- // 2. Density: at least one dash per EM_DASH_CHARS_PER_DASH (500) characters
300
- // of body text, so a long article that uses eight across several thousand
301
- // words is left alone while a short, dash-per-clause landing page is not.
302
- // Raised from the old flat 5-dash floor, which fired on ordinary long prose.
303
- //
304
- // stripHtmlToText drops tags but leaves character-entity escapes intact, so
305
- // a model that writes `&mdash;`, `&#8212;`, or `&#x2014;` renders an em-dash
306
- // the counter never saw. Decode the em-dash entities (named, zero-padded
307
- // decimal, upper/lower hex) to the literal glyph first. En-dash entities are
308
- // deliberately left alone: the rule counts em-dashes, and the literal `–`
309
- // was never counted either.
310
- (content, filePath) => {
311
- const text = stripHtmlToText(content)
312
- .replace(/&mdash;|&#0*8212;|&#x0*2014;/gi, '—');
313
- let count = 0;
314
- const re = /[—]|--(?=\S)/g;
315
- while (re.exec(text) !== null) count++;
316
- if (count < EM_DASH_FLOOR) return [];
317
- // Saturation gate: dashes must be dense in the prose, not sprinkled through
318
- // a long document. textLength <= count * chars-per-dash means the density is
319
- // at or above the threshold.
320
- if (text.length > count * EM_DASH_CHARS_PER_DASH) return [];
321
- return [finding('em-dash-overuse', filePath, `${count} em-dashes in body text`)];
322
- },
323
- // Marketing buzzwords: SaaS phrase list
324
- (content, filePath) => {
325
- const text = stripHtmlToText(content);
326
- const lower = text.toLowerCase();
327
- const BUZZWORDS = [
328
- 'streamline your', 'empower your', 'supercharge your',
329
- 'unleash your', 'unleash the power', 'leverage the power',
330
- 'built for the modern', 'trusted by leading', 'trusted by the world',
331
- 'best-in-class', 'industry-leading', 'world-class', 'enterprise-grade',
332
- 'next-generation', 'cutting-edge', 'transform your business',
333
- 'revolutionize', 'game-changer', 'game changing',
334
- 'mission-critical', 'best of breed', 'future-proof', 'future proof',
335
- 'seamless experience', 'seamlessly integrate',
336
- 'drive engagement', 'drive growth', 'drive results',
337
- 'harness the power',
338
- ];
339
- let count = 0;
340
- let firstSample = '';
341
- for (const phrase of BUZZWORDS) {
342
- let from = 0;
343
- while (true) {
344
- const idx = lower.indexOf(phrase, from);
345
- if (idx === -1) break;
346
- count++;
347
- if (!firstSample) {
348
- firstSample = text.slice(Math.max(0, idx - 12), Math.min(text.length, idx + phrase.length + 12)).trim();
349
- }
350
- from = idx + phrase.length;
351
- }
352
- }
353
- if (count === 0) return [];
354
- return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)];
355
- },
356
- // Aphoristic cadence: manufactured-contrast + short-rebuttal
357
- (content, filePath) => {
358
- const text = stripHtmlToText(content);
359
- const NOT_A_RE = /\bNot an? [a-z][^.!?]{1,40}[.!]\s+[A-Z][^.!?]{1,60}[.!]/g;
360
- const SHORT_REBUTTAL_RE = /\b[A-Z][^.!?]{4,80}[.!]\s+(No|Just)\s+[a-z][^.!?]{2,60}[.!]/g;
361
- let count = 0;
362
- let firstSample = '';
363
- let m;
364
- NOT_A_RE.lastIndex = 0;
365
- while ((m = NOT_A_RE.exec(text)) !== null) {
366
- count++;
367
- if (!firstSample) firstSample = m[0].trim().slice(0, 80);
368
- }
369
- SHORT_REBUTTAL_RE.lastIndex = 0;
370
- while ((m = SHORT_REBUTTAL_RE.exec(text)) !== null) {
371
- count++;
372
- if (!firstSample) firstSample = m[0].trim().slice(0, 80);
373
- }
374
- if (count < 3) return [];
375
- return [finding('aphoristic-cadence', filePath, `${count} aphoristic constructions: "${firstSample}"`)];
376
- },
377
- // Dark glow / chromatic halo shadows (page-level). Shared scanner handles
378
- // any color format, single-level var() resolution, zero-offset halos on
379
- // any background, and text-shadow glows.
380
- (content, filePath) => {
381
- const hits = scanCssTextForGlow(content);
382
- if (hits.length === 0) return [];
383
- const lines = content.substring(0, hits[0].index).split('\n');
384
- return [finding('dark-glow', filePath, hits[0].snippet, lines.length)];
385
- },
386
- // Radial-gradient background halo on a dark page (the gradient sibling
387
- // of the dark-glow shadow tell).
388
- (content, filePath) => {
389
- const hits = scanCssTextForRadialHalo(content);
390
- if (hits.length === 0) return [];
391
- const lines = content.substring(0, hits[0].index).split('\n');
392
- return [finding('radial-halo', filePath, hits[0].snippet, lines.length)];
393
- },
394
- // Auto-scrolling marquees (<marquee> or infinite horizontal loop
395
- // animations).
396
- (content, filePath) => scanCssTextForMarquee(content).map(hit => finding('marquee', filePath, hit.snippet)),
397
- ];
398
-
399
- // ---------------------------------------------------------------------------
400
- // Structural CSS checks used by source files whose styles are not parsed by
401
- // the static HTML engine.
402
- // ---------------------------------------------------------------------------
403
-
404
- const CHROMATIC_SHADOW_TOKEN_RE = /(?:^|-)(?:accent|kinpaku|patina|gold|red|orange|amber|yellow|lime|green|emerald|teal|cyan|blue|indigo|violet|purple|magenta|pink|rose|coral|aqua|mint|burgundy|crimson|scarlet)(?:-|$)/i;
405
-
406
- function insetStripeColorIsChromatic(rawColor) {
407
- const color = String(rawColor || '').trim().replace(/\s*!important\s*$/i, '');
408
- if (/^(?:currentcolor|transparent|inherit|unset)$/i.test(color)) return false;
409
- const variable = color.match(/^var\(\s*(--[\w-]+)/i);
410
- if (variable) return CHROMATIC_SHADOW_TOKEN_RE.test(variable[1]);
411
- if (!/^(?:#|rgba?\(|hsla?\(|hwb\(|oklch\(|oklab\(|lch\(|lab\(|color\(|[a-z]+$)/i.test(color)) return false;
412
- return !isNeutralAuthoredColor(color);
413
- }
414
-
415
- /**
416
- * Blank out comment bodies while preserving every byte offset (and therefore
417
- * every line number) so commented-out CSS is not scanned as live rules.
418
- */
419
- function blankCssComments(css) {
420
- return css.replace(/\/\*[\s\S]*?\*\//g, (block) => block.replace(/[^\n]/g, ' '));
421
- }
422
-
423
- function scanInsetStripeCss(rawContent, filePath, lineOffset = 0) {
424
- const content = blankCssComments(rawContent);
425
- const findings = [];
426
- const ruleRe = /([^{};]+)\{([^{}]*)\}/g;
427
- let match;
428
- // Deriving each line with content.slice(0, offset).split('\n') re-scans the
429
- // whole prefix per rule, which is O(n^2) on a large stylesheet. Rule matches
430
- // arrive in source order, so carry a monotonic cursor instead: one pass total.
431
- let scanOffset = 0;
432
- let scanLine = 1;
433
- const lineAtOffset = (offset) => {
434
- while (scanOffset < offset) {
435
- if (content[scanOffset] === '\n') scanLine++;
436
- scanOffset++;
437
- }
438
- return scanLine;
439
- };
440
- while ((match = ruleRe.exec(content)) !== null) {
441
- // The selector group is `[^{};]+`, which greedily absorbs the whitespace and
442
- // newlines trailing the previous rule. Advance past that run before deriving
443
- // the line, or every rule after the first reports the preceding line.
444
- const selectorStart = match.index + (match[1].length - match[1].trimStart().length);
445
- const selector = match[1].trim().replace(/\s+/g, ' ');
446
- if (!selector) continue;
447
- if (/:(?:hover|focus|focus-visible|focus-within|active|checked|target)\b/i.test(selector)) continue;
448
- if (/\[aria-selected\s*[*^$|~]?=\s*["']?true/i.test(selector)) continue;
449
- if (/\[aria-current(?!\s*[*^$|~]?=\s*["']?false)/i.test(selector)) continue;
450
- if (/(?:^|[\s._[-])(?:active|current|selected)(?![\w])/i.test(selector)) continue;
451
- if (/(?:^|[\s>+~,(])(?:button|hr|tr|td|th|table|blockquote|pre|code)(?![\w-])/i.test(selector)) continue;
452
-
453
- // Read the last of a repeated declaration, not the first: that is what the
454
- // cascade paints. Taking the first both flagged stripes that a later
455
- // `box-shadow: none` had cancelled and missed stripes that overrode an
456
- // earlier value, and mis-skipped rules whose narrow width was overridden.
457
- const width = lastMatch(match[2], /(?:^|;)\s*(?:width|inline-size)\s*:\s*(\d+(?:\.\d+)?)px/gi);
458
- if (width && Number(width[1]) <= 40) continue;
459
- const declaration = lastMatch(match[2], /(?:^|;)\s*box-shadow\s*:\s*([^;]+)/gi);
460
- if (!declaration || !/\binset\b/i.test(declaration[1])) continue;
461
- // `!important` qualifies the declaration, not the shadow value, so strip it
462
- // before the layers are read. Tokenizing split it into its own token, which
463
- // made the color count wrong and silently stopped flagging stripes declared
464
- // with it — a shape the previous regex handled.
465
- const shadowValue = declaration[1].replace(/\s*!\s*important\s*$/i, '').trim();
466
-
467
- for (const rawLayer of shadowValue.split(/,(?![^(]*\))/)) {
468
- const layer = rawLayer.trim();
469
- // Parse the layer by its grammar rather than by one spelling of it.
470
- // A box-shadow layer is `inset? && <length>{2,4} && <color>?` in any
471
- // order, so `inset 4px 0 red`, `4px 0 0 red inset`, and `red 4px 0 inset`
472
- // all paint the same stripe. Matching a fixed token order missed three
473
- // valid spellings in a row; enumerate the tokens instead. Tokenizing must
474
- // respect parens: `rgb(0 0 0)` is one color token, and splitting it on
475
- // whitespace would read its channels as lengths.
476
- const tokens = tokenizeShadowLayer(layer);
477
- if (!tokens.some((token) => /^inset$/i.test(token))) continue;
478
- const rest = tokens.filter((token) => !/^inset$/i.test(token));
479
- const lengths = rest.filter(isShadowLength);
480
- const colors = rest.filter((token) => !isShadowLength(token));
481
- // Only the two offsets are required; omitted blur/spread default to 0,
482
- // which is exactly the stripe shape. More than one non-length token is a
483
- // layer shape we do not claim to understand, so leave it alone.
484
- if (lengths.length < 2 || lengths.length > 4 || colors.length !== 1) continue;
485
- const values = lengths.map((token) => ({
486
- n: Number(token.replace(/px$/i, '')),
487
- hasPx: /px$/i.test(token),
488
- }));
489
- const x = values[0];
490
- const y = values[1];
491
- const blur = values[2] ? values[2].n : 0;
492
- const spread = values[3] ? values[3].n : 0;
493
- if ((x.n !== 0 && !x.hasPx) || (y.n !== 0 && !y.hasPx) || blur !== 0 || spread !== 0) continue;
494
- const ax = Math.abs(x.n);
495
- const ay = Math.abs(y.n);
496
- if (!((ax >= 3 && ax <= 12 && ay === 0) || (ay >= 3 && ay <= 12 && ax === 0))) continue;
497
- if (!insetStripeColorIsChromatic(colors[0])) continue;
498
- const edge = ay === 0 ? (x.n > 0 ? 'left' : 'right') : (y.n > 0 ? 'top' : 'bottom');
499
- const line = lineOffset + lineAtOffset(selectorStart);
500
- findings.push(finding('side-tab', filePath, `${selector} — inset box-shadow ${ay === 0 ? ax : ay}px stripe (${edge})`, line));
501
- break;
502
- }
503
- }
504
- return findings;
505
- }
506
-
507
- // ---------------------------------------------------------------------------
508
- // Style block extraction (Astro/Vue/Svelte <style> blocks)
509
- // ---------------------------------------------------------------------------
510
-
511
- function extractStyleBlocks(content, ext) {
512
- ext = ext.toLowerCase();
513
- if (ext !== '.astro' && ext !== '.vue' && ext !== '.svelte') return [];
514
- const blocks = [];
515
- const re = /<style[^>]*>([\s\S]*?)<\/style>/gi;
516
- let m;
517
- while ((m = re.exec(content)) !== null) {
518
- const before = content.substring(0, m.index);
519
- const startLine = before.split('\n').length + 1;
520
- blocks.push({ content: m[1], startLine });
521
- }
522
- return blocks;
523
- }
524
-
525
- // ---------------------------------------------------------------------------
526
- // CSS-in-JS extraction (styled-components, emotion)
527
- // ---------------------------------------------------------------------------
528
-
529
- const CSS_IN_JS_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx']);
530
-
531
- function extractCSSinJS(content, ext) {
532
- ext = ext.toLowerCase();
533
- if (!CSS_IN_JS_EXTENSIONS.has(ext)) return [];
534
- const blocks = [];
535
- const re = /(?:styled(?:\.\w+|\([^)]+\))|css)\s*`([\s\S]*?)`/g;
536
- let m;
537
- while ((m = re.exec(content)) !== null) {
538
- const before = content.substring(0, m.index);
539
- const startLine = before.split('\n').length;
540
- blocks.push({ content: m[1], startLine });
541
- }
542
- return blocks;
543
- }
544
-
545
- function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, options = {}) {
546
- const { profile, phase = 'regex-matchers' } = options || {};
547
- const findings = [];
548
- if (!profile) {
549
- for (const matcher of REGEX_MATCHERS) {
550
- for (let i = 0; i < lines.length; i++) {
551
- const line = lines[i];
552
- matcher.regex.lastIndex = 0;
553
- let m;
554
- while ((m = matcher.regex.exec(line)) !== null) {
555
- // For extracted blocks, use nearby lines as context for multi-line CSS patterns
556
- const context = blockContext
557
- ? lines.slice(Math.max(0, i - 3), Math.min(lines.length, i + 4)).join(' ')
558
- : line;
559
- if (matcher.test(m, context)) {
560
- findings.push(finding(matcher.id, filePath, matcher.fmt(m, context), i + 1 + lineOffset));
561
- }
562
- }
563
- }
564
- }
565
- return findings;
566
- }
567
-
568
- for (const matcher of REGEX_MATCHERS) {
569
- const matcherFindings = profileFindings(profile, {
570
- engine: 'regex',
571
- phase,
572
- ruleId: matcher.id,
573
- target: filePath,
574
- }, () => {
575
- const matches = [];
576
- for (let i = 0; i < lines.length; i++) {
577
- const line = lines[i];
578
- matcher.regex.lastIndex = 0;
579
- let m;
580
- while ((m = matcher.regex.exec(line)) !== null) {
581
- // For extracted blocks, use nearby lines as context for multi-line CSS patterns
582
- const context = blockContext
583
- ? lines.slice(Math.max(0, i - 3), Math.min(lines.length, i + 4)).join(' ')
584
- : line;
585
- if (matcher.test(m, context)) {
586
- matches.push(finding(matcher.id, filePath, matcher.fmt(m, context), i + 1 + lineOffset));
587
- }
588
- }
589
- }
590
- return matches;
591
- });
592
- findings.push(...matcherFindings);
593
- }
594
- return findings;
595
- }
596
-
597
- /** Page-level analyzers that scan rendered text content (em-dash use,
598
- * buzzword phrases, aphoristic cadence).
599
- * These are detector-agnostic — they work on any HTML/text source
600
- * and don't need a parsed DOM. Exported so detectHtml can call them
601
- * for `.html` files (which otherwise skip the regex engine). */
602
- const TEXT_CONTENT_ANALYZER_IDS = [
603
- 'em-dash-overuse',
604
- 'marketing-buzzword',
605
- 'aphoristic-cadence',
606
- ];
607
-
608
- function runTextContentAnalyzers(content, filePath, options = {}) {
609
- const profile = options?.profile;
610
- if (!shouldRunPageAnalyzers(content, filePath)) return [];
611
- // The 3 text-content analyzers are at indices 2-4 in REGEX_ANALYZERS
612
- // (single-font's removal on 2026-07-29 shifted every index down one).
613
- const findings = [];
614
- for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) {
615
- const analyzer = REGEX_ANALYZERS[2 + i];
616
- const ruleId = TEXT_CONTENT_ANALYZER_IDS[i];
617
- findings.push(...profileFindings(profile, {
618
- engine: 'regex',
619
- phase: 'text-content',
620
- ruleId,
621
- target: filePath,
622
- }, () => analyzer(content, filePath)));
623
- }
624
- return findings;
625
- }
626
-
627
- function detectText(content, filePath, options = {}) {
628
- const profile = options?.profile;
629
- const findings = [];
630
- const lines = content.split('\n');
631
- const ext = extFromFilePath(filePath);
632
-
633
- // Run regex matchers on the full file content (catches Tailwind classes, inline styles)
634
- // Enable block context for CSS files where related properties span multiple lines
635
- const cssLike = new Set(['.css', '.scss', '.sass', '.less']);
636
- findings.push(...runRegexMatchers(lines, filePath, 0, cssLike.has(ext) || null, {
637
- profile,
638
- phase: 'source',
639
- }));
640
- // Pseudo-element stripes (::before/::after absolute bars) carry the same
641
- // side-tab silhouette without any border token, so the line matchers can't
642
- // see them (issue #394). The shared scanner already runs on full HTML pages
643
- // via checkHtmlPatterns; give standalone stylesheets, component style
644
- // blocks, and CSS-in-JS templates the same coverage. Each hit carries the
645
- // rule's source offset, so the finding gets a real line and line-scoped
646
- // inline ignores keep working.
647
- const pseudoStripeFindings = (text, lineOffset) =>
648
- scanCssTextForPseudoStripe(text).map(hit =>
649
- finding(hit.id, filePath, hit.snippet, lineOffset + text.slice(0, hit.index).split('\n').length));
650
-
651
- if (cssLike.has(ext)) {
652
- findings.push(...scanInsetStripeCss(content, filePath));
653
- findings.push(...pseudoStripeFindings(content, 0));
654
- }
655
-
656
- // Block-level CSS checks that need multiple declarations must run over the
657
- // complete source, not line-by-line. This covers standalone stylesheets,
658
- // component style blocks, inline styles, and CSS-in-JS templates.
659
- findings.push(...profileFindings(profile, {
660
- engine: 'regex',
661
- phase: 'source',
662
- ruleId: 'codex-grid-background',
663
- target: filePath,
664
- }, () => scanCssTextForGridBackground(content).map(hit => {
665
- const line = content.substring(0, hit.index).split('\n').length;
666
- return finding('codex-grid-background', filePath, hit.snippet, line);
667
- })));
668
-
669
- // Extract and scan <style> blocks from Astro/Vue/Svelte components.
670
- const styleBlocks = profile
671
- ? profileStep(profile, {
672
- engine: 'regex',
673
- phase: 'extract',
674
- ruleId: 'style-blocks',
675
- target: filePath,
676
- }, () => extractStyleBlocks(content, ext))
677
- : extractStyleBlocks(content, ext);
678
- for (const block of styleBlocks) {
679
- const blockLines = block.content.split('\n');
680
- findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true, {
681
- profile,
682
- phase: 'style-block',
683
- }));
684
- // block.startLine is the first line *after* the <style> tag, but block.content
685
- // begins at the character right after that tag — so its own line 1 sits on the
686
- // tag's line, whether or not a newline follows immediately. lineAtOffset is
687
- // 1-based, so the offset is startLine - 2; startLine - 1 double-counted and
688
- // reported every selector one line low. runRegexMatchers keeps startLine - 1
689
- // because it indexes its split lines from zero.
690
- findings.push(...scanInsetStripeCss(block.content, filePath, block.startLine - 2));
691
- findings.push(...pseudoStripeFindings(block.content, block.startLine - 2));
692
- }
693
-
694
- // Extract and scan CSS-in-JS template literals
695
- const cssJsBlocks = profile
696
- ? profileStep(profile, {
697
- engine: 'regex',
698
- phase: 'extract',
699
- ruleId: 'css-in-js',
700
- target: filePath,
701
- }, () => extractCSSinJS(content, ext))
702
- : extractCSSinJS(content, ext);
703
- for (const block of cssJsBlocks) {
704
- const blockLines = block.content.split('\n');
705
- findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true, {
706
- profile,
707
- phase: 'css-in-js',
708
- }));
709
- findings.push(...scanInsetStripeCss(block.content, filePath, block.startLine - 1));
710
- findings.push(...pseudoStripeFindings(block.content, block.startLine - 1));
711
- }
712
-
713
- if (options?.designSystem) {
714
- findings.push(...profileFindings(profile, {
715
- engine: 'regex',
716
- phase: 'source',
717
- ruleId: 'design-system',
718
- target: filePath,
719
- }, () => checkSourceDesignSystem(content, filePath, { designSystem: options.designSystem })));
720
- }
721
-
722
- // Deduplicate findings (same antipattern + similar snippet, within 2 lines)
723
- const deduped = [];
724
- for (const f of findings) {
725
- const isDupe = deduped.some(d =>
726
- d.antipattern === f.antipattern &&
727
- d.snippet === f.snippet &&
728
- Math.abs(d.line - f.line) <= 2
729
- );
730
- if (!isDupe) deduped.push(f);
731
- }
732
-
733
- // Page-level analyzers only run on full pages
734
- if (shouldRunPageAnalyzers(content, filePath)) {
735
- const analyzerIds = [
736
- 'flat-type-hierarchy',
737
- 'monotonous-spacing',
738
- 'em-dash-overuse',
739
- 'marketing-buzzword',
740
- 'aphoristic-cadence',
741
- 'dark-glow',
742
- ];
743
- for (let i = 0; i < REGEX_ANALYZERS.length; i++) {
744
- const analyzer = REGEX_ANALYZERS[i];
745
- deduped.push(...profileFindings(profile, {
746
- engine: 'regex',
747
- phase: 'page-analyzer',
748
- ruleId: analyzerIds[i] || `analyzer-${i + 1}`,
749
- target: filePath,
750
- }, () => analyzer(content, filePath)));
751
- }
752
- }
753
-
754
- // Inline `impeccable-disable*` waivers travel with the file; honor them unless
755
- // explicitly bypassed (`--no-config` / `--no-inline-ignores`).
756
- return options?.inlineIgnores === false ? deduped : applyInlineIgnores(deduped, content);
757
- }
758
-
759
- export {
760
- REGEX_MATCHERS,
761
- REGEX_ANALYZERS,
762
- TEXT_CONTENT_ANALYZER_IDS,
763
- extractStyleBlocks,
764
- extractCSSinJS,
765
- runRegexMatchers,
766
- runTextContentAnalyzers,
767
- detectText,
768
- };