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,143 @@
1
+ // Maps billing line items to gate coverage so report surfaces uncovered dimensions (Sandbox, AI Gateway, Build, …) as blind spots.
2
+
3
+ // Service → billing dimension. dim=null means uncovered. Substring match — Vercel billing names are stable but untyped.
4
+ const SERVICE_DIMENSION = [
5
+ { match: /^Function Duration$/i, dim: 'function-duration' },
6
+ { match: /^Function Invocations$/i, dim: 'function-duration' },
7
+ { match: /^Fluid Active CPU$/i, dim: 'function-duration' },
8
+ { match: /^Fluid Provisioned Memory$/i, dim: 'function-duration' },
9
+ { match: /^Edge Requests$/i, dim: 'edge-requests' },
10
+ { match: /^Edge Requests.*Additional CPU Duration/i, dim: 'edge-requests' },
11
+ { match: /^Edge Function Execution Units$/i, dim: 'edge-requests' },
12
+ { match: /^Edge Middleware Invocations$/i, dim: 'edge-requests' },
13
+ { match: /^ISR (Reads|Writes)$/i, dim: 'isr' },
14
+ { match: /^Speed Insights( Data Points)?$/i, dim: 'speed-insights' },
15
+ { match: /^Image Optimization/i, dim: 'image-optimization' },
16
+ // Indirect: bot-protection gate addresses bandwidth/edge spend.
17
+ { match: /^Fast Data Transfer$/i, dim: 'edge-requests' },
18
+ { match: /^Fast Origin Transfer$/i, dim: 'edge-requests' },
19
+
20
+ // Uncovered.
21
+ { match: /^Sandbox/i, dim: null, family: 'sandbox' },
22
+ { match: /^AI Gateway$/i, dim: null, family: 'ai-gateway' },
23
+ { match: /^Build Minutes$/i, dim: 'build', family: 'build' },
24
+ { match: /^Build CPU Minutes$/i, dim: 'build', family: 'build' },
25
+ { match: /^Private Data Transfer$/i, dim: null, family: 'private-network' },
26
+ { match: /^Secure Compute Network$/i, dim: null, family: 'private-network' },
27
+ { match: /^Drains Volume$/i, dim: null, family: 'drains' },
28
+ { match: /^Observability Events$/i, dim: 'observability-events', family: 'observability-events' },
29
+ { match: /^Blob/i, dim: null, family: 'blob' },
30
+ { match: /^Edge Config (Reads|Writes)$/i, dim: null, family: 'edge-config' },
31
+ { match: /^Runtime Cache/i, dim: null, family: 'runtime-cache' },
32
+ { match: /^Microfrontends/i, dim: null, family: 'microfrontends' },
33
+ { match: /^Workflow/i, dim: null, family: 'workflow' },
34
+ { match: /^Queue/i, dim: null, family: 'queues' },
35
+ { match: /^Flag Requests$/i, dim: null, family: 'flags' },
36
+ { match: /^Flags Explorer/i, dim: null, family: 'flags' },
37
+ { match: /^BotID/i, dim: null, family: 'botid' },
38
+ { match: /^Firewall/i, dim: null, family: 'firewall' },
39
+ { match: /^Vercel Agent$/i, dim: null, family: 'vercel-agent' },
40
+
41
+ // Fixed costs (seats, contracts) — not actionable.
42
+ { match: /^v0 /i, dim: null, family: 'fixed', actionable: false },
43
+ { match: /^Additional Team Seats$/i, dim: null, family: 'fixed', actionable: false },
44
+ { match: /^SAML$/i, dim: null, family: 'fixed', actionable: false },
45
+ { match: /^HIPAA BAA$/i, dim: null, family: 'fixed', actionable: false },
46
+ { match: /^SIEM Integration$/i, dim: null, family: 'fixed', actionable: false },
47
+ { match: /^Web Analytics/i, dim: null, family: 'fixed', actionable: false },
48
+ { match: /^Static IPs$/i, dim: null, family: 'fixed', actionable: false },
49
+ { match: /^Bulk Redirects$/i, dim: null, family: 'fixed', actionable: false },
50
+ { match: /^Preview Deployment Suffix$/i, dim: null, family: 'fixed', actionable: false },
51
+ { match: /^Rolling Releases$/i, dim: null, family: 'fixed', actionable: false },
52
+ { match: /^Observability Plus$/i, dim: null, family: 'fixed', actionable: false },
53
+ { match: /^Platform Customer Usage$/i, dim: null, family: 'fixed', actionable: false },
54
+ { match: /^Advanced Deployment Protection$/i, dim: null, family: 'fixed', actionable: false },
55
+ ];
56
+
57
+ export function classifyService(serviceName, activeDims) {
58
+ if (!serviceName) return { covered: false, family: 'unknown' };
59
+ for (const e of SERVICE_DIMENSION) {
60
+ if (e.match.test(serviceName)) {
61
+ if (e.dim && activeDims.has(e.dim)) return { covered: true, dim: e.dim };
62
+ return { covered: false, family: e.family ?? 'unknown', actionable: e.actionable ?? true };
63
+ }
64
+ }
65
+ return { covered: false, family: 'unknown', actionable: true };
66
+ }
67
+
68
+ export function computeCostCoverage(usage, gates) {
69
+ const services = Array.isArray(usage?.services) ? usage.services : [];
70
+ const activeDims = new Set(
71
+ (gates ?? [])
72
+ .map((g) => g?.metadata?.billingDimension)
73
+ .filter((d) => typeof d === 'string' && d !== 'mixed')
74
+ );
75
+ let total = 0;
76
+ let covered = 0;
77
+ let uncovered = 0;
78
+ const byFamily = new Map();
79
+
80
+ for (const s of services) {
81
+ const billed = Number(s.billedCost ?? 0);
82
+ if (!Number.isFinite(billed) || billed <= 0) continue;
83
+ total += billed;
84
+ const c = classifyService(s.name, activeDims);
85
+ if (c.covered) {
86
+ covered += billed;
87
+ continue;
88
+ }
89
+ uncovered += billed;
90
+ const key = c.family;
91
+ const prev = byFamily.get(key) ?? { family: key, billed: 0, services: [], actionable: c.actionable !== false };
92
+ prev.billed += billed;
93
+ prev.services.push({ name: s.name, billed });
94
+ prev.actionable = prev.actionable && (c.actionable !== false);
95
+ byFamily.set(key, prev);
96
+ }
97
+
98
+ const uncoveredByFamily = [...byFamily.values()]
99
+ .sort((a, b) => b.billed - a.billed)
100
+ .map((f) => ({ ...f, services: f.services.sort((a, b) => b.billed - a.billed) }));
101
+
102
+ // Pick top gaps globally so multiple families surface (Sandbox + AI Gateway + Build, not 5 Sandbox sub-services). Exclude fixed costs — seats aren't actionable workload.
103
+ const allActionableServices = [];
104
+ for (const family of uncoveredByFamily) {
105
+ if (!family.actionable) continue;
106
+ for (const s of family.services) {
107
+ allActionableServices.push({ name: s.name, billed: s.billed, family: family.family });
108
+ }
109
+ }
110
+ allActionableServices.sort((a, b) => b.billed - a.billed);
111
+ const topGaps = allActionableServices.slice(0, 5).map((s) => ({
112
+ ...s,
113
+ share: total > 0 ? s.billed / total : 0,
114
+ }));
115
+ return { totalBilled: total, coveredBilled: covered, uncoveredBilled: uncovered, uncoveredByFamily, topGaps };
116
+ }
117
+
118
+ export function renderCostCoverageMarkdown(coverage) {
119
+ if (!coverage || !Number.isFinite(coverage.totalBilled) || coverage.totalBilled <= 0) return [];
120
+ const { totalBilled, coveredBilled, uncoveredBilled, topGaps } = coverage;
121
+ const actionableGaps = topGaps.filter((g) => g.share >= 0.01); // 1%+ share
122
+ if (actionableGaps.length === 0) return [];
123
+ const lines = [];
124
+ lines.push('');
125
+ lines.push('### Coverage gaps');
126
+ lines.push('');
127
+ const coveredPct = totalBilled > 0 ? (coveredBilled / totalBilled) * 100 : 0;
128
+ const uncoveredPct = totalBilled > 0 ? (uncoveredBilled / totalBilled) * 100 : 0;
129
+ lines.push(`This audit has metric coverage for **$${coveredBilled.toFixed(0)} (${coveredPct.toFixed(0)}%)** of this bill via function-duration, edge-requests, ISR, middleware, and image-optimization dimensions. **$${uncoveredBilled.toFixed(0)} (${uncoveredPct.toFixed(0)}%)** sits in billed areas this run cannot analyze safely, including the top actionable items below:`);
130
+ lines.push('');
131
+ lines.push('| Service | Billed | Share | Family | Coverage |');
132
+ lines.push('|---|---|---|---|---|');
133
+ for (const g of actionableGaps) {
134
+ lines.push(`| ${escapeCell(g.name)} | $${g.billed.toFixed(2)} | ${(g.share * 100).toFixed(1)}% | ${g.family} | _not analyzed in this run_ |`);
135
+ }
136
+ lines.push('');
137
+ lines.push('_Recommendations in this report address the covered dimensions. The uncovered rows are not ignored; they need a separate investigation before we can make safe recommendations._');
138
+ return lines;
139
+ }
140
+
141
+ function escapeCell(s) {
142
+ return String(s ?? '').replace(/\|/g, '\\|').replace(/\n/g, ' ');
143
+ }
@@ -0,0 +1,325 @@
1
+ const NO_VALUE = '<none>';
2
+
3
+ export function dedupeRecommendations(recommendations = []) {
4
+ if (!Array.isArray(recommendations)) {
5
+ throw new TypeError('dedupeRecommendations recommendations must be an array');
6
+ }
7
+
8
+ const byKey = new Map();
9
+ const order = [];
10
+ for (const rec of recommendations) {
11
+ if (!rec || typeof rec !== 'object' || rec.abstain === true) {
12
+ order.push(rec);
13
+ continue;
14
+ }
15
+
16
+ const key = recommendationKey(rec);
17
+ if (!byKey.has(key)) {
18
+ const normalized = withDedupMetadata(rec);
19
+ byKey.set(key, normalized);
20
+ order.push({ __dedupKey: key });
21
+ continue;
22
+ }
23
+
24
+ const current = byKey.get(key);
25
+ const merged = mergeDuplicateRecs(current, rec);
26
+ byKey.set(key, merged);
27
+ }
28
+
29
+ return order.map((entry) => entry?.__dedupKey ? byKey.get(entry.__dedupKey) : entry);
30
+ }
31
+
32
+ export function recommendationKey(rec) {
33
+ const intent = dedupIntent(rec);
34
+ const bucket = intent === 'cache-control:s-maxage'
35
+ ? NO_VALUE
36
+ : String(rec?.bucket ?? NO_VALUE);
37
+ return JSON.stringify([
38
+ bucket,
39
+ dedupEditTarget(rec),
40
+ primarySkillRule(rec),
41
+ intent,
42
+ ]);
43
+ }
44
+
45
+ export function normalizePath(path) {
46
+ if (typeof path !== 'string' || path.trim() === '') return NO_VALUE;
47
+ return path
48
+ .trim()
49
+ .replace(/\\/g, '/')
50
+ .replace(/^\.\//, '')
51
+ .replace(/\/+/g, '/')
52
+ .replace(/:(\d+)(?::\d+)?$/, '');
53
+ }
54
+
55
+ export function primarySkillRule(rec) {
56
+ const citations = Array.isArray(rec?.citations) ? rec.citations : [];
57
+ return citations.find((c) => typeof c === 'string' && /^[A-Za-z][\w-]*:[A-Za-z][\w-]*$/.test(c)) ?? NO_VALUE;
58
+ }
59
+
60
+ export function fixShape(rec) {
61
+ if (typeof rec?.fixShape === 'string' && rec.fixShape.trim()) {
62
+ return normalizeFixText(rec.fixShape);
63
+ }
64
+ const primaryText = [rec?.fix, rec?.desiredBehavior]
65
+ .filter((v) => typeof v === 'string' && v.trim())
66
+ .join('\n');
67
+ const text = primaryText || rec?.what;
68
+ return normalizeFixText(text);
69
+ }
70
+
71
+ export function dedupIntent(rec) {
72
+ if (isSMaxageCacheHeaderRec(rec)) return 'cache-control:s-maxage';
73
+ if (isCacheLifeRec(rec)) return cacheLifeIntent(rec);
74
+ const sharedFunction = sharedFunctionTarget(rec);
75
+ if (sharedFunction) return `parallel-shared-helper:${sharedFunction}`;
76
+ return fixShape(rec);
77
+ }
78
+
79
+ export function dedupEditTarget(rec) {
80
+ return sharedFunctionTarget(rec) ?? normalizePath(firstAffectedFile(rec));
81
+ }
82
+
83
+ function firstAffectedFile(rec) {
84
+ const direct = affectedFiles(rec);
85
+ const editTarget = referencedCodeFiles(rec, ['fix', 'desiredBehavior', 'currentBehavior'])[0];
86
+ if (editTarget) return editTarget;
87
+ const referenced = referencedCodeFiles(rec)
88
+ .find((file) => direct.includes(file));
89
+ if (referenced) return referenced;
90
+ return Array.isArray(rec?.affectedFiles) ? rec.affectedFiles[0] : null;
91
+ }
92
+
93
+ function affectedFiles(rec) {
94
+ return Array.isArray(rec?.affectedFiles)
95
+ ? rec.affectedFiles.map(normalizePath).filter((file) => file !== NO_VALUE)
96
+ : [];
97
+ }
98
+
99
+ function referencedCodeFiles(rec, fields = ['what', 'why', 'fix', 'currentBehavior', 'desiredBehavior', 'verify']) {
100
+ const text = fields
101
+ .map((field) => rec?.[field])
102
+ .filter((v) => typeof v === 'string' && v.trim())
103
+ .join('\n');
104
+ const matches = text.match(/(?:^|[\s`'"(])((?:\.{1,2}\/|[A-Za-z0-9_.@-]+\/)[A-Za-z0-9_./@[\]()-]+\.(?:mjs|cjs|js|jsx|ts|tsx))/g) ?? [];
105
+ return unique(matches.map((m) =>
106
+ normalizePath(m.replace(/^[\s`'"(]+/, ''))
107
+ ).filter((file) => file !== NO_VALUE));
108
+ }
109
+
110
+ function isSMaxageCacheHeaderRec(rec) {
111
+ const text = [
112
+ rec?.what,
113
+ rec?.why,
114
+ rec?.fix,
115
+ rec?.desiredBehavior,
116
+ ...(Array.isArray(rec?.citations) ? rec.citations : []),
117
+ ].filter(Boolean).join('\n');
118
+ return /\bs-maxage\b/i.test(text) &&
119
+ /\b(?:Cache-Control|CDN cache|cdn-cache|caching\/cdn-cache)\b/i.test(text);
120
+ }
121
+
122
+ function isCacheLifeRec(rec) {
123
+ const text = [
124
+ rec?.candidateRef,
125
+ rec?.what,
126
+ rec?.why,
127
+ rec?.fix,
128
+ rec?.desiredBehavior,
129
+ ...(Array.isArray(rec?.citations) ? rec.citations : []),
130
+ ].filter(Boolean).join('\n');
131
+ return /^isr_overrevalidation:/.test(String(rec?.candidateRef ?? '')) &&
132
+ /\bcacheLife\s*\(|\bcacheLife\b/i.test(text);
133
+ }
134
+
135
+ function sharedFunctionTarget(rec) {
136
+ const rule = primarySkillRule(rec);
137
+ if (!/(?:^|:)async-parallel$|(?:^|:)server-parallel-fetching$|(?:^|:)async-suspense-boundaries$/.test(rule)) {
138
+ return null;
139
+ }
140
+ const text = [
141
+ rec?.what,
142
+ rec?.why,
143
+ rec?.fix,
144
+ rec?.currentBehavior,
145
+ rec?.desiredBehavior,
146
+ ].filter((v) => typeof v === 'string' && v.trim()).join('\n');
147
+ const names = [
148
+ ...text.matchAll(/\b(?:get|fetch|load|read|render|create|generate|filter|resolve)[A-Z][A-Za-z0-9_]*\b/g),
149
+ ].map((m) => m[0]);
150
+ const stop = new Set([
151
+ 'getPayload',
152
+ 'draftMode',
153
+ 'notFound',
154
+ 'redirect',
155
+ 'Promise',
156
+ 'Response',
157
+ 'NextResponse',
158
+ ]);
159
+ const candidates = names.filter((name) => !stop.has(name));
160
+ if (candidates.length === 0) return null;
161
+ const score = new Map();
162
+ for (const name of candidates) {
163
+ score.set(name, (score.get(name) ?? 0) + 1);
164
+ }
165
+ return [...score.entries()]
166
+ .sort((a, b) => b[1] - a[1] || text.indexOf(a[0]) - text.indexOf(b[0]))
167
+ .map(([name]) => `function:${name}`)[0] ?? null;
168
+ }
169
+
170
+ function cacheLifeIntent(rec) {
171
+ const text = [
172
+ rec?.what,
173
+ rec?.why,
174
+ rec?.fix,
175
+ rec?.desiredBehavior,
176
+ rec?.verify,
177
+ ].filter(Boolean).join('\n');
178
+ const profiles = unique(
179
+ [...text.matchAll(/\bcacheLife\s*\(\s*['"`]([^'"`]+)['"`]/g)]
180
+ .map((m) => m[1])
181
+ );
182
+ const tags = unique([
183
+ ...[...text.matchAll(/\bcacheTag\s*\(([^)]*)\)/gs)].flatMap((m) => {
184
+ const args = m[1] ?? '';
185
+ return [
186
+ ...[...args.matchAll(/['"]([^'"]+)['"]/g)].map((x) => x[1]),
187
+ ...[...args.matchAll(/`([^`]+)`/g)].map((x) => x[1].includes('${') ? `${x[1].split('${')[0]}*` : x[1]),
188
+ ];
189
+ }),
190
+ ]);
191
+ const invalidation = /\b(?:revalidateTag|updateTag)\s*\(/.test(text) ? 'with-invalidation-api' : 'no-invalidation-api';
192
+ return [
193
+ 'next-cache:cache-life',
194
+ profiles.join('|') || NO_VALUE,
195
+ tags.join('|') || NO_VALUE,
196
+ invalidation,
197
+ ].join(':');
198
+ }
199
+
200
+ function unique(values) {
201
+ return Array.from(new Set(values.filter((v) => typeof v === 'string' && v.trim()).map((v) => v.trim()))).sort();
202
+ }
203
+
204
+ function normalizeFixText(text) {
205
+ if (typeof text !== 'string' || text.trim() === '') return NO_VALUE;
206
+ return text
207
+ .toLowerCase()
208
+ .replace(/```[\s\S]*?```/g, ' codeblock ')
209
+ .replace(/`[^`]*`/g, ' code ')
210
+ .replace(/\b\d+(?:\.\d+)?(?:ms|s|%|kb|mb|gb|k|m)?\b/g, '#')
211
+ .replace(/[^a-z0-9#]+/g, ' ')
212
+ .trim()
213
+ .split(/\s+/)
214
+ .slice(0, 80)
215
+ .join(' ') || NO_VALUE;
216
+ }
217
+
218
+ function withDedupMetadata(rec) {
219
+ const existing = normalizedAppliesAlsoTo(rec.appliesAlsoTo);
220
+ const count = Math.max(
221
+ numericCount(rec.corroborationCount),
222
+ 1 + existing.length,
223
+ );
224
+ return existing.length > 0 || count > 1
225
+ ? { ...rec, appliesAlsoTo: existing, corroborationCount: count }
226
+ : { ...rec };
227
+ }
228
+
229
+ function mergeDuplicateRecs(a, b) {
230
+ const aScore = recScore(a);
231
+ const bScore = recScore(b);
232
+ const winner = bScore > aScore ? b : a;
233
+ const loser = winner === a ? b : a;
234
+ const winnerExisting = normalizedAppliesAlsoTo(winner.appliesAlsoTo);
235
+ const loserExisting = normalizedAppliesAlsoTo(loser.appliesAlsoTo);
236
+ const appliesAlsoTo = uniqueAppliesAlsoTo([
237
+ ...winnerExisting,
238
+ appliesAlsoEntry(loser),
239
+ ...loserExisting,
240
+ ]);
241
+ const corroborationCount =
242
+ numericCount(winner.corroborationCount) + numericCount(loser.corroborationCount);
243
+ return {
244
+ ...winner,
245
+ appliesAlsoTo,
246
+ corroborationCount: Math.max(corroborationCount, 1 + appliesAlsoTo.length),
247
+ };
248
+ }
249
+
250
+ function recScore(rec) {
251
+ const priority = typeof rec?.priority === 'number' ? rec.priority : 0;
252
+ const quality = typeof rec?.quality?.overall === 'number' ? rec.quality.overall : 0;
253
+ return (priority * 1_000_000_000_000) + signalMagnitude(rec) + quality;
254
+ }
255
+
256
+ function signalMagnitude(rec) {
257
+ const text = [
258
+ rec?.o11ySignal,
259
+ rec?.why,
260
+ rec?.what,
261
+ rec?.impact,
262
+ ].filter((v) => typeof v === 'string' && v.trim()).join('\n');
263
+ const inv = parseNumber(text, /(?:inv|invocations?|function invocations?|requests?)[:=]\s*([\d,]+)/i);
264
+ const p95 = parseNumber(text, /(?:p95|95th percentile(?: duration)?)[:=]?\s*([\d,]+)\s*ms/i);
265
+ const errors = parseNumber(text, /(?:errs|errors?)[:=]\s*([\d,]+)/i);
266
+ const writes = parseNumber(text, /writes[:=]\s*([\d,]+)/i);
267
+ const reads = parseNumber(text, /reads[:=]\s*([\d,]+)/i);
268
+ if (inv != null && p95 != null) return inv * p95;
269
+ if (errors != null) return errors;
270
+ if (writes != null && reads != null) return writes + reads;
271
+ if (inv != null) return inv;
272
+ return 0;
273
+ }
274
+
275
+ function parseNumber(text, re) {
276
+ const match = re.exec(text);
277
+ if (!match) return null;
278
+ const value = Number(String(match[1]).replace(/,/g, ''));
279
+ return Number.isFinite(value) ? value : null;
280
+ }
281
+
282
+ function numericCount(value) {
283
+ return Number.isFinite(value) && value > 0 ? value : 1;
284
+ }
285
+
286
+ function appliesAlsoEntry(rec) {
287
+ return {
288
+ candidateRef: rec?.candidateRef ?? null,
289
+ affectedFiles: Array.isArray(rec?.affectedFiles)
290
+ ? rec.affectedFiles.map(normalizePath).filter((p) => p !== NO_VALUE)
291
+ : [],
292
+ o11ySignal: rec?.o11ySignal ?? null,
293
+ what: rec?.what ?? null,
294
+ };
295
+ }
296
+
297
+ function normalizedAppliesAlsoTo(entries) {
298
+ if (!Array.isArray(entries)) return [];
299
+ return entries
300
+ .filter((e) => e && typeof e === 'object')
301
+ .map((e) => ({
302
+ candidateRef: e.candidateRef ?? null,
303
+ affectedFiles: Array.isArray(e.affectedFiles)
304
+ ? e.affectedFiles.map(normalizePath).filter((p) => p !== NO_VALUE)
305
+ : [],
306
+ o11ySignal: e.o11ySignal ?? null,
307
+ what: e.what ?? null,
308
+ }));
309
+ }
310
+
311
+ function uniqueAppliesAlsoTo(entries) {
312
+ const seen = new Set();
313
+ const out = [];
314
+ for (const entry of entries) {
315
+ const key = JSON.stringify([
316
+ entry.candidateRef ?? NO_VALUE,
317
+ entry.affectedFiles?.join(',') ?? NO_VALUE,
318
+ entry.what ?? NO_VALUE,
319
+ ]);
320
+ if (seen.has(key)) continue;
321
+ seen.add(key);
322
+ out.push(entry);
323
+ }
324
+ return out;
325
+ }