opencode-skills-collection 3.0.34 → 3.0.36

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 (269) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +16 -1
  2. package/bundled-skills/accesslint-audit/SKILL.md +115 -0
  3. package/bundled-skills/accesslint-diff/SKILL.md +81 -0
  4. package/bundled-skills/accesslint-scan/SKILL.md +47 -0
  5. package/bundled-skills/composition-patterns/SKILL.md +87 -0
  6. package/bundled-skills/composition-patterns/rules/_sections.md +29 -0
  7. package/bundled-skills/composition-patterns/rules/_template.md +24 -0
  8. package/bundled-skills/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  9. package/bundled-skills/composition-patterns/rules/architecture-compound-components.md +112 -0
  10. package/bundled-skills/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  11. package/bundled-skills/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  12. package/bundled-skills/composition-patterns/rules/react19-no-forwardref.md +42 -0
  13. package/bundled-skills/composition-patterns/rules/state-context-interface.md +191 -0
  14. package/bundled-skills/composition-patterns/rules/state-decouple-implementation.md +113 -0
  15. package/bundled-skills/composition-patterns/rules/state-lift-state.md +125 -0
  16. package/bundled-skills/debugging-toolkit/SKILL.md +35 -0
  17. package/bundled-skills/deploy-to-vercel/SKILL.md +304 -0
  18. package/bundled-skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
  19. package/bundled-skills/deploy-to-vercel/resources/deploy.sh +301 -0
  20. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  21. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  22. package/bundled-skills/docs/maintainers/backups/README-2026-06-02.md +687 -0
  23. package/bundled-skills/docs/maintainers/repo-growth-seo.md +4 -4
  24. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  25. package/bundled-skills/docs/users/bundles.md +245 -1
  26. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  27. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  28. package/bundled-skills/docs/users/getting-started.md +3 -3
  29. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  30. package/bundled-skills/docs/users/plugins.md +21 -13
  31. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +95 -0
  32. package/bundled-skills/docs/users/usage.md +4 -4
  33. package/bundled-skills/docs/users/visual-guide.md +4 -4
  34. package/bundled-skills/mmx-cli/SKILL.md +5 -2
  35. package/bundled-skills/nextjs-seo-indexing/SKILL.md +3 -3
  36. package/bundled-skills/polis-protocol/SKILL.md +93 -0
  37. package/bundled-skills/python-development/SKILL.md +35 -0
  38. package/bundled-skills/radix-ui-design-system/SKILL.md +2 -2
  39. package/bundled-skills/react-native-skills/SKILL.md +120 -0
  40. package/bundled-skills/react-native-skills/rules/_sections.md +86 -0
  41. package/bundled-skills/react-native-skills/rules/_template.md +28 -0
  42. package/bundled-skills/react-native-skills/rules/animation-derived-value.md +53 -0
  43. package/bundled-skills/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
  44. package/bundled-skills/react-native-skills/rules/animation-gpu-properties.md +65 -0
  45. package/bundled-skills/react-native-skills/rules/design-system-compound-components.md +66 -0
  46. package/bundled-skills/react-native-skills/rules/fonts-config-plugin.md +71 -0
  47. package/bundled-skills/react-native-skills/rules/imports-design-system-folder.md +68 -0
  48. package/bundled-skills/react-native-skills/rules/js-hoist-intl.md +61 -0
  49. package/bundled-skills/react-native-skills/rules/list-performance-callbacks.md +44 -0
  50. package/bundled-skills/react-native-skills/rules/list-performance-function-references.md +132 -0
  51. package/bundled-skills/react-native-skills/rules/list-performance-images.md +53 -0
  52. package/bundled-skills/react-native-skills/rules/list-performance-inline-objects.md +97 -0
  53. package/bundled-skills/react-native-skills/rules/list-performance-item-expensive.md +94 -0
  54. package/bundled-skills/react-native-skills/rules/list-performance-item-memo.md +82 -0
  55. package/bundled-skills/react-native-skills/rules/list-performance-item-types.md +104 -0
  56. package/bundled-skills/react-native-skills/rules/list-performance-virtualize.md +67 -0
  57. package/bundled-skills/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
  58. package/bundled-skills/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
  59. package/bundled-skills/react-native-skills/rules/navigation-native-navigators.md +188 -0
  60. package/bundled-skills/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
  61. package/bundled-skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
  62. package/bundled-skills/react-native-skills/rules/react-state-dispatcher.md +91 -0
  63. package/bundled-skills/react-native-skills/rules/react-state-fallback.md +56 -0
  64. package/bundled-skills/react-native-skills/rules/react-state-minimize.md +65 -0
  65. package/bundled-skills/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
  66. package/bundled-skills/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
  67. package/bundled-skills/react-native-skills/rules/scroll-position-no-state.md +82 -0
  68. package/bundled-skills/react-native-skills/rules/state-ground-truth.md +80 -0
  69. package/bundled-skills/react-native-skills/rules/ui-expo-image.md +66 -0
  70. package/bundled-skills/react-native-skills/rules/ui-image-gallery.md +104 -0
  71. package/bundled-skills/react-native-skills/rules/ui-measure-views.md +78 -0
  72. package/bundled-skills/react-native-skills/rules/ui-menus.md +174 -0
  73. package/bundled-skills/react-native-skills/rules/ui-native-modals.md +77 -0
  74. package/bundled-skills/react-native-skills/rules/ui-pressable.md +61 -0
  75. package/bundled-skills/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
  76. package/bundled-skills/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
  77. package/bundled-skills/react-native-skills/rules/ui-styling.md +87 -0
  78. package/bundled-skills/schema-markup-generator/SKILL.md +1 -1
  79. package/bundled-skills/skill-issue/SKILL.md +73 -0
  80. package/bundled-skills/social-metadata-hardening/SKILL.md +4 -3
  81. package/bundled-skills/social-post-writer-seo/SKILL.md +19 -0
  82. package/bundled-skills/tdd-workflows/SKILL.md +35 -0
  83. package/bundled-skills/user-thoughts/SKILL.md +236 -0
  84. package/bundled-skills/user-thoughts/assets/Runtime-Template/README.ai.md +13 -0
  85. package/bundled-skills/user-thoughts/assets/Runtime-Template/define.ini +3 -0
  86. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/README.ai.md +25 -0
  87. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/backlog.md +19 -0
  88. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/dev-stack.md +7 -0
  89. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/general.md +7 -0
  90. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/plans.md +7 -0
  91. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/rules.md +7 -0
  92. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/details.md +7 -0
  93. package/bundled-skills/user-thoughts/assets/Runtime-Template/mdbase/details/ui/outline.md +7 -0
  94. package/bundled-skills/user-thoughts/references/commands.md +54 -0
  95. package/bundled-skills/user-thoughts/references/edge-cases.md +84 -0
  96. package/bundled-skills/user-thoughts/references/safety.md +65 -0
  97. package/bundled-skills/user-thoughts/references/sortin.md +76 -0
  98. package/bundled-skills/user-thoughts/scripts/common.py +62 -0
  99. package/bundled-skills/user-thoughts/scripts/ignore_ops.py +125 -0
  100. package/bundled-skills/user-thoughts/scripts/init.py +63 -0
  101. package/bundled-skills/user-thoughts/scripts/show_mdbase.py +93 -0
  102. package/bundled-skills/user-thoughts/scripts/show_raw.py +42 -0
  103. package/bundled-skills/user-thoughts/scripts/sortin.py +211 -0
  104. package/bundled-skills/user-thoughts/scripts/status.py +56 -0
  105. package/bundled-skills/user-thoughts/scripts/toggle.py +68 -0
  106. package/bundled-skills/user-thoughts/scripts/write_raw.py +106 -0
  107. package/bundled-skills/vercel-cli-with-tokens/SKILL.md +361 -0
  108. package/bundled-skills/vercel-optimize/CONTRIBUTING.md +41 -0
  109. package/bundled-skills/vercel-optimize/SKILL.md +331 -0
  110. package/bundled-skills/vercel-optimize/lib/auth-route.mjs +23 -0
  111. package/bundled-skills/vercel-optimize/lib/budget-summary.mjs +182 -0
  112. package/bundled-skills/vercel-optimize/lib/citations.mjs +139 -0
  113. package/bundled-skills/vercel-optimize/lib/cost-coverage.mjs +143 -0
  114. package/bundled-skills/vercel-optimize/lib/dedup-recs.mjs +325 -0
  115. package/bundled-skills/vercel-optimize/lib/deep-dive.mjs +350 -0
  116. package/bundled-skills/vercel-optimize/lib/display-labels.mjs +185 -0
  117. package/bundled-skills/vercel-optimize/lib/extract-claims.mjs +550 -0
  118. package/bundled-skills/vercel-optimize/lib/framework-support.mjs +67 -0
  119. package/bundled-skills/vercel-optimize/lib/gates/build-minutes-fanout.mjs +69 -0
  120. package/bundled-skills/vercel-optimize/lib/gates/cold-start.mjs +66 -0
  121. package/bundled-skills/vercel-optimize/lib/gates/contract.mjs +79 -0
  122. package/bundled-skills/vercel-optimize/lib/gates/cwv-poor.mjs +87 -0
  123. package/bundled-skills/vercel-optimize/lib/gates/external-api-slow.mjs +55 -0
  124. package/bundled-skills/vercel-optimize/lib/gates/hard-gates.mjs +73 -0
  125. package/bundled-skills/vercel-optimize/lib/gates/index.mjs +45 -0
  126. package/bundled-skills/vercel-optimize/lib/gates/isr-overrevalidation.mjs +62 -0
  127. package/bundled-skills/vercel-optimize/lib/gates/middleware-heavy.mjs +51 -0
  128. package/bundled-skills/vercel-optimize/lib/gates/observability-events-attribution.mjs +56 -0
  129. package/bundled-skills/vercel-optimize/lib/gates/platform-bot-protection.mjs +115 -0
  130. package/bundled-skills/vercel-optimize/lib/gates/platform-fluid-compute.mjs +83 -0
  131. package/bundled-skills/vercel-optimize/lib/gates/region-misconfig.mjs +64 -0
  132. package/bundled-skills/vercel-optimize/lib/gates/route-errors.mjs +80 -0
  133. package/bundled-skills/vercel-optimize/lib/gates/scanner-driven.mjs +122 -0
  134. package/bundled-skills/vercel-optimize/lib/gates/select-candidates.mjs +134 -0
  135. package/bundled-skills/vercel-optimize/lib/gates/slow-route.mjs +88 -0
  136. package/bundled-skills/vercel-optimize/lib/gates/types.d.ts +38 -0
  137. package/bundled-skills/vercel-optimize/lib/gates/uncached-route.mjs +93 -0
  138. package/bundled-skills/vercel-optimize/lib/gates/usage-spike-triage.mjs +121 -0
  139. package/bundled-skills/vercel-optimize/lib/grade-recommendation.mjs +155 -0
  140. package/bundled-skills/vercel-optimize/lib/impact-label.mjs +126 -0
  141. package/bundled-skills/vercel-optimize/lib/impact-magnitude.mjs +60 -0
  142. package/bundled-skills/vercel-optimize/lib/investigation-brief.mjs +610 -0
  143. package/bundled-skills/vercel-optimize/lib/observation-safety.mjs +174 -0
  144. package/bundled-skills/vercel-optimize/lib/project-facts.mjs +99 -0
  145. package/bundled-skills/vercel-optimize/lib/queries.mjs +315 -0
  146. package/bundled-skills/vercel-optimize/lib/reconcile-candidates.mjs +372 -0
  147. package/bundled-skills/vercel-optimize/lib/render-report.mjs +955 -0
  148. package/bundled-skills/vercel-optimize/lib/repo-root.mjs +86 -0
  149. package/bundled-skills/vercel-optimize/lib/route-normalize.mjs +220 -0
  150. package/bundled-skills/vercel-optimize/lib/sanitizers/bot-protection-certainty.mjs +38 -0
  151. package/bundled-skills/vercel-optimize/lib/sanitizers/cache-tag-invalidation-certainty.mjs +30 -0
  152. package/bundled-skills/vercel-optimize/lib/sanitizers/count-correct.mjs +52 -0
  153. package/bundled-skills/vercel-optimize/lib/sanitizers/function-duration-invocations.mjs +38 -0
  154. package/bundled-skills/vercel-optimize/lib/sanitizers/index.mjs +79 -0
  155. package/bundled-skills/vercel-optimize/lib/sanitizers/middleware-conflict.mjs +36 -0
  156. package/bundled-skills/vercel-optimize/lib/sanitizers/missing-citation.mjs +16 -0
  157. package/bundled-skills/vercel-optimize/lib/sanitizers/pre-release.mjs +74 -0
  158. package/bundled-skills/vercel-optimize/lib/sanitizers/rate-limit.mjs +67 -0
  159. package/bundled-skills/vercel-optimize/lib/sanitizers/rendering-mode-mislabel.mjs +38 -0
  160. package/bundled-skills/vercel-optimize/lib/sanitizers/undeclared-dep.mjs +78 -0
  161. package/bundled-skills/vercel-optimize/lib/sanitizers/vercel-directive-strip.mjs +37 -0
  162. package/bundled-skills/vercel-optimize/lib/sanitizers/window-units.mjs +32 -0
  163. package/bundled-skills/vercel-optimize/lib/scanners/cache-components-suspense-dedupe.mjs +109 -0
  164. package/bundled-skills/vercel-optimize/lib/scanners/edge-heavy-import.mjs +94 -0
  165. package/bundled-skills/vercel-optimize/lib/scanners/force-dynamic.mjs +42 -0
  166. package/bundled-skills/vercel-optimize/lib/scanners/headers-in-page.mjs +44 -0
  167. package/bundled-skills/vercel-optimize/lib/scanners/index.mjs +35 -0
  168. package/bundled-skills/vercel-optimize/lib/scanners/large-static-asset.mjs +92 -0
  169. package/bundled-skills/vercel-optimize/lib/scanners/max-age-without-s-maxage.mjs +42 -0
  170. package/bundled-skills/vercel-optimize/lib/scanners/middleware-broad-matcher.mjs +55 -0
  171. package/bundled-skills/vercel-optimize/lib/scanners/missing-cache-headers.mjs +90 -0
  172. package/bundled-skills/vercel-optimize/lib/scanners/prisma-include-tree.mjs +42 -0
  173. package/bundled-skills/vercel-optimize/lib/scanners/region-pin-in-config.mjs +88 -0
  174. package/bundled-skills/vercel-optimize/lib/scanners/source-maps-production.mjs +36 -0
  175. package/bundled-skills/vercel-optimize/lib/scanners/sveltekit-prerender-missing.mjs +43 -0
  176. package/bundled-skills/vercel-optimize/lib/scanners/turbo-force-bypass.mjs +129 -0
  177. package/bundled-skills/vercel-optimize/lib/scanners/unoptimized-image.mjs +113 -0
  178. package/bundled-skills/vercel-optimize/lib/scanners/use-cache-date-stamp.mjs +106 -0
  179. package/bundled-skills/vercel-optimize/lib/support-topics.mjs +355 -0
  180. package/bundled-skills/vercel-optimize/lib/throttle.mjs +273 -0
  181. package/bundled-skills/vercel-optimize/lib/util.mjs +17 -0
  182. package/bundled-skills/vercel-optimize/lib/vercel.mjs +784 -0
  183. package/bundled-skills/vercel-optimize/lib/verify-claim.mjs +1296 -0
  184. package/bundled-skills/vercel-optimize/lib/workspace-resolver.mjs +521 -0
  185. package/bundled-skills/vercel-optimize/references/candidates.md +176 -0
  186. package/bundled-skills/vercel-optimize/references/data-collection.md +218 -0
  187. package/bundled-skills/vercel-optimize/references/docs-library.json +683 -0
  188. package/bundled-skills/vercel-optimize/references/doctrine.md +105 -0
  189. package/bundled-skills/vercel-optimize/references/observability-plus.md +108 -0
  190. package/bundled-skills/vercel-optimize/references/playbooks/README.md +53 -0
  191. package/bundled-skills/vercel-optimize/references/playbooks/ai-application.md +32 -0
  192. package/bundled-skills/vercel-optimize/references/playbooks/api-service.md +30 -0
  193. package/bundled-skills/vercel-optimize/references/playbooks/content-site.md +30 -0
  194. package/bundled-skills/vercel-optimize/references/playbooks/ecommerce.md +30 -0
  195. package/bundled-skills/vercel-optimize/references/playbooks/marketing.md +30 -0
  196. package/bundled-skills/vercel-optimize/references/playbooks/saas.md +31 -0
  197. package/bundled-skills/vercel-optimize/references/playbooks/sveltekit.md +75 -0
  198. package/bundled-skills/vercel-optimize/references/recommendations.md +203 -0
  199. package/bundled-skills/vercel-optimize/references/scanner-patterns.md +251 -0
  200. package/bundled-skills/vercel-optimize/references/scoring.md +205 -0
  201. package/bundled-skills/vercel-optimize/references/support-topics/README.md +46 -0
  202. package/bundled-skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md +22 -0
  203. package/bundled-skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md +22 -0
  204. package/bundled-skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md +22 -0
  205. package/bundled-skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md +22 -0
  206. package/bundled-skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md +23 -0
  207. package/bundled-skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md +22 -0
  208. package/bundled-skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md +23 -0
  209. package/bundled-skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md +22 -0
  210. package/bundled-skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md +22 -0
  211. package/bundled-skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md +22 -0
  212. package/bundled-skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md +22 -0
  213. package/bundled-skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md +22 -0
  214. package/bundled-skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md +22 -0
  215. package/bundled-skills/vercel-optimize/references/support-topics/external-api-critical-path.md +22 -0
  216. package/bundled-skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md +22 -0
  217. package/bundled-skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md +22 -0
  218. package/bundled-skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md +22 -0
  219. package/bundled-skills/vercel-optimize/references/support-topics/function-invocation-reduction.md +22 -0
  220. package/bundled-skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md +23 -0
  221. package/bundled-skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md +22 -0
  222. package/bundled-skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md +22 -0
  223. package/bundled-skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md +22 -0
  224. package/bundled-skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md +22 -0
  225. package/bundled-skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md +23 -0
  226. package/bundled-skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md +23 -0
  227. package/bundled-skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md +23 -0
  228. package/bundled-skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md +22 -0
  229. package/bundled-skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md +23 -0
  230. package/bundled-skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md +22 -0
  231. package/bundled-skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md +23 -0
  232. package/bundled-skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md +22 -0
  233. package/bundled-skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md +22 -0
  234. package/bundled-skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md +22 -0
  235. package/bundled-skills/vercel-optimize/references/support-topics/route-error-durable-offload.md +22 -0
  236. package/bundled-skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md +22 -0
  237. package/bundled-skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md +22 -0
  238. package/bundled-skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md +22 -0
  239. package/bundled-skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md +22 -0
  240. package/bundled-skills/vercel-optimize/references/support-topics/usage-spike-triage.md +22 -0
  241. package/bundled-skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md +23 -0
  242. package/bundled-skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md +22 -0
  243. package/bundled-skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md +23 -0
  244. package/bundled-skills/vercel-optimize/references/verification.md +102 -0
  245. package/bundled-skills/vercel-optimize/references/voice.md +76 -0
  246. package/bundled-skills/vercel-optimize/scripts/budget-summary.mjs +56 -0
  247. package/bundled-skills/vercel-optimize/scripts/build-docs.mjs +74 -0
  248. package/bundled-skills/vercel-optimize/scripts/check-citations.mjs +81 -0
  249. package/bundled-skills/vercel-optimize/scripts/check-docs-fresh.mjs +93 -0
  250. package/bundled-skills/vercel-optimize/scripts/collect-signals.mjs +576 -0
  251. package/bundled-skills/vercel-optimize/scripts/collect-sub-agent-outputs.mjs +296 -0
  252. package/bundled-skills/vercel-optimize/scripts/deep-dive.mjs +319 -0
  253. package/bundled-skills/vercel-optimize/scripts/gate-investigations.mjs +166 -0
  254. package/bundled-skills/vercel-optimize/scripts/merge-signals.mjs +192 -0
  255. package/bundled-skills/vercel-optimize/scripts/prepare-investigation-brief.mjs +231 -0
  256. package/bundled-skills/vercel-optimize/scripts/reconcile-candidates.mjs +62 -0
  257. package/bundled-skills/vercel-optimize/scripts/render-report.mjs +437 -0
  258. package/bundled-skills/vercel-optimize/scripts/scan-codebase.mjs +313 -0
  259. package/bundled-skills/vercel-optimize/scripts/verify-and-regen.mjs +346 -0
  260. package/bundled-skills/vercel-optimize/scripts/verify-finding.mjs +19 -0
  261. package/bundled-skills/vercel-react-view-transitions/SKILL.md +327 -0
  262. package/bundled-skills/vercel-react-view-transitions/references/css-recipes.md +242 -0
  263. package/bundled-skills/vercel-react-view-transitions/references/implementation.md +182 -0
  264. package/bundled-skills/vercel-react-view-transitions/references/nextjs.md +176 -0
  265. package/bundled-skills/vercel-react-view-transitions/references/patterns.md +262 -0
  266. package/bundled-skills/vibe-code-cleanup/SKILL.md +4 -4
  267. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +3 -2
  268. package/package.json +1 -1
  269. package/skills_index.json +338 -4
@@ -0,0 +1,86 @@
1
+ // Auto-detect repo-root for claim verification. Priority: Vercel API rootDirectory > --repo-root > walk-up.
2
+ // In a monorepo the sub-agent emits paths like `apps/<app>/src/...` and the verifier needs the prefix root, not the app dir.
3
+
4
+ import { access } from 'node:fs/promises';
5
+ import { join, dirname, resolve, normalize } from 'node:path';
6
+
7
+ // Prefer affectedFiles[0] over findingRefs — findingRefs often share the same file.
8
+ export function pickProbeFile(recs) {
9
+ for (const r of (recs ?? [])) {
10
+ if (r?.abstain) continue;
11
+ const af = Array.isArray(r?.affectedFiles) ? r.affectedFiles[0] : null;
12
+ if (typeof af === 'string' && af.length > 0) return af;
13
+ const ref = Array.isArray(r?.findingRefs) ? r.findingRefs[0] : null;
14
+ if (typeof ref === 'string' && ref.length > 0) {
15
+ const m = ref.match(/^(.+?):\d+$/);
16
+ if (m) return m[1];
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+
22
+ export async function fileResolvesAt(root, file) {
23
+ try {
24
+ await access(join(root, file));
25
+ return true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ export async function detectRepoRoot(probeFile, startDir, maxDepth = 10) {
32
+ let dir = resolve(startDir);
33
+ for (let depth = 0; depth < maxDepth; depth++) {
34
+ if (await fileResolvesAt(dir, probeFile)) return dir;
35
+ const parent = dirname(dir);
36
+ if (parent === dir) return null;
37
+ dir = parent;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ // rootDirectory "apps/fixture-site" + cwd .../monorepo/apps/fixture-site → repo root .../monorepo.
43
+ export function deriveRootFromSignals(signals, cwd = process.cwd()) {
44
+ const dir = signals?.project?.rootDirectory;
45
+ if (!dir || typeof dir !== 'string') return null;
46
+ const offset = normalize(dir).replace(/^\.\/?/, '').replace(/\/$/, '');
47
+ if (!offset) return null;
48
+ const cwdAbs = resolve(cwd);
49
+ // Match `<root>/<offset>` OR `<root>/<offset>/<more>` — orchestrator may run from a subdir.
50
+ const parts = cwdAbs.split('/');
51
+ const offsetParts = offset.split('/');
52
+ for (let start = parts.length - offsetParts.length; start >= 0; start--) {
53
+ const slice = parts.slice(start, start + offsetParts.length).join('/');
54
+ if (slice === offset) {
55
+ const root = parts.slice(0, start).join('/');
56
+ return root || '/';
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+
62
+ export async function resolveRepoRoot(recs, suppliedRoot, cwd = process.cwd(), signals = null) {
63
+ if (signals) {
64
+ const apiRoot = deriveRootFromSignals(signals, cwd);
65
+ if (apiRoot) {
66
+ return { root: apiRoot, source: 'api', probe: null, apiOffset: signals?.project?.rootDirectory ?? null };
67
+ }
68
+ }
69
+
70
+ const probe = pickProbeFile(recs);
71
+ if (!probe) {
72
+ return { root: suppliedRoot ?? '.', source: suppliedRoot ? 'supplied' : 'default', probe: null };
73
+ }
74
+ if (suppliedRoot && await fileResolvesAt(suppliedRoot, probe)) {
75
+ return { root: suppliedRoot, source: 'supplied', probe };
76
+ }
77
+ const detected = await detectRepoRoot(probe, suppliedRoot ?? cwd);
78
+ if (detected) {
79
+ return {
80
+ root: detected,
81
+ source: suppliedRoot ? 'corrected' : 'auto-detected',
82
+ probe,
83
+ };
84
+ }
85
+ return { root: suppliedRoot ?? '.', source: suppliedRoot ? 'supplied' : 'default', probe };
86
+ }
@@ -0,0 +1,220 @@
1
+ // Canonicalize Next.js 16 segment-tree metric route paths so gate dedup doesn't burn budget on N copies of the same source file.
2
+ //
3
+ // Next.js folds flag state into base64 prefix, dynamic placeholders into `$d$X`, route groups into `!K..p`, cache-lifecycle leaves into `__PAGE__.segment` / `_tree.segment` / `_index.segment`. ~4-10x dupes per page without canonicalization.
4
+ //
5
+ // This module is the ONLY place we touch Next.js metric path encoding — every gate calls canonicalizeRoute before aggregating.
6
+
7
+ export const ROUTE_SHAPE_RE = /(?:^.{200,}$)|[\s'"`,;&=<>(){}!\\^|\u0000-\u001F]|%(?:22|5B|5C|7B|7D|20|3C|3E|26)|localhost:|https?:\/|\/\/(?!$)|[:,\$\s]$|\.segments?\/|__PAGE__|@[a-z]/i;
8
+
9
+ export function isSegmentTreePath(route) {
10
+ if (typeof route !== 'string') return false;
11
+ return /\.segments(\/|$)/.test(route);
12
+ }
13
+
14
+ export function canonicalizeRoute(route) {
15
+ if (typeof route !== 'string' || route.length === 0) return route;
16
+ if (!isSegmentTreePath(route)) return stripRouteGroups(replaceBase64WithDynamic(route));
17
+
18
+ // Discard prefix (flag-state + dynamic value, both noise). Tail is the segment-tree node.
19
+ const idx = route.indexOf('.segments');
20
+ if (idx < 0) return route;
21
+ const segmentTail = route.slice(idx + '.segments'.length).replace(/^\//, '');
22
+
23
+ // _tree.segment / _index.segment have no per-segment tail — fall back to static head of prefix.
24
+ if (segmentTail === '_tree.segment' || segmentTail === '_index.segment') {
25
+ return canonicalizeBranchPrefix(route, idx);
26
+ }
27
+
28
+ const parts = segmentTail.split('/').filter((p) => p && !isMetricLeaf(p));
29
+ if (parts.length === 0) return canonicalizeBranchPrefix(route, idx);
30
+ const decoded = parts.map(decodeSegmentToken).filter(Boolean);
31
+ if (decoded.length === 0) return canonicalizeBranchPrefix(route, idx);
32
+ // scan-codebase's routePath enumeration drops route groups — match it or the route→file lookup breaks.
33
+ return stripRouteGroups('/' + decoded.join('/'));
34
+ }
35
+
36
+ function canonicalizeBranchPrefix(route, segmentsIdx) {
37
+ const prefix = route.slice(0, segmentsIdx);
38
+ const parts = prefix.split('/').filter(Boolean);
39
+ // Drop trailing dynamic value (e.g. "london") and base64 flag-state — neither is a route segment.
40
+ const cleaned = parts
41
+ .filter((p) => !isBase64FlagState(p))
42
+ .slice(0, -1);
43
+ if (cleaned.length === 0) return prefix || '/';
44
+ return '/' + cleaned.join('/');
45
+ }
46
+
47
+ function isMetricLeaf(token) {
48
+ return (
49
+ token === '__PAGE__.segment' ||
50
+ token === '_tree.segment' ||
51
+ token === '_index.segment' ||
52
+ token === '__page__.segment' ||
53
+ token.endsWith('.segment') && token.startsWith('_')
54
+ );
55
+ }
56
+
57
+ // Conservative heuristic for `eyJoYXNTZXNzaW9uIjpmYWxzZX0`-shape tokens: URL-safe base64 alphabet, length ≥16, mixed case.
58
+ function isBase64FlagState(token) {
59
+ if (typeof token !== 'string') return false;
60
+ if (token.length < 16) return false;
61
+ return /^[A-Za-z0-9_-]+$/.test(token) && /[A-Z]/.test(token) && /[a-z]/.test(token);
62
+ }
63
+
64
+ // `/event/<base64>/teaser` → `/event/[*]/teaser`. Stripping entirely (old behavior) corrupted segment count and broke route→file lookup.
65
+ function replaceBase64WithDynamic(route) {
66
+ if (typeof route !== 'string' || !route.startsWith('/')) return route;
67
+ const parts = route.split('/');
68
+ let mutated = false;
69
+ const replaced = parts.map((p, i) => {
70
+ if (i === 0) return p;
71
+ if (isBase64FlagState(p)) { mutated = true; return '[*]'; }
72
+ return p;
73
+ });
74
+ if (!mutated) return route;
75
+ return replaced.join('/') || '/';
76
+ }
77
+
78
+ // Route groups `(default)` never appear in rendered URLs — scan-codebase drops them, so canonical form must match.
79
+ function stripRouteGroups(route) {
80
+ if (typeof route !== 'string' || !route.includes('(')) return route;
81
+ const parts = route.split('/');
82
+ const kept = parts.filter((p) => !/^\([^)]+\)$/.test(p));
83
+ const joined = kept.join('/');
84
+ return joined.startsWith('/') ? (joined || '/') : '/' + joined;
85
+ }
86
+
87
+ // $d$X → [X] · $oc$X → [[...X]] · $c$X → [...X] · !K…p → (group) · metric-leaves → dropped.
88
+ function decodeSegmentToken(token) {
89
+ if (isMetricLeaf(token)) return '';
90
+
91
+ let t = token.endsWith('.segment') ? token.slice(0, -'.segment'.length) : token;
92
+
93
+ if (/^\$d\$/.test(t)) return `[${t.slice(3)}]`;
94
+ if (/^\$oc\$/.test(t)) return `[[...${t.slice(4)}]]`;
95
+ if (/^\$c\$/.test(t)) return `[...${t.slice(3)}]`;
96
+
97
+ // `!` is segment-tree marker; body is base64 of `(default)` etc. Accept only when decoded looks like `(name)`.
98
+ if (t.startsWith('!') && t.length > 1) {
99
+ const body = t.slice(1);
100
+ try {
101
+ const decoded = Buffer.from(body, 'base64').toString('utf-8');
102
+ if (/^\(.*\)$/.test(decoded)) return decoded;
103
+ } catch {
104
+ /* fall through on decode failure */
105
+ }
106
+ }
107
+
108
+ return t;
109
+ }
110
+
111
+ export function candidateKey(candidate) {
112
+ const route = candidate?.route ?? candidate?.hostname ?? null;
113
+ const kind = candidate?.kind ?? '?';
114
+ const canonical = route ? canonicalizeRoute(route) : '<account>';
115
+ return `${kind}::${canonical}`;
116
+ }
117
+
118
+ // Records alternates so the brief shows "all 4 cities collapse here" rather than a single per-city dupe.
119
+ export function mergeCandidates(a, b) {
120
+ if (!a) return b;
121
+ if (!b) return a;
122
+ const winner = (b.priority ?? 0) > (a.priority ?? 0) ? b : a;
123
+ const loser = winner === a ? b : a;
124
+ const altRoutes = new Set([
125
+ ...(Array.isArray(winner.aliasRoutes) ? winner.aliasRoutes : []),
126
+ ...(Array.isArray(loser.aliasRoutes) ? loser.aliasRoutes : []),
127
+ ]);
128
+ if (loser.route && loser.route !== winner.route) altRoutes.add(loser.route);
129
+ return {
130
+ ...winner,
131
+ // Canonicalize so briefs/reports/deep-dives see the clean path.
132
+ route: canonicalizeRoute(winner.route),
133
+ aliasRoutes: [...altRoutes].sort(),
134
+ mergedCount: (winner.mergedCount ?? 1) + (loser.mergedCount ?? 1),
135
+ };
136
+ }
137
+
138
+ // Input assumed sorted priority desc; output preserves that order. Account-scope pass through unchanged.
139
+ export function dedupeCandidates(candidates) {
140
+ const byKey = new Map();
141
+ const order = [];
142
+ const dropped = [];
143
+ for (const c of candidates) {
144
+ if (!c || c.scope === 'account' || (!c.route && !c.hostname)) {
145
+ order.push(c);
146
+ continue;
147
+ }
148
+ const key = candidateKey(c);
149
+ if (byKey.has(key)) {
150
+ const merged = mergeCandidates(byKey.get(key), c);
151
+ byKey.set(key, merged);
152
+ dropped.push({
153
+ candidate: c,
154
+ mergedInto: key,
155
+ reason: 'duplicate of higher-priority sibling (same source route after canonicalization)',
156
+ });
157
+ } else {
158
+ byKey.set(key, { ...c, route: c.route ? canonicalizeRoute(c.route) : c.route });
159
+ order.push({ __key: key });
160
+ }
161
+ }
162
+ const deduped = order.map((c) => (c && c.__key ? byKey.get(c.__key) : c));
163
+ return { deduped, dropped };
164
+ }
165
+
166
+ export function isLikelyNextRouteShape(route) {
167
+ return typeof route === 'string' && route.length > 0 && !ROUTE_SHAPE_RE.test(route);
168
+ }
169
+
170
+ export function routeShapeWarning(route, signals = {}) {
171
+ return routeShapeWarnings(route, signals)[0] ?? null;
172
+ }
173
+
174
+ export function routeShapeWarnings(route, signals = {}) {
175
+ if (typeof route !== 'string' || route.length === 0) return [];
176
+ const warnings = [];
177
+ if (ROUTE_SHAPE_RE.test(route)) warnings.push('route-shape:suspicious-metric-label');
178
+ const first = firstRouteSegment(canonicalizeRoute(route));
179
+ if (first && shouldWarnUnknownFirstSegment(first, signals)) {
180
+ warnings.push(`route-shape:unknown-first-segment:${first}`);
181
+ }
182
+ return warnings;
183
+ }
184
+
185
+ export function withRouteShapeWarnings(candidate, signals = {}) {
186
+ const warnings = routeShapeWarnings(candidate?.route, signals);
187
+ if (warnings.length === 0) return candidate;
188
+ return {
189
+ ...candidate,
190
+ warnings: [...new Set([...(Array.isArray(candidate.warnings) ? candidate.warnings : []), ...warnings])],
191
+ };
192
+ }
193
+
194
+ function firstRouteSegment(route) {
195
+ if (typeof route !== 'string') return null;
196
+ return route.split('/').filter(Boolean)[0] ?? null;
197
+ }
198
+
199
+ function shouldWarnUnknownFirstSegment(first, signals) {
200
+ const exempt = new Set(['_next', '_vercel', 'api', '.well-known']);
201
+ if (exempt.has(first)) return false;
202
+ const known = knownFirstSegments(signals);
203
+ if (known.size === 0) return false;
204
+ if ([...known].some(isDynamicPlaceholder)) return false;
205
+ return !known.has(first);
206
+ }
207
+
208
+ function knownFirstSegments(signals) {
209
+ const out = new Set();
210
+ const routes = signals.codebase?.routes ?? [];
211
+ for (const route of routes) {
212
+ const first = firstRouteSegment(route?.routePath);
213
+ if (first) out.add(first);
214
+ }
215
+ return out;
216
+ }
217
+
218
+ function isDynamicPlaceholder(segment) {
219
+ return /^\[.*\]$/.test(segment);
220
+ }
@@ -0,0 +1,38 @@
1
+ // Bot Protection evidence is usually account-level summary data. Avoid turning
2
+ // observed bot traffic into unsupported statements about exact WAF rule state.
3
+
4
+ export const metadata = {
5
+ id: 'bot-protection-certainty',
6
+ description: 'Soften unsupported Bot Protection / WAF certainty and require a staged rollout caveat.',
7
+ };
8
+
9
+ const STRING_FIELDS = ['what', 'why', 'fix', 'currentBehavior', 'desiredBehavior', 'verify'];
10
+
11
+ export function apply(rec) {
12
+ if (!String(rec?.candidateRef ?? '').startsWith('platform_bot_protection:')) return {};
13
+ const tags = [];
14
+ for (const field of STRING_FIELDS) {
15
+ if (typeof rec?.[field] !== 'string') continue;
16
+ const before = rec[field];
17
+ let after = before
18
+ .replace(/\bno\s+(?:firewall\s+)?bot_filter\s+rule\b/gi, 'the collected firewall summary did not show an enforced bot-filter rule')
19
+ .replace(/\b(?:bots?|bot traffic)\s+(?:is|are)\s+the\s+cause\b/gi, 'bot traffic is a likely contributor')
20
+ .replace(/\bwithout\s+false[- ]positive\s+risk\b/gi, 'with false-positive risk monitored during rollout')
21
+ .replace(/\bno\s+false[- ]positive\s+risk\b/gi, 'false-positive risk still needs rollout monitoring');
22
+ if (after !== before) {
23
+ rec[field] = after;
24
+ tags.push(`bot-protection-certainty:${field}`);
25
+ }
26
+ }
27
+
28
+ const text = STRING_FIELDS.map((field) => rec?.[field]).filter((s) => typeof s === 'string').join('\n');
29
+ if (/\b(?:Bot Protection|BotID|bot_filter|WAF)\b/i.test(text) &&
30
+ !/\bstaged\b[\s\S]{0,80}\b(?:log|allowlist|exclusions?)\b/i.test(text)) {
31
+ const caveat = ' Use a staged rollout that starts in Log mode where available, then moves to the appropriate Challenge or Deny action only after allowlist/exclusion review for known monitoring and partner clients.';
32
+ if (typeof rec.fix === 'string') rec.fix += caveat;
33
+ else rec.fix = caveat.trim();
34
+ tags.push('bot-protection-certainty:staged-rollout');
35
+ }
36
+
37
+ return tags.length > 0 ? { tags, needsReview: true } : {};
38
+ }
@@ -0,0 +1,30 @@
1
+ // A cacheTag() in the cached function is not proof that CMS edits invalidate it.
2
+ // The report must not claim "existing tags preserve instant updates" unless the
3
+ // investigation verifies matching revalidateTag/updateTag paths.
4
+
5
+ export const metadata = {
6
+ id: 'cache-tag-invalidation-certainty',
7
+ description: 'Remove unsupported certainty that existing cache tags already preserve CMS/on-demand invalidation.',
8
+ };
9
+
10
+ const STRING_FIELDS = ['what', 'why', 'fix', 'currentBehavior', 'desiredBehavior', 'verify'];
11
+ const UNSUPPORTED_TAG_CERTAINTY =
12
+ /\b(?:existing|current)\s+(?:cache\s+)?tags?\b[^.!?\n]{0,160}\b(?:preserve|keep|cover|maintain|ensure)\b[^.!?\n]{0,160}\b(?:instant|on-demand|CMS|content|publish|update|updates|invalidation|revalidation)\b[^.!?\n]*(?:[.!?]|$)/gi;
13
+ const SAFE_REPLACEMENT =
14
+ 'Confirm a matching revalidateTag() or updateTag() path for each cacheTag() before increasing the cache lifetime.';
15
+
16
+ export function apply(rec) {
17
+ const text = STRING_FIELDS.map((field) => rec?.[field]).filter((s) => typeof s === 'string').join('\n');
18
+ if (!/\bcache(?:Life|Tag)\b/.test(text)) return {};
19
+ const tags = [];
20
+ for (const field of STRING_FIELDS) {
21
+ if (typeof rec?.[field] !== 'string') continue;
22
+ const before = rec[field];
23
+ const after = before.replace(UNSUPPORTED_TAG_CERTAINTY, SAFE_REPLACEMENT);
24
+ if (after !== before) {
25
+ rec[field] = after;
26
+ tags.push(`cache-tag-invalidation-certainty:${field}`);
27
+ }
28
+ }
29
+ return tags.length > 0 ? { tags, needsReview: true } : {};
30
+ }
@@ -0,0 +1,52 @@
1
+ // Rewrite verifier-failed count claims to ground truth (or "a number of"
2
+ // when actual isn't numeric) so we don't ship false precision.
3
+
4
+ import { escapeRegex } from '../util.mjs';
5
+
6
+ export const metadata = {
7
+ id: 'count-correct',
8
+ description: 'Rewrite count claims to verified ground truth (count-correct) or "a number of" (count-strip) when verifier disagrees.',
9
+ };
10
+
11
+ const COUNT_CLAIM_TYPES = new Set(['pattern_count', 'repo_count', 'cited_count_literal']);
12
+
13
+ export function apply(rec, ctx = {}) {
14
+ const results = ctx.verifyResults ?? rec.verifyResults ?? rec.verification?.failed ?? null;
15
+ if (!Array.isArray(results) || results.length === 0) return {};
16
+
17
+ const tags = [];
18
+
19
+ for (const r of results) {
20
+ if (!r) continue;
21
+ const type = r.type ?? r.claimType;
22
+ if (!COUNT_CLAIM_TYPES.has(type)) continue;
23
+ const disp = r.disposition ?? (r.actual !== r.expected ? 'failed' : 'verified');
24
+ if (disp !== 'failed') continue;
25
+ const expected = r.expected;
26
+ const actual = r.actual;
27
+ const token = r.token ?? r.text ?? expected;
28
+ if (expected == null || token == null) continue;
29
+
30
+ if (typeof actual === 'number' && Number.isFinite(actual)) {
31
+ rewriteCount(rec, token, expected, `~${actual}`);
32
+ tags.push(`count-correct:${token}:${expected}->${actual}`);
33
+ } else {
34
+ rewriteCount(rec, token, expected, 'a number of');
35
+ tags.push(`count-strip:${token}`);
36
+ }
37
+ }
38
+
39
+ if (tags.length === 0) return {};
40
+ return { tags };
41
+ }
42
+
43
+ function rewriteCount(rec, token, oldCount, replacement) {
44
+ const fields = ['what', 'why', 'fix', 'currentBehavior', 'desiredBehavior'];
45
+ // Matches "60", "~60", and "60+" — LLM commonly writes "60+ icons".
46
+ const oldEsc = escapeRegex(String(oldCount));
47
+ const re = new RegExp(`\\b~?${oldEsc}\\+?\\s+${escapeRegex(token)}\\b`, 'g');
48
+ for (const f of fields) {
49
+ if (typeof rec[f] !== 'string') continue;
50
+ rec[f] = rec[f].replace(re, `${replacement} ${token}`);
51
+ }
52
+ }
@@ -0,0 +1,38 @@
1
+ // A function-duration optimization can reduce p95/CPU/GB-hr. It does not by
2
+ // itself reduce function invocation count unless the fix also adds CDN/static
3
+ // response caching.
4
+
5
+ export const metadata = {
6
+ id: 'function-duration-invocations',
7
+ description: 'Remove false claims that slow-route data-cache fixes reduce function invocation count.',
8
+ };
9
+
10
+ const STRING_FIELDS = [
11
+ 'what',
12
+ 'why',
13
+ 'fix',
14
+ 'currentBehavior',
15
+ 'desiredBehavior',
16
+ 'verify',
17
+ ];
18
+
19
+ const BAD_INVOCATION_CLAIM =
20
+ /\bfunction invocations?\b[^.!?\n]{0,120}\b(?:drop|drops|fall|falls|decrease|decreases|decline|declines|reduce|reduces|reduced|cut|cuts)\b[^.!?\n]*(?:[.!?]|$)|\b(?:drop|drops|fall|falls|decrease|decreases|decline|declines|reduce|reduces|reduced|cut|cuts)\b[^.!?\n]{0,120}\bfunction invocations?\b[^.!?\n]*(?:[.!?]|$)/gi;
21
+
22
+ const SAFE_REPLACEMENT =
23
+ '95th percentile duration should drop; function invocation count may stay flat unless a separate CDN or static-rendering change is made.';
24
+
25
+ export function apply(rec) {
26
+ if (!String(rec?.candidateRef ?? '').startsWith('slow_route:')) return {};
27
+ const tags = [];
28
+ for (const field of STRING_FIELDS) {
29
+ if (typeof rec?.[field] !== 'string') continue;
30
+ const before = rec[field];
31
+ const after = before.replace(BAD_INVOCATION_CLAIM, SAFE_REPLACEMENT);
32
+ if (after !== before) {
33
+ rec[field] = after;
34
+ tags.push(`function-duration-invocations:${field}`);
35
+ }
36
+ }
37
+ return tags.length > 0 ? { tags } : {};
38
+ }
@@ -0,0 +1,79 @@
1
+ // Sanitizer orchestrator. Order matters: citation strippers must run
2
+ // before missing-citation so an emptied citations[] still drops the rec.
3
+
4
+ import { applyDollarStrip } from '../impact-magnitude.mjs';
5
+ import { sanitizeCitations } from '../citations.mjs';
6
+ import * as vercelDirectiveStrip from './vercel-directive-strip.mjs';
7
+ import * as rateLimit from './rate-limit.mjs';
8
+ import * as preRelease from './pre-release.mjs';
9
+ import * as middlewareConflict from './middleware-conflict.mjs';
10
+ import * as undeclaredDep from './undeclared-dep.mjs';
11
+ import * as countCorrect from './count-correct.mjs';
12
+ import * as renderingModeMislabel from './rendering-mode-mislabel.mjs';
13
+ import * as windowUnits from './window-units.mjs';
14
+ import * as functionDurationInvocations from './function-duration-invocations.mjs';
15
+ import * as botProtectionCertainty from './bot-protection-certainty.mjs';
16
+ import * as cacheTagInvalidationCertainty from './cache-tag-invalidation-certainty.mjs';
17
+ import * as missingCitation from './missing-citation.mjs';
18
+
19
+ export const SANITIZERS = [
20
+ vercelDirectiveStrip,
21
+ rateLimit,
22
+ preRelease,
23
+ middlewareConflict,
24
+ undeclaredDep,
25
+ countCorrect,
26
+ renderingModeMislabel,
27
+ windowUnits,
28
+ functionDurationInvocations,
29
+ botProtectionCertainty,
30
+ cacheTagInvalidationCertainty,
31
+ ];
32
+
33
+ export function recordSanitizer(rec, tag) {
34
+ rec.sanitizerTrail = rec.sanitizerTrail ?? [];
35
+ rec.sanitizerTrail.push(tag);
36
+ }
37
+
38
+ export async function applySanitizers(rec, ctx = {}) {
39
+ applyDollarStrip(rec);
40
+
41
+ for (const s of SANITIZERS) {
42
+ const result = s.apply(rec, ctx) ?? {};
43
+ const tags = result.tags ?? (result.tag ? [result.tag] : []);
44
+ for (const t of tags) recordSanitizer(rec, t);
45
+ if (result.needsReview) rec.needsReview = true;
46
+ if (result.dropped) {
47
+ return { kept: false, rec, dropReason: tags[0] ?? `dropped-by:${s.metadata?.id ?? 'unknown'}` };
48
+ }
49
+ }
50
+
51
+ if (ctx.framework && ctx.version) {
52
+ const before = (rec.citations ?? []).slice();
53
+ const { strippedUnknown, strippedVersion } = await sanitizeCitations(rec, ctx.framework, ctx.version);
54
+ for (const u of strippedUnknown) recordSanitizer(rec, `unknown-citation:${u}`);
55
+ for (const u of strippedVersion) recordSanitizer(rec, `version-mismatch:${u}`);
56
+ const lostAny = strippedUnknown.length > 0 || strippedVersion.length > 0;
57
+ const lostAll = lostAny && (rec.citations ?? []).length === 0 && before.length > 0;
58
+ if (lostAll) rec.needsReview = true;
59
+ }
60
+
61
+ // missing-citation runs LAST so citation strippers above can starve a rec.
62
+ const missing = missingCitation.apply(rec, ctx) ?? {};
63
+ if (missing.dropped) {
64
+ return { kept: false, rec, dropReason: missing.tag ?? 'missing-citation' };
65
+ }
66
+
67
+ return { kept: true, rec };
68
+ }
69
+
70
+ export async function applySanitizersBatch(recs, ctx = {}) {
71
+ const kept = [];
72
+ const dropped = [];
73
+ for (const rec of recs) {
74
+ const r = await applySanitizers(rec, ctx);
75
+ if (r.kept) kept.push(r.rec);
76
+ else dropped.push({ rec: r.rec, dropReason: r.dropReason });
77
+ }
78
+ return { kept, dropped };
79
+ }
@@ -0,0 +1,36 @@
1
+ // Append a caveat when a rec targets a route covered by middleware: e.g.
2
+ // middleware setting Set-Cookie downstream poisons cache headers the rec
3
+ // adds. Trusts finding.routesCovered rather than re-implementing Next's
4
+ // matcher algorithm.
5
+
6
+ import { extractRoute } from '../util.mjs';
7
+
8
+ export const metadata = {
9
+ id: 'middleware-conflict',
10
+ description: 'Append caveat when rec targets a route covered by middleware.',
11
+ };
12
+
13
+ export function apply(rec, ctx = {}) {
14
+ const findings = ctx?.signals?.codebase?.findings ?? [];
15
+ const middlewareFinding = findings.find((f) => f?.scannerId === 'middleware-broad-matcher' || f?.id === 'middleware-broad-matcher');
16
+ if (!middlewareFinding) return {};
17
+
18
+ const route = extractRoute(rec);
19
+ if (!route) return {};
20
+
21
+ const matcher = middlewareFinding.detail?.matcher
22
+ ?? middlewareFinding.matcher
23
+ ?? '(unspecified matcher)';
24
+ const middlewareFile = middlewareFinding.file ?? middlewareFinding.path ?? 'middleware.ts';
25
+
26
+ const covered = middlewareFinding.detail?.routesCovered ?? middlewareFinding.routesCovered;
27
+ if (Array.isArray(covered) && covered.length > 0 && !covered.includes(route)) {
28
+ return {};
29
+ }
30
+
31
+ const tag = `middleware-conflict:${matcher}`;
32
+ const caveat = `\n\n_Caveat: Middleware at \`${middlewareFile}\` (matcher: \`${matcher}\`) may intercept \`${route}\` and alter request/response before this fix takes effect. Verify the middleware does not set headers (e.g. \`Set-Cookie\`) that would invalidate caching._`;
33
+
34
+ if (typeof rec.fix === 'string') rec.fix += caveat;
35
+ return { tag, needsReview: true };
36
+ }
@@ -0,0 +1,16 @@
1
+ // Final-gate sanitizer: drops a rec with no citations left after
2
+ // unknown-citation + version-mismatch have run. Every rec must carry ≥1
3
+ // citation.
4
+
5
+ export const metadata = {
6
+ id: 'missing-citation',
7
+ description: 'Drop rec when citations[] is empty after other sanitizers.',
8
+ };
9
+
10
+ export function apply(rec, _ctx = {}) {
11
+ const cites = Array.isArray(rec.citations) ? rec.citations : [];
12
+ if (cites.length === 0) {
13
+ return { dropped: true, tag: 'missing-citation' };
14
+ }
15
+ return {};
16
+ }
@@ -0,0 +1,74 @@
1
+ // Append a caveat (don't drop — customer may opt in to canary) when a
2
+ // fix needs a canary/rc/beta dep version.
3
+
4
+ import { matchesFrameworkVersion } from '../citations.mjs';
5
+
6
+ const PRE_RELEASE_FEATURES = [
7
+ {
8
+ match: /\bppr\b|partial[- ]?prerendering/i,
9
+ requires: 'next@canary',
10
+ message: 'PPR is experimental — verify your Next.js version supports it as stable',
11
+ },
12
+ {
13
+ match: /\buse cache['"]?\s*directive\b|"use cache"|'use cache'/i,
14
+ requires: 'next@>=15.0.0',
15
+ message: 'use cache directive is stable in 15+',
16
+ },
17
+ {
18
+ match: /\bcacheLife\(/i,
19
+ requires: 'next@>=15.0.0',
20
+ message: 'cacheLife is stable in 15+',
21
+ },
22
+ {
23
+ match: /\bcacheTag\(/i,
24
+ requires: 'next@>=15.0.0',
25
+ message: 'cacheTag is stable in 15+',
26
+ },
27
+ ];
28
+
29
+ const SEMVER_PRE_RELEASE_RE = /\b([\w-]+)@(\d+\.\d+\.\d+-(?:rc|beta|canary|alpha|next|exp)[\w.-]*)/g;
30
+
31
+ export const metadata = {
32
+ id: 'pre-release',
33
+ description: 'Append caveat when fix targets a canary/rc/beta feature.',
34
+ };
35
+
36
+ export function apply(rec, ctx = {}) {
37
+ const text = [rec.fix, rec.currentBehavior, rec.desiredBehavior]
38
+ .filter((s) => typeof s === 'string')
39
+ .join('\n');
40
+ if (!text) return {};
41
+
42
+ const tags = [];
43
+ const caveats = [];
44
+
45
+ for (const feat of PRE_RELEASE_FEATURES) {
46
+ if (feat.match.test(text)) {
47
+ if (featureAvailableForStack(feat, ctx)) continue;
48
+ const tag = `pre-release:${feat.requires}`;
49
+ if (!tags.includes(tag)) {
50
+ tags.push(tag);
51
+ caveats.push(`Requires ${feat.requires} (${feat.message}).`);
52
+ }
53
+ }
54
+ }
55
+
56
+ for (const m of text.matchAll(SEMVER_PRE_RELEASE_RE)) {
57
+ const [, pkg, version] = m;
58
+ const tag = `pre-release:${pkg}@${version}`;
59
+ if (!tags.includes(tag)) {
60
+ tags.push(tag);
61
+ caveats.push(`Requires pre-release version: \`${pkg}@${version}\`.`);
62
+ }
63
+ }
64
+
65
+ if (tags.length === 0) return {};
66
+ const caveatBlock = '\n\n_Note: ' + caveats.join(' ') + '_';
67
+ if (typeof rec.fix === 'string') rec.fix += caveatBlock;
68
+ return { tags, needsReview: true };
69
+ }
70
+
71
+ function featureAvailableForStack(feat, ctx) {
72
+ if (!ctx?.framework || !ctx?.version) return false;
73
+ return matchesFrameworkVersion(feat.requires, ctx.framework, ctx.version);
74
+ }