@1aboveio/skills 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (633) hide show
  1. package/README.md +30 -2
  2. package/bin/cli.mjs +14 -0
  3. package/bin/select-core.mjs +172 -0
  4. package/package.json +146 -4
  5. package/skills/app-dev/airflow/LICENSE +3 -0
  6. package/skills/app-dev/airflow/SKILL.md +199 -0
  7. package/skills/app-dev/airflow/release-check.mjs +93 -0
  8. package/skills/app-dev/android-foundation/LICENSE +3 -0
  9. package/skills/app-dev/android-foundation/SKILL.md +192 -0
  10. package/skills/app-dev/android-foundation/assets/android-ci-job.yml +217 -0
  11. package/skills/app-dev/android-foundation/references/ci.md +87 -0
  12. package/skills/app-dev/android-foundation/references/gotchas.md +221 -0
  13. package/skills/app-dev/android-foundation/references/setup.md +164 -0
  14. package/skills/app-dev/android-foundation/release-check.mjs +93 -0
  15. package/skills/app-dev/android-foundation/scripts/device-run.sh +176 -0
  16. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +172 -0
  17. package/skills/app-dev/app-debug/LICENSE +3 -0
  18. package/skills/app-dev/app-debug/SKILL.md +110 -0
  19. package/skills/app-dev/app-debug/references/common-errors.md +128 -0
  20. package/skills/app-dev/app-debug/release-check.mjs +93 -0
  21. package/skills/app-dev/better-auth/LICENSE +3 -0
  22. package/skills/app-dev/better-auth/SKILL.md +598 -0
  23. package/skills/app-dev/better-auth/references/feishu-api.md +270 -0
  24. package/skills/app-dev/better-auth/release-check.mjs +93 -0
  25. package/skills/app-dev/data-catalog/LICENSE +3 -0
  26. package/skills/app-dev/data-catalog/SKILL.md +204 -0
  27. package/skills/app-dev/data-catalog/agents/openai.yaml +3 -0
  28. package/skills/app-dev/data-catalog/references/data-catalog-conventions.md +75 -0
  29. package/skills/app-dev/data-catalog/references/field-definitions.md +116 -0
  30. package/skills/app-dev/data-catalog/references/format.md +121 -0
  31. package/skills/app-dev/data-catalog/release-check.mjs +93 -0
  32. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +36 -0
  33. package/skills/app-dev/data-catalog/scripts/data_catalog.py +1392 -0
  34. package/skills/app-dev/data-catalog/scripts/persist-conventions-core.mjs +117 -0
  35. package/skills/app-dev/data-catalog/scripts/persist-conventions.mjs +17 -0
  36. package/skills/app-dev/data-catalog/scripts/test_data_catalog.py +571 -0
  37. package/skills/app-dev/ios-foundation/LICENSE +3 -0
  38. package/skills/app-dev/ios-foundation/SKILL.md +184 -0
  39. package/skills/app-dev/ios-foundation/references/setup.md +243 -0
  40. package/skills/app-dev/ios-foundation/release-check.mjs +93 -0
  41. package/skills/app-dev/monorepo/LICENSE +3 -0
  42. package/skills/app-dev/monorepo/SKILL.md +462 -0
  43. package/skills/app-dev/monorepo/release-check.mjs +93 -0
  44. package/skills/app-dev/nextjs-fullstack/LICENSE +3 -0
  45. package/skills/app-dev/nextjs-fullstack/SKILL.md +211 -0
  46. package/skills/app-dev/nextjs-fullstack/conventions.md +318 -0
  47. package/skills/app-dev/nextjs-fullstack/frontend-conventions.md +61 -0
  48. package/skills/app-dev/nextjs-fullstack/nextjs16.md +287 -0
  49. package/skills/app-dev/nextjs-fullstack/release-check.mjs +93 -0
  50. package/skills/app-dev/nextjs-fullstack/server-actions.md +409 -0
  51. package/skills/app-dev/prisma-setup/LICENSE +3 -0
  52. package/skills/app-dev/prisma-setup/SKILL.md +181 -0
  53. package/skills/app-dev/prisma-setup/nextjs.md +258 -0
  54. package/skills/app-dev/prisma-setup/release-check.mjs +93 -0
  55. package/skills/app-dev/prisma-setup/turborepo.md +301 -0
  56. package/skills/app-dev/pyspark/LICENSE +3 -0
  57. package/skills/app-dev/pyspark/SKILL.md +127 -0
  58. package/skills/app-dev/pyspark/assets/templates/etl.py +748 -0
  59. package/skills/app-dev/pyspark/assets/templates/utils/__init__.py +1 -0
  60. package/skills/app-dev/pyspark/assets/templates/utils/hudi_metadata.py +14 -0
  61. package/skills/app-dev/pyspark/references/diagnosis-and-profiling.md +169 -0
  62. package/skills/app-dev/pyspark/references/etl-contract.md +129 -0
  63. package/skills/app-dev/pyspark/references/production-validation.md +131 -0
  64. package/skills/app-dev/pyspark/references/reconciliation.md +38 -0
  65. package/skills/app-dev/pyspark/references/transformation-design.md +190 -0
  66. package/skills/app-dev/pyspark/references/velocity-feature-calculation.md +193 -0
  67. package/skills/app-dev/pyspark/release-check.mjs +93 -0
  68. package/skills/app-dev/pyspark/scripts/spark_eventlog_summary.py +223 -0
  69. package/skills/app-dev/python-backend/LICENSE +3 -0
  70. package/skills/app-dev/python-backend/SKILL.md +325 -0
  71. package/skills/app-dev/python-backend/assets/uv-install.sh +48 -0
  72. package/skills/app-dev/python-backend/references/uv-installs.md +83 -0
  73. package/skills/app-dev/python-backend/release-check.mjs +93 -0
  74. package/skills/app-dev/shadcn/LICENSE +3 -0
  75. package/skills/app-dev/shadcn/SKILL.md +120 -0
  76. package/skills/app-dev/shadcn/assets/shadcn-small.png +0 -0
  77. package/skills/app-dev/shadcn/assets/shadcn.png +0 -0
  78. package/skills/app-dev/shadcn/cli.md +411 -0
  79. package/skills/app-dev/shadcn/customization.md +224 -0
  80. package/skills/app-dev/shadcn/mcp.md +101 -0
  81. package/skills/app-dev/shadcn/release-check.mjs +93 -0
  82. package/skills/app-dev/shadcn/rules/base-vs-radix.md +323 -0
  83. package/skills/app-dev/shadcn/rules/component-selection.md +67 -0
  84. package/skills/app-dev/shadcn/rules/composition.md +195 -0
  85. package/skills/app-dev/shadcn/rules/data-table.md +201 -0
  86. package/skills/app-dev/shadcn/rules/forms.md +255 -0
  87. package/skills/app-dev/shadcn/rules/icons.md +103 -0
  88. package/skills/app-dev/shadcn/rules/styling.md +167 -0
  89. package/skills/app-dev/zod-v4/LICENSE +3 -0
  90. package/skills/app-dev/zod-v4/SKILL.md +286 -0
  91. package/skills/app-dev/zod-v4/release-check.mjs +93 -0
  92. package/skills/cicd/cloud-build/LICENSE +3 -0
  93. package/skills/cicd/cloud-build/SKILL.md +707 -0
  94. package/skills/cicd/cloud-build/release-check.mjs +93 -0
  95. package/skills/cicd/cloud-debug/LICENSE +3 -0
  96. package/skills/cicd/cloud-debug/SKILL.md +315 -0
  97. package/skills/cicd/cloud-debug/references/build-failures.md +238 -0
  98. package/skills/cicd/cloud-debug/references/deploy-failures.md +376 -0
  99. package/skills/cicd/cloud-debug/references/pipeline-failures.md +378 -0
  100. package/skills/cicd/cloud-debug/release-check.mjs +93 -0
  101. package/skills/cicd/cloud-deploy/LICENSE +3 -0
  102. package/skills/cicd/cloud-deploy/SKILL.md +230 -0
  103. package/skills/cicd/cloud-deploy/references/config-templates.md +257 -0
  104. package/skills/cicd/cloud-deploy/release-check.mjs +93 -0
  105. package/skills/cicd/docker/LICENSE +3 -0
  106. package/skills/cicd/docker/SKILL.md +151 -0
  107. package/skills/cicd/docker/release-check.mjs +93 -0
  108. package/skills/cicd/github-actions/LICENSE +3 -0
  109. package/skills/cicd/github-actions/SKILL.md +111 -0
  110. package/skills/cicd/github-actions/references/audit-profile-optimize.md +150 -0
  111. package/skills/cicd/github-actions/references/ci-case-studies.md +141 -0
  112. package/skills/cicd/github-actions/references/conditional-ci.md +214 -0
  113. package/skills/cicd/github-actions/references/debugging.md +13 -0
  114. package/skills/cicd/github-actions/references/pipeline.md +87 -0
  115. package/skills/cicd/github-actions/references/post-deploy-and-publishing.md +33 -0
  116. package/skills/cicd/github-actions/references/runners.md +253 -0
  117. package/skills/cicd/github-actions/references/security.md +29 -0
  118. package/skills/cicd/github-actions/references/speed-and-audit.md +115 -0
  119. package/skills/cicd/github-actions/references/two-pass-reading.md +92 -0
  120. package/skills/cicd/github-actions/release-check.mjs +93 -0
  121. package/skills/cicd/github-actions/scripts/ci-run-audit-core.mjs +612 -0
  122. package/skills/cicd/github-actions/scripts/ci-run-audit.mjs +4 -0
  123. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +637 -0
  124. package/skills/cicd/github-flow/LICENSE +3 -0
  125. package/skills/cicd/github-flow/SKILL.md +123 -0
  126. package/skills/cicd/github-flow/references/ci-conventions.md +14 -0
  127. package/skills/cicd/github-flow/release-check.mjs +93 -0
  128. package/skills/cicd/github-flow/scripts/flow-core.mjs +105 -0
  129. package/skills/cicd/github-flow/scripts/flow.mjs +81 -0
  130. package/skills/cicd/github-flow/scripts/flow.test.mjs +114 -0
  131. package/skills/cicd/github-flow/scripts/persist-conventions-core.mjs +117 -0
  132. package/skills/cicd/github-flow/scripts/persist-conventions.mjs +49 -0
  133. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +108 -0
  134. package/skills/cicd/google-cloud/LICENSE +3 -0
  135. package/skills/cicd/google-cloud/SKILL.md +119 -0
  136. package/skills/cicd/google-cloud/references/gcs.md +469 -0
  137. package/skills/cicd/google-cloud/references/iam.md +451 -0
  138. package/skills/cicd/google-cloud/references/project.md +349 -0
  139. package/skills/cicd/google-cloud/references/secrets.md +336 -0
  140. package/skills/cicd/google-cloud/references/vpc.md +312 -0
  141. package/skills/cicd/google-cloud/release-check.mjs +93 -0
  142. package/skills/cicd/google-cloud/scripts/create-sa.sh +36 -0
  143. package/skills/cicd/google-cloud/scripts/gcp-config.sh +31 -0
  144. package/skills/cicd/google-cloud/scripts/grant-iap.sh +41 -0
  145. package/skills/cicd/google-cloud/scripts/setup-secrets.sh +48 -0
  146. package/skills/cicd/mergify/LICENSE +3 -0
  147. package/skills/cicd/mergify/SKILL.md +102 -0
  148. package/skills/cicd/mergify/assets/templates/mergify.yml +215 -0
  149. package/skills/cicd/mergify/assets/templates/ruleset.json +46 -0
  150. package/skills/cicd/mergify/references/branch-protection.md +282 -0
  151. package/skills/cicd/mergify/references/configuration.md +274 -0
  152. package/skills/cicd/mergify/references/diagnosis.md +109 -0
  153. package/skills/cicd/mergify/references/stacks.md +84 -0
  154. package/skills/cicd/mergify/references/traps.md +119 -0
  155. package/skills/cicd/mergify/references/watch-contract.md +274 -0
  156. package/skills/cicd/mergify/release-check.mjs +93 -0
  157. package/skills/cicd/mergify/scripts/audit-core.mjs +208 -0
  158. package/skills/cicd/mergify/scripts/audit.mjs +4 -0
  159. package/skills/cicd/mergify/scripts/audit.test.mjs +201 -0
  160. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +106 -0
  161. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +30 -0
  162. package/skills/cicd/mergify/scripts/stack-push-core.mjs +258 -0
  163. package/skills/cicd/mergify/scripts/stack-push.mjs +4 -0
  164. package/skills/cicd/mergify/scripts/stack-push.test.mjs +233 -0
  165. package/skills/cicd/mergify/scripts/watch-pr-delivery-core.mjs +1065 -0
  166. package/skills/cicd/mergify/scripts/watch-pr-delivery.mjs +4 -0
  167. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +1826 -0
  168. package/skills/cicd/podman/LICENSE +3 -0
  169. package/skills/cicd/podman/SKILL.md +71 -0
  170. package/skills/cicd/podman/agents/openai.yaml +4 -0
  171. package/skills/cicd/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
  172. package/skills/cicd/podman/assets/templates/podman-service-override.conf +3 -0
  173. package/skills/cicd/podman/references/compose-compatibility.md +70 -0
  174. package/skills/cicd/podman/references/networking-and-ports.md +74 -0
  175. package/skills/cicd/podman/references/rootless-services-and-sockets.md +156 -0
  176. package/skills/cicd/podman/references/troubleshooting.md +98 -0
  177. package/skills/cicd/podman/release-check.mjs +93 -0
  178. package/skills/content/article-review/LICENSE +3 -0
  179. package/skills/content/article-review/SKILL.md +66 -0
  180. package/skills/content/article-review/references/report-format.md +68 -0
  181. package/skills/content/article-review/references/template-guide.md +79 -0
  182. package/skills/content/article-review/references/template-wechat-payments.md +125 -0
  183. package/skills/content/article-review/release-check.mjs +93 -0
  184. package/skills/content/article-to-image/LICENSE +3 -0
  185. package/skills/content/article-to-image/SKILL.md +91 -0
  186. package/skills/content/article-to-image/references/formats.md +53 -0
  187. package/skills/content/article-to-image/release-check.mjs +93 -0
  188. package/skills/content/flowchart-svg/LICENSE +3 -0
  189. package/skills/content/flowchart-svg/SKILL.md +43 -0
  190. package/skills/content/flowchart-svg/release-check.mjs +93 -0
  191. package/skills/content/flowchart-svg/scripts/export_png.sh +8 -0
  192. package/skills/content/image-gen/LICENSE +3 -0
  193. package/skills/content/image-gen/SKILL.md +192 -0
  194. package/skills/content/image-gen/release-check.mjs +93 -0
  195. package/skills/content/image-gen/scripts/credentials.mjs +217 -0
  196. package/skills/content/image-gen/scripts/credentials.test.mjs +212 -0
  197. package/skills/content/image-gen/scripts/generate.mjs +240 -0
  198. package/skills/content/image-gen/scripts/options.mjs +293 -0
  199. package/skills/content/image-gen/scripts/options.test.mjs +245 -0
  200. package/skills/content/mermaid/LICENSE +3 -0
  201. package/skills/content/mermaid/SKILL.md +635 -0
  202. package/skills/content/mermaid/release-check.mjs +93 -0
  203. package/skills/content/wechat-article-writer/LICENSE +3 -0
  204. package/skills/content/wechat-article-writer/SKILL.md +92 -0
  205. package/skills/content/wechat-article-writer/references/style-selector.md +133 -0
  206. package/skills/content/wechat-article-writer/references/writing-rules.md +152 -0
  207. package/skills/content/wechat-article-writer/release-check.mjs +93 -0
  208. package/skills/content/wechat-article-writer/templates/wechat-comparison.md +52 -0
  209. package/skills/content/wechat-article-writer/templates/wechat-howto.md +51 -0
  210. package/skills/content/wechat-article-writer/templates/wechat-insight.md +50 -0
  211. package/skills/content/wechat-article-writer/templates/wechat-pitfall.md +52 -0
  212. package/skills/content/wechat-publisher/LICENSE +3 -0
  213. package/skills/content/wechat-publisher/SKILL.md +171 -0
  214. package/skills/content/wechat-publisher/references/api.md +105 -0
  215. package/skills/content/wechat-publisher/references/layout-rules.md +57 -0
  216. package/skills/content/wechat-publisher/references/wechat-safe-template.html +86 -0
  217. package/skills/content/wechat-publisher/release-check.mjs +93 -0
  218. package/skills/content/wechat-publisher/scripts/md_to_wechat_html.py +194 -0
  219. package/skills/content/wechat-publisher/scripts/publish.py +239 -0
  220. package/skills/content/wechat-search/LICENSE +3 -0
  221. package/skills/content/wechat-search/SKILL.md +211 -0
  222. package/skills/content/wechat-search/release-check.mjs +93 -0
  223. package/skills/content/wechat-search/scripts/start-chrome.sh +83 -0
  224. package/skills/content/xai-search/LICENSE +3 -0
  225. package/skills/content/xai-search/SKILL.md +79 -0
  226. package/skills/content/xai-search/release-check.mjs +93 -0
  227. package/skills/content/xai-search/scripts/xai_search.sh +187 -0
  228. package/skills/content/xai-search/tests/smoke_test.sh +36 -0
  229. package/skills/core/distill-writing/LICENSE +3 -0
  230. package/skills/core/distill-writing/SKILL.md +74 -0
  231. package/skills/core/distill-writing/references/extraction.md +95 -0
  232. package/skills/core/distill-writing/references/updating.md +46 -0
  233. package/skills/core/distill-writing/release-check.mjs +93 -0
  234. package/skills/core/doc-to-md/LICENSE +3 -0
  235. package/skills/core/doc-to-md/SKILL.md +78 -0
  236. package/skills/core/doc-to-md/release-check.mjs +93 -0
  237. package/skills/core/doc-to-md/scripts/convert.py +157 -0
  238. package/skills/core/editorial-review/LICENSE +3 -0
  239. package/skills/core/editorial-review/SKILL.md +79 -0
  240. package/skills/core/editorial-review/references/checklist.md +94 -0
  241. package/skills/core/editorial-review/release-check.mjs +93 -0
  242. package/skills/core/house-style/LICENSE +3 -0
  243. package/skills/core/house-style/SKILL.md +33 -0
  244. package/skills/core/house-style/references/defaults.md +66 -0
  245. package/skills/core/house-style/references/rules.md +267 -0
  246. package/skills/core/house-style/references/sources.md +27 -0
  247. package/skills/core/house-style/references/style-en.md +131 -0
  248. package/skills/core/house-style/references/style-zh.md +141 -0
  249. package/skills/core/house-style/references/terminology-zh.md +223 -0
  250. package/skills/core/house-style/release-check.mjs +93 -0
  251. package/skills/core/house-style/templates/brd.md +103 -0
  252. package/skills/core/house-style/templates/commercial-proposal.md +93 -0
  253. package/skills/core/house-style/templates/compliance-policy.md +109 -0
  254. package/skills/core/house-style/templates/gap-analysis.md +105 -0
  255. package/skills/core/house-style/templates/general.md +63 -0
  256. package/skills/core/house-style/templates/integration-guide.md +98 -0
  257. package/skills/core/house-style/templates/prd.md +81 -0
  258. package/skills/core/house-style/templates/risk-operations-procedure.md +94 -0
  259. package/skills/core/house-style/templates/technical-spec.md +96 -0
  260. package/skills/core/polish-cn/LICENSE +3 -0
  261. package/skills/core/polish-cn/SKILL.md +102 -0
  262. package/skills/core/polish-cn/references/brief.md +93 -0
  263. package/skills/core/polish-cn/references/models.md +76 -0
  264. package/skills/core/polish-cn/release-check.mjs +93 -0
  265. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +244 -0
  266. package/skills/core/polish-cn/scripts/polish_blocks.py +489 -0
  267. package/skills/core/reg-parser/LICENSE +3 -0
  268. package/skills/core/reg-parser/SKILL.md +212 -0
  269. package/skills/core/reg-parser/references/checklist-schema.md +223 -0
  270. package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.json +1306 -0
  271. package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.md +469 -0
  272. package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.json +634 -0
  273. package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.md +233 -0
  274. package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.json +2194 -0
  275. package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.md +771 -0
  276. package/skills/core/reg-parser/references/checklists/kyc-aml-checklist.md +258 -0
  277. package/skills/core/reg-parser/references/checklists/visa-vars-checklist.md +1058 -0
  278. package/skills/core/reg-parser/references/mandates/hk-amlo-cap615.md +389 -0
  279. package/skills/core/reg-parser/references/mandates/visa-vars.md +205 -0
  280. package/skills/core/reg-parser/release-check.mjs +93 -0
  281. package/skills/core/reg-parser/scripts/compare_checklist.py +230 -0
  282. package/skills/core/reg-parser/scripts/dump_checklist.py +361 -0
  283. package/skills/core/reg-parser/scripts/preprocess-mandate.sh +187 -0
  284. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +165 -0
  285. package/skills/core/reg-parser/scripts/test_quarantine.sh +105 -0
  286. package/skills/core/reg-parser/scripts/validate_checklist.py +563 -0
  287. package/skills/core/reg-parser/scripts/validate_mandate.py +273 -0
  288. package/skills/core/terminology/LICENSE +3 -0
  289. package/skills/core/terminology/SKILL.md +62 -0
  290. package/skills/core/terminology/references/format.md +50 -0
  291. package/skills/core/terminology/references/init.md +32 -0
  292. package/skills/core/terminology/release-check.mjs +93 -0
  293. package/skills/core/write-doc/LICENSE +3 -0
  294. package/skills/core/write-doc/SKILL.md +141 -0
  295. package/skills/core/write-doc/references/bilingual.md +60 -0
  296. package/skills/core/write-doc/release-check.mjs +93 -0
  297. package/skills/engineering/better-review/LICENSE +3 -0
  298. package/skills/engineering/better-review/SKILL.md +249 -0
  299. package/skills/engineering/better-review/agents/openai.yaml +9 -0
  300. package/skills/engineering/better-review/references/domain-hazards.md +53 -0
  301. package/skills/engineering/better-review/references/passes.md +147 -0
  302. package/skills/engineering/better-review/references/result.md +151 -0
  303. package/skills/engineering/better-review/release-check.mjs +93 -0
  304. package/skills/engineering/better-review/scripts/pr-context.mjs +212 -0
  305. package/skills/engineering/better-review/scripts/pr-context.test.mjs +168 -0
  306. package/skills/engineering/better-review/scripts/rereview-scope.mjs +503 -0
  307. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +587 -0
  308. package/skills/engineering/better-review/scripts/scan-diff.mjs +365 -0
  309. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +414 -0
  310. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +96 -0
  311. package/skills/engineering/e2e-test/LICENSE +3 -0
  312. package/skills/engineering/e2e-test/SKILL.md +98 -0
  313. package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +24 -0
  314. package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
  315. package/skills/engineering/e2e-test/references/authoring/playwright-config.md +73 -0
  316. package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +144 -0
  317. package/skills/engineering/e2e-test/references/ci-integration.md +25 -0
  318. package/skills/engineering/e2e-test/references/ci-playwright-container.md +290 -0
  319. package/skills/engineering/e2e-test/references/presentation-sweep.md +104 -0
  320. package/skills/engineering/e2e-test/references/reviewing.md +41 -0
  321. package/skills/engineering/e2e-test/references/route-manifest.md +51 -0
  322. package/skills/engineering/e2e-test/release-check.mjs +93 -0
  323. package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
  324. package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
  325. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +106 -0
  326. package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
  327. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +117 -0
  328. package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
  329. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +69 -0
  330. package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
  331. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +98 -0
  332. package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +147 -0
  333. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +71 -0
  334. package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
  335. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +59 -0
  336. package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +82 -0
  337. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +76 -0
  338. package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
  339. package/skills/engineering/engineering-runtime/LICENSE +3 -0
  340. package/skills/engineering/engineering-runtime/SKILL.md +11 -0
  341. package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
  342. package/skills/engineering/engineering-runtime/coherence/workflow.json +597 -0
  343. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +68 -0
  344. package/skills/engineering/engineering-runtime/release-check.mjs +93 -0
  345. package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
  346. package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
  347. package/skills/engineering/engineering-runtime/scripts/invocation-policy.mjs +187 -0
  348. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +95 -0
  349. package/skills/engineering/engineering-runtime/scripts/main-module.mjs +79 -0
  350. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +288 -0
  351. package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
  352. package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
  353. package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
  354. package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
  355. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +96 -0
  356. package/skills/engineering/engineering-runtime/scripts/review-completion.mjs +56 -0
  357. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +595 -0
  358. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +189 -0
  359. package/skills/engineering/engineering-runtime/scripts/yaml-mapping.mjs +18 -0
  360. package/skills/engineering/ensure-coverage/LICENSE +3 -0
  361. package/skills/engineering/ensure-coverage/SKILL.md +129 -0
  362. package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +92 -0
  363. package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +85 -0
  364. package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
  365. package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
  366. package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
  367. package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +19 -0
  368. package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
  369. package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +57 -0
  370. package/skills/engineering/ensure-coverage/references/depth/test-smells.md +39 -0
  371. package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +44 -0
  372. package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +110 -0
  373. package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +35 -0
  374. package/skills/engineering/ensure-coverage/references/presentation/axis.md +64 -0
  375. package/skills/engineering/ensure-coverage/references/process/audit-mode.md +27 -0
  376. package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
  377. package/skills/engineering/ensure-coverage/references/process/test-strategy.md +56 -0
  378. package/skills/engineering/ensure-coverage/release-check.mjs +93 -0
  379. package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +67 -0
  380. package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +140 -0
  381. package/skills/engineering/ensure-coverage/scripts/README.md +99 -0
  382. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +182 -0
  383. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +96 -0
  384. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +105 -0
  385. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +47 -0
  386. package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +137 -0
  387. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +86 -0
  388. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +674 -0
  389. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +447 -0
  390. package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
  391. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +552 -0
  392. package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +282 -0
  393. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +289 -0
  394. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
  395. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +119 -0
  396. package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
  397. package/skills/engineering/golden-path/LICENSE +3 -0
  398. package/skills/engineering/golden-path/SKILL.md +230 -0
  399. package/skills/engineering/golden-path/agents/openai.yaml +3 -0
  400. package/skills/engineering/golden-path/assets/golden-paths.example.json +57 -0
  401. package/skills/engineering/golden-path/references/contract.md +239 -0
  402. package/skills/engineering/golden-path/references/migration.md +111 -0
  403. package/skills/engineering/golden-path/release-check.mjs +93 -0
  404. package/skills/engineering/golden-path/scripts/golden-path-core.mjs +1779 -0
  405. package/skills/engineering/golden-path/scripts/golden-path.mjs +6 -0
  406. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +1457 -0
  407. package/skills/engineering/harness-runtime/LICENSE +3 -0
  408. package/skills/engineering/harness-runtime/SKILL.md +19 -0
  409. package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
  410. package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
  411. package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
  412. package/skills/engineering/harness-runtime/contracts.md +15 -0
  413. package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
  414. package/skills/engineering/harness-runtime/discover-models.test.mjs +363 -0
  415. package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
  416. package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
  417. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +967 -0
  418. package/skills/engineering/harness-runtime/index.mjs +767 -0
  419. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +829 -0
  420. package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
  421. package/skills/engineering/harness-runtime/model-catalog.test.mjs +366 -0
  422. package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
  423. package/skills/engineering/harness-runtime/references/model-catalog-seed.json +408 -0
  424. package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
  425. package/skills/engineering/harness-runtime/release-check.mjs +93 -0
  426. package/skills/engineering/resolve-issues/LICENSE +3 -0
  427. package/skills/engineering/resolve-issues/SKILL.md +250 -0
  428. package/skills/engineering/resolve-issues/agents/openai.yaml +9 -0
  429. package/skills/engineering/resolve-issues/plugin-hooks.json +17 -0
  430. package/skills/engineering/resolve-issues/references/canary.md +28 -0
  431. package/skills/engineering/resolve-issues/references/ci.md +105 -0
  432. package/skills/engineering/resolve-issues/references/done.md +25 -0
  433. package/skills/engineering/resolve-issues/references/dropbox.md +90 -0
  434. package/skills/engineering/resolve-issues/references/expire.md +318 -0
  435. package/skills/engineering/resolve-issues/references/exploration.md +202 -0
  436. package/skills/engineering/resolve-issues/references/implementation.md +303 -0
  437. package/skills/engineering/resolve-issues/references/preflight.md +445 -0
  438. package/skills/engineering/resolve-issues/references/review.md +228 -0
  439. package/skills/engineering/resolve-issues/references/shared-modules.md +44 -0
  440. package/skills/engineering/resolve-issues/references/task-plan.md +264 -0
  441. package/skills/engineering/resolve-issues/release-check.mjs +93 -0
  442. package/skills/engineering/resolve-issues/scripts/cleanup-runs.mjs +364 -0
  443. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +280 -0
  444. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
  445. package/skills/engineering/resolve-issues/scripts/dropbox.mjs +473 -0
  446. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +346 -0
  447. package/skills/engineering/resolve-issues/scripts/findings-reuse.mjs +98 -0
  448. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +88 -0
  449. package/skills/engineering/resolve-issues/scripts/github-reviews.mjs +384 -0
  450. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +264 -0
  451. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +135 -0
  452. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +79 -0
  453. package/skills/engineering/resolve-issues/scripts/issue-tree.mjs +162 -0
  454. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +107 -0
  455. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +14 -0
  456. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
  457. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +149 -0
  458. package/skills/engineering/resolve-issues/scripts/next-actions.mjs +856 -0
  459. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +915 -0
  460. package/skills/engineering/resolve-issues/scripts/plan-sync.mjs +269 -0
  461. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +156 -0
  462. package/skills/engineering/resolve-issues/scripts/preflight-models.mjs +736 -0
  463. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +448 -0
  464. package/skills/engineering/resolve-issues/scripts/ready-check.mjs +185 -0
  465. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +217 -0
  466. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +98 -0
  467. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +91 -0
  468. package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +509 -0
  469. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +1060 -0
  470. package/skills/engineering/resolve-issues/scripts/size-check.mjs +492 -0
  471. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +458 -0
  472. package/skills/engineering/resolve-release/LICENSE +3 -0
  473. package/skills/engineering/resolve-release/SKILL.md +215 -0
  474. package/skills/engineering/resolve-release/agents/openai.yaml +8 -0
  475. package/skills/engineering/resolve-release/flows/cloud-run-canary.md +13 -0
  476. package/skills/engineering/resolve-release/flows/cloud-run.md +7 -0
  477. package/skills/engineering/resolve-release/flows/root-tag-npm.md +13 -0
  478. package/skills/engineering/resolve-release/flows/workspace-npm.md +22 -0
  479. package/skills/engineering/resolve-release/references/canary.md +51 -0
  480. package/skills/engineering/resolve-release/references/candidate.md +41 -0
  481. package/skills/engineering/resolve-release/references/cut.md +131 -0
  482. package/skills/engineering/resolve-release/references/dark-launch.md +66 -0
  483. package/skills/engineering/resolve-release/references/flows.md +150 -0
  484. package/skills/engineering/resolve-release/references/preflight.md +154 -0
  485. package/skills/engineering/resolve-release/references/prepared-metadata.md +260 -0
  486. package/skills/engineering/resolve-release/references/promotion.md +82 -0
  487. package/skills/engineering/resolve-release/references/publish.md +232 -0
  488. package/skills/engineering/resolve-release/references/workspace-npm.md +103 -0
  489. package/skills/engineering/resolve-release/release-check.mjs +93 -0
  490. package/skills/engineering/resolve-release/scripts/apply.mjs +212 -0
  491. package/skills/engineering/resolve-release/scripts/apply.test.mjs +99 -0
  492. package/skills/engineering/resolve-release/scripts/dark-flags.mjs +134 -0
  493. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +100 -0
  494. package/skills/engineering/resolve-release/scripts/flow.mjs +500 -0
  495. package/skills/engineering/resolve-release/scripts/flow.test.mjs +340 -0
  496. package/skills/engineering/resolve-release/scripts/green-head.mjs +231 -0
  497. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +101 -0
  498. package/skills/engineering/resolve-release/scripts/npm-version-absence.mjs +138 -0
  499. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +426 -0
  500. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +659 -0
  501. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +317 -0
  502. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +291 -0
  503. package/skills/engineering/resolve-release/scripts/plan.mjs +806 -0
  504. package/skills/engineering/resolve-release/scripts/plan.test.mjs +428 -0
  505. package/skills/engineering/rush-issues/LICENSE +3 -0
  506. package/skills/engineering/rush-issues/SKILL.md +58 -0
  507. package/skills/engineering/rush-issues/agents/openai.yaml +8 -0
  508. package/skills/engineering/rush-issues/rehydrate.mjs +10 -0
  509. package/skills/engineering/rush-issues/release-check.mjs +93 -0
  510. package/skills/engineering/rush-release/LICENSE +3 -0
  511. package/skills/engineering/rush-release/SKILL.md +35 -0
  512. package/skills/engineering/rush-release/agents/openai.yaml +8 -0
  513. package/skills/engineering/rush-release/release-check.mjs +93 -0
  514. package/skills/engineering/test-quality/LICENSE +3 -0
  515. package/skills/engineering/test-quality/SKILL.md +79 -0
  516. package/skills/engineering/test-quality/references/audit.md +80 -0
  517. package/skills/engineering/test-quality/references/lint.md +24 -0
  518. package/skills/engineering/test-quality/references/principles.md +143 -0
  519. package/skills/engineering/test-quality/references/testing-conventions.md +9 -0
  520. package/skills/engineering/test-quality/release-check.mjs +93 -0
  521. package/skills/engineering/test-quality/scripts/lint-tests.mjs +574 -0
  522. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +591 -0
  523. package/skills/engineering/test-quality/scripts/mock-policy-config.mjs +176 -0
  524. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +119 -0
  525. package/skills/engineering/test-quality/scripts/persist-conventions-core.mjs +117 -0
  526. package/skills/engineering/test-quality/scripts/persist-conventions.mjs +17 -0
  527. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +104 -0
  528. package/skills/risk-compliance/compliance-audit/LICENSE +3 -0
  529. package/skills/risk-compliance/compliance-audit/SKILL.md +439 -0
  530. package/skills/risk-compliance/compliance-audit/references/findings-schema.md +293 -0
  531. package/skills/risk-compliance/compliance-audit/references/normalization-rules.md +45 -0
  532. package/skills/risk-compliance/compliance-audit/references/vars-checklist.md +1068 -0
  533. package/skills/risk-compliance/compliance-audit/references/vars-findings-schema.md +398 -0
  534. package/skills/risk-compliance/compliance-audit/release-check.mjs +93 -0
  535. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +14 -0
  536. package/skills/risk-compliance/compliance-audit/scripts/export_tracker_xlsx.py +221 -0
  537. package/skills/risk-compliance/compliance-audit/scripts/reconcile_tracker.py +458 -0
  538. package/skills/risk-compliance/compliance-audit/scripts/test_export_tracker_xlsx.py +97 -0
  539. package/skills/risk-compliance/compliance-audit/scripts/test_validate_tracker.py +157 -0
  540. package/skills/risk-compliance/compliance-audit/scripts/validate_tracker.py +829 -0
  541. package/skills/risk-compliance/contract-compare/LICENSE +3 -0
  542. package/skills/risk-compliance/contract-compare/SKILL.md +199 -0
  543. package/skills/risk-compliance/contract-compare/references/comparison-dimensions.md +209 -0
  544. package/skills/risk-compliance/contract-compare/release-check.mjs +93 -0
  545. package/skills/risk-compliance/contract-review/LICENSE +3 -0
  546. package/skills/risk-compliance/contract-review/SKILL.md +240 -0
  547. package/skills/risk-compliance/contract-review/references/review-criteria.md +223 -0
  548. package/skills/risk-compliance/contract-review/release-check.mjs +93 -0
  549. package/skills/risk-compliance/doc-authenticity/LICENSE +3 -0
  550. package/skills/risk-compliance/doc-authenticity/SKILL.md +160 -0
  551. package/skills/risk-compliance/doc-authenticity/agents/openai.yaml +8 -0
  552. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +12 -0
  553. package/skills/risk-compliance/doc-authenticity/references/agreement.md +66 -0
  554. package/skills/risk-compliance/doc-authenticity/references/bank-statement.md +150 -0
  555. package/skills/risk-compliance/doc-authenticity/references/forensic-signals.md +157 -0
  556. package/skills/risk-compliance/doc-authenticity/references/image-signals.md +114 -0
  557. package/skills/risk-compliance/doc-authenticity/references/invoice.md +66 -0
  558. package/skills/risk-compliance/doc-authenticity/references/issuer-profiles.md +86 -0
  559. package/skills/risk-compliance/doc-authenticity/references/verdict-and-report.md +95 -0
  560. package/skills/risk-compliance/doc-authenticity/references/verdict.schema.json +81 -0
  561. package/skills/risk-compliance/doc-authenticity/release-check.mjs +93 -0
  562. package/skills/risk-compliance/doc-authenticity/scripts/image_forensics.py +294 -0
  563. package/skills/risk-compliance/doc-authenticity/scripts/pdf_forensics.py +401 -0
  564. package/skills/risk-compliance/doc-authenticity/scripts/validate_codes.py +224 -0
  565. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.py +308 -0
  566. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +212 -0
  567. package/skills/risk-compliance/fraud-analysis/LICENSE +3 -0
  568. package/skills/risk-compliance/fraud-analysis/SKILL.md +114 -0
  569. package/skills/risk-compliance/fraud-analysis/references/archetypes/authorized-payment-scam.md +41 -0
  570. package/skills/risk-compliance/fraud-analysis/references/archetypes/first-party-fraud.md +44 -0
  571. package/skills/risk-compliance/fraud-analysis/references/archetypes/third-party-fraud.md +27 -0
  572. package/skills/risk-compliance/fraud-analysis/references/contexts/bank-transfer.md +24 -0
  573. package/skills/risk-compliance/fraud-analysis/references/contexts/card-payment.md +30 -0
  574. package/skills/risk-compliance/fraud-analysis/references/contexts/payment-collection.md +20 -0
  575. package/skills/risk-compliance/fraud-analysis/references/contexts/payout.md +20 -0
  576. package/skills/risk-compliance/fraud-analysis/references/feature-engineering.md +158 -0
  577. package/skills/risk-compliance/fraud-analysis/references/mechanisms/account-takeover.md +36 -0
  578. package/skills/risk-compliance/fraud-analysis/references/report-rationale.md +45 -0
  579. package/skills/risk-compliance/fraud-analysis/references/report-template.md +190 -0
  580. package/skills/risk-compliance/fraud-analysis/references/review-checklist.md +175 -0
  581. package/skills/risk-compliance/fraud-analysis/references/taxonomy.md +79 -0
  582. package/skills/risk-compliance/fraud-analysis/references/terminology.md +108 -0
  583. package/skills/risk-compliance/fraud-analysis/references/workflow.md +175 -0
  584. package/skills/risk-compliance/fraud-analysis/release-check.mjs +93 -0
  585. package/skills/risk-compliance/payment-analysis/LICENSE +3 -0
  586. package/skills/risk-compliance/payment-analysis/SKILL.md +180 -0
  587. package/skills/risk-compliance/payment-analysis/references/auth-rate-actions.md +30 -0
  588. package/skills/risk-compliance/payment-analysis/references/chargebacks.md +208 -0
  589. package/skills/risk-compliance/payment-analysis/references/chinese-polish.md +51 -0
  590. package/skills/risk-compliance/payment-analysis/references/event-layers.md +127 -0
  591. package/skills/risk-compliance/payment-analysis/references/fx.md +59 -0
  592. package/skills/risk-compliance/payment-analysis/references/journey.md +79 -0
  593. package/skills/risk-compliance/payment-analysis/references/metrics.md +67 -0
  594. package/skills/risk-compliance/payment-analysis/references/report-template.md +119 -0
  595. package/skills/risk-compliance/payment-analysis/references/terminology.md +95 -0
  596. package/skills/risk-compliance/payment-analysis/references/visualization.md +56 -0
  597. package/skills/risk-compliance/payment-analysis/release-check.mjs +93 -0
  598. package/skills/risk-compliance/report-writing/LICENSE +3 -0
  599. package/skills/risk-compliance/report-writing/SKILL.md +220 -0
  600. package/skills/risk-compliance/report-writing/references/audit-report-template.md +121 -0
  601. package/skills/risk-compliance/report-writing/references/external-assessment-template.md +167 -0
  602. package/skills/risk-compliance/report-writing/references/management-report-template.md +145 -0
  603. package/skills/risk-compliance/report-writing/references/vars-report-template.md +224 -0
  604. package/skills/risk-compliance/report-writing/release-check.mjs +93 -0
  605. package/skills/risk-compliance/similar-domain/LICENSE +3 -0
  606. package/skills/risk-compliance/similar-domain/SKILL.md +203 -0
  607. package/skills/risk-compliance/similar-domain/references/entities.md +67 -0
  608. package/skills/risk-compliance/similar-domain/references/fingerprints.md +134 -0
  609. package/skills/risk-compliance/similar-domain/references/pivots.md +82 -0
  610. package/skills/risk-compliance/similar-domain/references/report-template.md +93 -0
  611. package/skills/risk-compliance/similar-domain/references/scoring.md +130 -0
  612. package/skills/risk-compliance/similar-domain/references/terminology.md +41 -0
  613. package/skills/risk-compliance/similar-domain/references/workflow.md +85 -0
  614. package/skills/risk-compliance/similar-domain/release-check.mjs +93 -0
  615. package/skills/risk-compliance/tld-detection/LICENSE +3 -0
  616. package/skills/risk-compliance/tld-detection/SKILL.md +245 -0
  617. package/skills/risk-compliance/tld-detection/agents/openai.yaml +9 -0
  618. package/skills/risk-compliance/tld-detection/references/chinese-polish.md +55 -0
  619. package/skills/risk-compliance/tld-detection/references/clean.md +51 -0
  620. package/skills/risk-compliance/tld-detection/references/clustering.md +235 -0
  621. package/skills/risk-compliance/tld-detection/references/decision.md +189 -0
  622. package/skills/risk-compliance/tld-detection/references/fx-conversion.md +232 -0
  623. package/skills/risk-compliance/tld-detection/references/report-template.md +272 -0
  624. package/skills/risk-compliance/tld-detection/references/streams.md +141 -0
  625. package/skills/risk-compliance/tld-detection/references/terminology.md +95 -0
  626. package/skills/risk-compliance/tld-detection/references/workflow.md +95 -0
  627. package/skills/risk-compliance/tld-detection/release-check.mjs +93 -0
  628. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.py +390 -0
  629. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +14 -0
  630. package/skills/risk-compliance/tld-detection/scripts/detect_streams.py +650 -0
  631. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +14 -0
  632. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +134 -0
  633. package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +520 -0
@@ -0,0 +1,574 @@
1
+ // lint-tests — static checks over test files for the test-quality principles
2
+ // (../SKILL.md). Copied from ensure-coverage's lint-tests and extended; the two
3
+ // copies evolve independently.
4
+ //
5
+ // node lint-tests.mjs # whole-tree scan of discovered test files
6
+ // node lint-tests.mjs --diff <base> # only findings on lines ADDED in base...HEAD
7
+ // node lint-tests.mjs --files a,b # specific files
8
+ // --rules no-skip,text-oracle # run a subset (default: every rule)
9
+ // --json # machine output
10
+ // --shares 4 # split the flagged files into 4 review shares
11
+ //
12
+ // (Named lint-tests, not test-lint, so node's --test runner doesn't glob it as a
13
+ // test file via its `test-*` pattern and execute the CLI mid-suite.)
14
+ //
15
+ // Two severities:
16
+ // gate — syntactically decidable, ~zero false positives. Any gate finding makes
17
+ // the exit code 1.
18
+ // lead — a heuristic that also matches some legitimate tests. Reported for the
19
+ // auditor to read and confirm against the principle's "Not a failure"
20
+ // cases; never changes the exit code.
21
+ //
22
+ // Adding a rule = append to ALL_RULES with id, principle, severity, message and
23
+ // one of: `patterns` (line regexes), `match` (per line), `scanFile` (whole file),
24
+ // or `crossFile: true` (resolved by the CLI over every scanned file). If a
25
+ // legitimate test could trip it, it is a lead.
26
+
27
+ import fs from 'node:fs'
28
+ import path from 'node:path'
29
+ import crypto from 'node:crypto'
30
+ import { execSync } from 'node:child_process'
31
+ import { loadMockPolicyFromFile, extractMockTarget, classifyMockTarget } from './mock-policy-config.mjs'
32
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
33
+
34
+ // Skip / focus / expected-fail forms. `.only`/focus silently disable every OTHER
35
+ // test; skip/xfail/fixme disable this one. All banned (SKILL.md principle 4).
36
+ // Unconditional forms only: a declared-skipped test (`test.skip('name', …)`),
37
+ // focus, fixme, todo, and the unconditional decorators. Conditional skips
38
+ // (skipif, a runtime `test.skip()` / `t.Skip()`, xfail) are CONDITIONAL_SKIP_PATTERNS.
39
+ export const SKIP_PATTERNS = [
40
+ { kind: 'skip', re: /\b(?:test|it|describe|suite|context)\s*\.\s*skip\s*(?:\.\s*each\b|\(\s*['"`])/ },
41
+ { kind: 'only', re: /\b(?:test|it|describe|suite|context)\s*\.\s*only\b/ },
42
+ { kind: 'fixme', re: /\b(?:test|it)\s*\.\s*fixme\b/ },
43
+ { kind: 'todo', re: /\b(?:test|it)\s*\.\s*todo\s*\(/ },
44
+ { kind: 'skip', re: /\b(?:xit|xdescribe|xtest)\s*\(/ },
45
+ { kind: 'only', re: /\b(?:fit|fdescribe|ftest)\s*\(/ },
46
+ { kind: 'skip', re: /@(?:pytest\.mark\.)?skip\b/ },
47
+ { kind: 'skip', re: /@unittest\.skip\b/ },
48
+ ]
49
+
50
+ // A skip that depends on a condition: legitimate for a platform or an optional
51
+ // dependency, a hidden hole when the condition always holds in CI. A lead.
52
+ export const CONDITIONAL_SKIP_PATTERNS = [
53
+ { kind: 'skipif', re: /@(?:pytest\.mark\.)?skipif\b|@unittest\.skip(?:If|Unless)\b/ },
54
+ { kind: 'runtime-skip', re: /\bpytest\.(?:skip|importorskip)\s*\(|\bt\.Skip(?:f|Now)?\s*\(|\b(?:t|ctx)\.skip\s*\(/ },
55
+ { kind: 'runtime-skip', re: /\b(?:test|it|describe)\s*\.\s*skip\s*\(\s*(?!['"`])/ },
56
+ { kind: 'xfail', re: /@(?:pytest\.mark\.)?xfail\b/ },
57
+ ]
58
+
59
+ // Fixed sleeps — a timer instead of an explicit wait. The single biggest source of
60
+ // flake; doctrine is "never waitForTimeout()". Precise: these forms are ~never
61
+ // legitimate in a test (an aliased `cy.wait('@x')` or polling helper is not matched).
62
+ export const FIXED_TIMEOUT_PATTERNS = [
63
+ { kind: 'waitForTimeout', re: /\.waitForTimeout\s*\(/ },
64
+ { kind: 'cy.wait', re: /\bcy\.wait\s*\(\s*\d/ }, // numeric only; cy.wait('@alias') is fine
65
+ { kind: 'time.sleep', re: /\btime\.sleep\s*\(/ },
66
+ { kind: 'time.Sleep', re: /\btime\.Sleep\s*\(/ },
67
+ ]
68
+
69
+ // Empty test body — a `test`/`it` whose callback body is `{}`. A placeholder
70
+ // that passes vacuously and asserts nothing: the Missing-Assertion smell in its
71
+ // one decidable, ~zero-false-positive form. The BROADER "has statements but no
72
+ // assertion" case is NOT here — a test can assert through a helper, so a block
73
+ // with no `expect`/`assert` token may still assert; that stays a review LEAD
74
+ // for the auditor (SKILL.md principle 4).
75
+ // Matches only single-line empty bodies (`() => {}` / `function () {}`), incl.
76
+ // `.skip`/`.each(...)`/`.concurrent` chains and Playwright `async ({ page })`
77
+ // fixtures; `describe`/`suite` are excluded (an empty suite is not a fake test).
78
+ // A multi-line empty body is rarer and deliberately left to the lead, not
79
+ // false-blocked.
80
+ export const EMPTY_TEST_PATTERNS = [
81
+ { kind: 'empty-test', re: /^\s*(?:async\s+)?def\s+test\w*\s*\([^)]*\)\s*(?:->\s*[\w\[\], ]+)?:\s*(?:pass|\.\.\.)\s*$/ },
82
+ { kind: 'empty-test', re: /\b(?:it|test)\s*(?:\.\s*\w+\s*(?:\([^)]*\)\s*)?)?\(\s*[`'"][^`'"]*[`'"]\s*,\s*(?:async\s+)?(?:function\s*\*?\s*)?\([^)]*\)\s*(?:=>\s*)?\{\s*\}\s*\)?/ },
83
+ ]
84
+
85
+ // Swallowed assertion — an assertion matcher whose rejection is eaten by a
86
+ // chained `.catch(...)`, so the assertion can NEVER fail the test. The vacuous
87
+ // form behind the #237 login regression: `expect(locator).not.toBeVisible().catch(() => {})`
88
+ // passed whether or not the button was actually visible. Decidable and
89
+ // ~zero-false-positive: a Playwright web-first matcher (`.toXxx(...)`) directly
90
+ // chained to `.catch(` is never legitimate — a real assertion's rejection must
91
+ // surface, not be discarded. (Synchronous Jest matchers don't return a promise,
92
+ // so `.toBe(x).catch()` only appears as a deliberate swallow.) The broader
93
+ // "assertion guarded by a try/catch that re-passes" case stays a review LEAD
94
+ // for the auditor; only the directly-chained matcher form is gated here.
95
+ export const SWALLOWED_ASSERTION_PATTERNS = [
96
+ { kind: 'swallowed-catch', re: /\.to[A-Z]\w*\s*\([^)]*\)\s*\.catch\s*\(/ },
97
+ ]
98
+
99
+ export const RULES = [
100
+ { id: 'no-skip', principle: 4, severity: 'gate', message: 'test skipped, focused, or todo: a skipped test cannot fail (principle 4)', patterns: SKIP_PATTERNS },
101
+ { id: 'no-fixed-timeout', principle: 7, severity: 'gate', message: 'fixed sleep: flaky by construction; wait on the condition instead (principle 7)', patterns: FIXED_TIMEOUT_PATTERNS },
102
+ { id: 'no-empty-test', principle: 4, severity: 'gate', message: 'empty test body: passes vacuously (principle 4)', patterns: EMPTY_TEST_PATTERNS },
103
+ { id: 'no-swallowed-assertion', principle: 4, severity: 'gate', message: 'assertion failure swallowed by .catch(): the matcher can never fail (principle 4)', patterns: SWALLOWED_ASSERTION_PATTERNS },
104
+ ]
105
+
106
+ export const CONDITIONAL_SKIP_RULE = {
107
+ id: 'conditional-skip',
108
+ principle: 4,
109
+ severity: 'lead',
110
+ message: 'conditional skip: fine for a platform or optional dependency; a hole if the condition always holds in CI (principle 4)',
111
+ patterns: CONDITIONAL_SKIP_PATTERNS,
112
+ }
113
+
114
+ // Over-mocking via the externals-allowlist inversion (mock-policy-config.mjs): a
115
+ // mock whose target resolves to first-party code is a finding; declared
116
+ // externals, bare packages and the stdlib are exempt. `coverage.config.json`
117
+ // only declares topology; with no config the conventional `@/`/`~/` defaults
118
+ // apply, so the rule is never silently inert.
119
+ export const MOCK_INTERNAL_SEAM_RULE = {
120
+ id: 'mock-internal-seam',
121
+ principle: 3,
122
+ severity: 'gate',
123
+ defaultOn: true,
124
+ message: 'mocks a first-party module: fake only what you do not own (principle 3)',
125
+ match(raw, code, ctx) {
126
+ const target = extractMockTarget(code)
127
+ if (!target) return null
128
+ const { internal, reason } = classifyMockTarget(target, ctx && ctx.policy)
129
+ if (!internal) return null
130
+ return { kind: 'internal-seam', detail: target, reason }
131
+ },
132
+ }
133
+
134
+ // A matcher that adds nothing: Testing Library `getBy*` and vue-test-utils
135
+ // `get()` already throw when the element is missing, so `toBeDefined()` after
136
+ // them is redundant. The test still fails on a missing element; it proves
137
+ // presence only. A lead, not a gate: the test is shallow, not vacuous.
138
+ export const REDUNDANT_MATCHER_PATTERNS = [
139
+ { kind: 'getBy-exists', re: /\bexpect\(\s*(?:[\w$]+(?:\([^()]*\))?\.)*(?:getBy|getAllBy)\w+\(.*\)\s*\)\s*\.(?:toBeDefined|toBeTruthy|not\.toBeNull|not\.toBeUndefined)\(\s*\)/ },
140
+ { kind: 'get-exists', re: /\bexpect\(\s*[\w$.]+\.get(?:Component)?\(.*\)\.exists\(\)\s*\)\s*\.toBe\(\s*true\s*\)/ },
141
+ ]
142
+
143
+ export const REDUNDANT_MATCHER_RULE = {
144
+ id: 'redundant-matcher',
145
+ principle: 4,
146
+ severity: 'lead',
147
+ message: 'the query already throws when missing, so this matcher adds nothing; the test proves presence only (principle 4)',
148
+ patterns: REDUNDANT_MATCHER_PATTERNS,
149
+ }
150
+
151
+ // Text oracle: the test reads a checked-in doc or source file and matches its
152
+ // text. File-level because the path, the read and the match sit on different
153
+ // lines. A lead: reading a file to execute it, comparing it to a value from
154
+ // code, or a forbidden-capability pin are all legitimate (SKILL.md principle 1).
155
+ // Config files belong to config-read, which fires whatever the assertion.
156
+ const READ_CALL = /\b(?:readFileSync|readFile|read_text|ReadFile|open)\s*\(/
157
+ const WRITE_CALL = /\b(?:writeFileSync|writeFile|write_text|WriteFile|appendFileSync|copyFileSync|cpSync|copyfile|copy2?)\s*\(/
158
+ const TEXT_ASSERT = /\.(?:toContain|toMatch|includes)\s*\(|\bassert\.(?:match|doesNotMatch)\s*\(|\b(?:Contains|NotContains|Regexp)\s*\(\s*t\s*,|\bassert(?:In|NotIn|Regex)\s*\(|['"][^'"\n]{3,}['"]\s+(?:not\s+)?in\s+[\w.]/
159
+ export const CHECKED_IN_TEXT = /['"`]([^'"`\s]*\.(?:md|mdx|txt|sh|sql|tsx?|jsx?|[cm]js|vue|py|go|css|html|prisma))['"`]/
160
+ // Config by name, not by extension: tests read JSON and YAML as data (run
161
+ // state, artifacts, temp files) far more often than as config.
162
+ const CONFIG_NAME = [
163
+ String.raw`\.github\/workflows\/[^'"\`\s/]+\.ya?ml`,
164
+ String.raw`\.gitlab-ci\.ya?ml`,
165
+ String.raw`\.?(?:mergify|cloudbuild|docker-compose|compose|skaffold|kustomization|pnpm-workspace|action|dependabot|renovate|codecov|app|netlify|vercel|firebase|fly|wrangler|turbo|nx|biome|tsconfig|jsconfig|package|deno|pyproject|Cargo|setup|tox)(?:[.-][\w.-]*)?\.(?:ya?ml|json[c5]?|toml|cfg|ini)`,
166
+ String.raw`\.(?:eslint|prettier|babel|stylelint)rc(?:\.[\w]+)?`,
167
+ String.raw`(?:Dockerfile|Containerfile|\.env)(?:\.[\w.-]+)?`,
168
+ ].join('|')
169
+ export const CONFIG_FILE = new RegExp(String.raw`['"\`]((?:[^'"\`\s]*\/)?(?:${CONFIG_NAME}))['"\`]`)
170
+ const FIXTURE_PATH = /(?:^|[/'"`])(?:fixtures?|__fixtures__|testdata|__snapshots__|golden)(?:[/'"`]|$)/
171
+ const RUNS_OR_IMPORTS = /\b(?:spawn|spawnSync|execFile|execFileSync|execSync|exec|fork|import|require|run|subprocess\.\w+|runpy\.\w+)\s*\(|^\s*(?:import|export)\b|\bfrom\s+['"]/
172
+ const DOC_COMMENT = /^\s*(?:\*|\/\*)/
173
+
174
+ // Every checked-in path matching `pathRe` that the test reads as an oracle.
175
+ // A path that is written, copied, or bound to a name later executed or
176
+ // imported is produced or run by the test, not read as an oracle.
177
+ function checkedInReads(lines, code, pathRe) {
178
+ const exempt = new Set()
179
+ for (const l of code) {
180
+ const m = WRITE_CALL.test(l) && l.match(pathRe)
181
+ if (m) exempt.add(m[1])
182
+ }
183
+ code.forEach((l) => {
184
+ const m = l.match(pathRe)
185
+ const bound = m && l.match(/^\s*(?:(?:const|let|var)\s+)?([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=/)
186
+ if (!bound) return
187
+ const name = new RegExp(`\\b${bound[1].replace(/\$/g, '\\$')}\\b`)
188
+ if (code.some((other) => other !== l && name.test(other) && RUNS_OR_IMPORTS.test(other))) exempt.add(m[1])
189
+ })
190
+ const out = []
191
+ code.forEach((l, i) => {
192
+ const m = l.match(pathRe)
193
+ if (!m || exempt.has(m[1])) return
194
+ if (DOC_COMMENT.test(lines[i]) || FIXTURE_PATH.test(m[1]) || RUNS_OR_IMPORTS.test(l) || WRITE_CALL.test(l)) return
195
+ out.push({ line: i + 1, detail: m[1] })
196
+ })
197
+ return out
198
+ }
199
+
200
+ export const TEXT_ORACLE_RULE = {
201
+ id: 'text-oracle',
202
+ principle: 1,
203
+ severity: 'lead',
204
+ collapse: true,
205
+ message: 'reads a checked-in file and matches its text: test behavior, not text; a text-only test is not coverage (principle 1)',
206
+ scanFile(file, lines) {
207
+ const code = lines.map(stripComment)
208
+ if (!code.some((l) => READ_CALL.test(l)) || !code.some((l) => TEXT_ASSERT.test(l))) return []
209
+ return checkedInReads(lines, code, CHECKED_IN_TEXT).map((hit) => ({ ...hit, kind: 'checked-in-text' }))
210
+ },
211
+ }
212
+
213
+ // Config read: the test reads a checked-in config file (Dockerfile, workflow,
214
+ // cloudbuild, .mergify.yml, package.json, tsconfig, pyproject). Any assertion
215
+ // counts: parsing the file and comparing fields restates it as surely as a
216
+ // text match. A lead: JSON data the app loads (locales, price tables) is not
217
+ // config (SKILL.md principle 6).
218
+ export const CONFIG_READ_RULE = {
219
+ id: 'config-read',
220
+ principle: 6,
221
+ severity: 'lead',
222
+ collapse: true,
223
+ message: 'reads a checked-in config file: the tool that reads it is the test; do not test config (principle 6)',
224
+ scanFile(file, lines) {
225
+ const code = lines.map(stripComment)
226
+ if (!code.some((l) => READ_CALL.test(l))) return []
227
+ return checkedInReads(lines, code, CONFIG_FILE).map((hit) => ({ ...hit, kind: 'config-file' }))
228
+ },
229
+ }
230
+
231
+ // Count pin: an exact size of five or more. Lengths that encode an external
232
+ // format (ULID 26, MD5 32, UUID 36, SHA-1 40, SHA-256 64, SHA-512 128) are
233
+ // excluded; a lead because a fixture's own size is a legitimate pin.
234
+ const FORMAT_LENGTHS = new Set([26, 32, 36, 40, 64, 128])
235
+ const COUNT_PIN_PATTERNS = [
236
+ /\.toHaveLength\(\s*(\d+)\s*\)/,
237
+ /\bexpect\([\w$.[\]()]+\.length\)\s*\.to(?:Be|Equal|StrictEqual)\(\s*(\d+)\s*\)/,
238
+ /\bassert\.(?:strict)?[Ee]qual\(\s*[\w$.[\]()]+\.length\s*,\s*(\d+)\s*[,)]/,
239
+ /\b(?:assert|require)\.Len\(\s*t\s*,\s*[^,]+,\s*(\d+)\s*\)/,
240
+ /\bassert\s+len\([^)]*\)\s*==\s*(\d+)\b/,
241
+ ]
242
+
243
+ export const COUNT_PIN_RULE = {
244
+ id: 'count-pin',
245
+ principle: 2,
246
+ severity: 'lead',
247
+ message: 'pins an exact count: every legitimate addition fails it; derive the expected size or assert the property (principle 2)',
248
+ match(raw, code) {
249
+ for (const re of COUNT_PIN_PATTERNS) {
250
+ const m = code.match(re)
251
+ if (!m) continue
252
+ const n = Number(m[1])
253
+ if (n >= 5 && !FORMAT_LENGTHS.has(n)) return { kind: 'count-pin', detail: String(n) }
254
+ }
255
+ return null
256
+ },
257
+ }
258
+
259
+ // Utility-class assertion: a Tailwind-shaped token asserted on a DOM class list.
260
+ // jsdom has no layout, so this proves neither size nor position; a semantic
261
+ // state class (`health-unknown`) does not match.
262
+ const CLASS_ASSERT = /\.toHaveClass\(|\.classes\(\)\s*\)\s*\.toContain\(|\.className\s*\)\s*\.(?:toContain|toMatch)\(/
263
+ const STRING_LITERAL = /(['"`])((?:(?!\1).)*)\1/g
264
+ const UTILITY_TOKEN = /^!?(?:(?:sm|md|lg|xl|2xl|hover|focus|group-[\w-]+|data-\[[^\]]*\]):.+|.*-\[[^\]]+\]!?|-?(?:m[trblxy]?|p[trblxy]?|w|h|min-h|min-w|max-w|max-h|size|gap|rounded|shadow|grid-cols|space-[xy])(?:-[\w./]+)?!?)$/
265
+ const hasUtilityToken = (code) => [...code.matchAll(STRING_LITERAL)]
266
+ .some((m) => m[2].split(/\s+/).some((token) => UTILITY_TOKEN.test(token)))
267
+
268
+ export const UTILITY_CLASS_RULE = {
269
+ id: 'utility-class',
270
+ principle: 3,
271
+ severity: 'lead',
272
+ message: 'asserts a utility CSS class: jsdom has no layout; prove size or position in a real browser (principle 3)',
273
+ match(raw, code) {
274
+ return CLASS_ASSERT.test(code) && hasUtilityToken(code) ? { kind: 'utility-class' } : null
275
+ },
276
+ }
277
+
278
+ // Duplicate test file: identical content after whitespace normalisation.
279
+ // Resolved by the CLI across every scanned file (findDuplicateFiles).
280
+ export const DUPLICATE_FILE_RULE = {
281
+ id: 'duplicate-file',
282
+ principle: 5,
283
+ severity: 'lead',
284
+ crossFile: true,
285
+ message: 'identical to another test file: one behavior, one place (principle 5)',
286
+ }
287
+
288
+ export const ALL_RULES = [
289
+ ...RULES,
290
+ CONDITIONAL_SKIP_RULE,
291
+ MOCK_INTERNAL_SEAM_RULE,
292
+ REDUNDANT_MATCHER_RULE,
293
+ TEXT_ORACLE_RULE,
294
+ CONFIG_READ_RULE,
295
+ COUNT_PIN_RULE,
296
+ UTILITY_CLASS_RULE,
297
+ DUPLICATE_FILE_RULE,
298
+ ]
299
+
300
+ export function selectRules(ruleIds) {
301
+ if (ruleIds && ruleIds.length > 0) {
302
+ const set = new Set(ruleIds)
303
+ return ALL_RULES.filter((r) => set.has(r.id))
304
+ }
305
+ return ALL_RULES.filter((r) => r.defaultOn !== false)
306
+ }
307
+
308
+ // Strip line comments so a doc line or a commented-out example is not flagged.
309
+ // Best-effort (ignores strings/block comments) — live code has no leading `//`.
310
+ export function stripComment(line) {
311
+ return line.replace(/\/\/.*$/, '').replace(/(^|\s)#.*$/, '$1')
312
+ }
313
+
314
+ // Only test code is scanned: linting app code would false-flag a legitimate
315
+ // `.skip()`/`.only` array method or a real `sleep`, and a data file under
316
+ // `tests/` (parquet, JSON fixtures) is not a test.
317
+ export function isTestFile(p) {
318
+ if (!p) return false
319
+ return /\.(spec|test)\.[cm]?[jt]sx?$/.test(p)
320
+ || (/(^|\/)(e2e|tests?|__tests__)\//.test(p) && /\.(?:[cm]?[jt]sx?|py|go)$/.test(p))
321
+ || /(^|\/)test_[^/]*\.py$/.test(p)
322
+ || /_test\.py$/.test(p)
323
+ || /_test\.go$/.test(p)
324
+ }
325
+
326
+ function finding(file, rule, line, raw, hit = {}) {
327
+ return {
328
+ file, line, rule: rule.id, severity: rule.severity, principle: rule.principle,
329
+ kind: hit.kind || rule.id,
330
+ snippet: (raw || '').trim().slice(0, 120),
331
+ ...(hit.detail ? { detail: hit.detail } : {}),
332
+ ...(hit.reason ? { reason: hit.reason } : {}),
333
+ }
334
+ }
335
+
336
+ // Pure: run the line-level rules over one line → findings (≤ one per rule).
337
+ function scanLine(file, raw, lineNo, rules, ctx) {
338
+ const code = stripComment(raw)
339
+ const out = []
340
+ for (const rule of rules) {
341
+ if (typeof rule.match === 'function') {
342
+ const hit = rule.match(raw, code, ctx)
343
+ if (hit) out.push(finding(file, rule, lineNo, raw, hit))
344
+ } else if (rule.patterns) {
345
+ const hit = rule.patterns.find((p) => p.re.test(code))
346
+ if (hit) out.push(finding(file, rule, lineNo, raw, hit))
347
+ }
348
+ }
349
+ return out
350
+ }
351
+
352
+ // A `collapse` rule reports once per file, at its first hit, naming every
353
+ // distinct detail: one lead per test file is what an auditor reads.
354
+ function scanFileRules(file, lines, rules, ctx, keep = () => true) {
355
+ const out = []
356
+ for (const rule of rules) {
357
+ if (typeof rule.scanFile !== 'function') continue
358
+ const hits = rule.scanFile(file, lines, ctx).filter((hit) => keep(hit.line))
359
+ if (hits.length === 0) continue
360
+ if (rule.collapse) {
361
+ const detail = [...new Set(hits.map((hit) => hit.detail))].join(', ')
362
+ out.push(finding(file, rule, hits[0].line, lines[hits[0].line - 1], { ...hits[0], detail }))
363
+ } else {
364
+ for (const hit of hits) out.push(finding(file, rule, hit.line, lines[hit.line - 1], hit))
365
+ }
366
+ }
367
+ return out
368
+ }
369
+
370
+ const byPosition = (a, b) => a.line - b.line || a.rule.localeCompare(b.rule)
371
+
372
+ export function scanLines(file, lines, ruleIds, ctx) {
373
+ const rules = selectRules(ruleIds)
374
+ const out = []
375
+ lines.forEach((raw, i) => out.push(...scanLine(file, raw, i + 1, rules, ctx)))
376
+ out.push(...scanFileRules(file, lines, rules, ctx))
377
+ return out.sort(byPosition)
378
+ }
379
+
380
+ export function scanText(file, text, ruleIds, ctx) {
381
+ return scanLines(file, (text || '').split(/\r?\n/), ruleIds, ctx)
382
+ }
383
+
384
+ // Pure: scan a unified diff, reporting only findings on ADDED lines of test
385
+ // files. File-level rules need the whole file: pass `readFile(path) → text` and
386
+ // they run on it, keeping only findings that land on an added line.
387
+ export function scanDiff(diffText, ruleIds, ctx, readFile) {
388
+ const rules = selectRules(ruleIds)
389
+ const out = []
390
+ const added = new Map()
391
+ let file = null
392
+ let newLine = 0
393
+ for (const raw of (diffText || '').split(/\r?\n/)) {
394
+ if (raw.startsWith('+++ ')) {
395
+ const m = raw.match(/^\+\+\+ (?:b\/)?(.+)$/)
396
+ file = m && m[1] !== '/dev/null' ? m[1] : null
397
+ continue
398
+ }
399
+ if (raw.startsWith('@@')) {
400
+ const m = raw.match(/\+(\d+)/)
401
+ newLine = m ? parseInt(m[1], 10) : 0
402
+ continue
403
+ }
404
+ if (raw.startsWith('+')) {
405
+ if (isTestFile(file)) {
406
+ out.push(...scanLine(file, raw.slice(1), newLine, rules, ctx))
407
+ if (!added.has(file)) added.set(file, new Set())
408
+ added.get(file).add(newLine)
409
+ }
410
+ newLine++
411
+ } else if (raw.startsWith('-') || raw.startsWith('\\')) {
412
+ // removed line / "No newline" marker — do not advance the new-file counter
413
+ } else {
414
+ newLine++ // context line
415
+ }
416
+ }
417
+ if (typeof readFile === 'function' && rules.some((r) => typeof r.scanFile === 'function')) {
418
+ for (const [f, lines] of added) {
419
+ const text = readFile(f)
420
+ if (text == null) continue
421
+ out.push(...scanFileRules(f, text.split(/\r?\n/), rules, ctx, (line) => lines.has(line)))
422
+ }
423
+ }
424
+ return out
425
+ }
426
+
427
+ // Pure: group files whose whitespace-normalised content is identical. Files
428
+ // under ten non-blank lines are ignored (tiny smoke files legitimately match).
429
+ export function findDuplicateFiles(entries) {
430
+ const groups = new Map()
431
+ for (const { file, text } of entries) {
432
+ const lines = (text || '').split(/\r?\n/).filter((l) => l.trim())
433
+ if (lines.length < 10) continue
434
+ const key = crypto.createHash('sha256').update(lines.map((l) => l.trim().replace(/\s+/g, ' ')).join('\n')).digest('hex')
435
+ if (!groups.has(key)) groups.set(key, [])
436
+ groups.get(key).push(file)
437
+ }
438
+ const out = []
439
+ for (const files of groups.values()) {
440
+ if (files.length < 2) continue
441
+ for (const f of files) {
442
+ out.push(finding(f, DUPLICATE_FILE_RULE, 1, '', { detail: files.filter((o) => o !== f).join(', ') }))
443
+ }
444
+ }
445
+ return out
446
+ }
447
+
448
+ // ---- CLI ----
449
+
450
+ const SKIP_DIRS = new Set(['node_modules', 'site-packages', 'venv'])
451
+
452
+ function discover(dir, acc = []) {
453
+ let entries
454
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return acc } // unreadable dir: skip, never crash the scan
455
+ for (const entry of entries) {
456
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.') || entry.name.endsWith('-workspace')) continue
457
+ const full = path.join(dir, entry.name)
458
+ if (entry.isDirectory()) discover(full, acc)
459
+ else if (isTestFile(full)) acc.push(full)
460
+ }
461
+ return acc
462
+ }
463
+
464
+ // `--exclude dist,vendor,fixtures`: drop any file with one of these as a path
465
+ // segment (or path prefix), for vendored, generated, and fixture trees.
466
+ export function excluded(file, segments) {
467
+ if (!segments || segments.length === 0) return false
468
+ const norm = file.split(path.sep).join('/').replace(/^\.\//, '')
469
+ const parts = norm.split('/')
470
+ return segments.some((s) => parts.includes(s) || norm.startsWith(`${s.replace(/\/$/, '')}/`))
471
+ }
472
+
473
+ // `--shares N`: split the files with findings into at most N shares of about
474
+ // equal finding count, one per audit reviewer. A file stays whole; the
475
+ // biggest files are placed first, each into the lightest share.
476
+ export function splitShares(findings, n) {
477
+ const counts = new Map()
478
+ for (const f of findings) counts.set(f.file, (counts.get(f.file) || 0) + 1)
479
+ const files = [...counts].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
480
+ const shares = Array.from({ length: Math.min(n, files.length) }, () => ({ findings: 0, files: [] }))
481
+ for (const [file, count] of files) {
482
+ const lightest = shares.reduce((min, share) => (share.findings < min.findings ? share : min))
483
+ lightest.files.push(file)
484
+ lightest.findings += count
485
+ }
486
+ return shares
487
+ }
488
+
489
+ const USAGE = [
490
+ 'usage: lint-tests.mjs [--diff <base>] [--files a,b] [--rules a,b] [--exclude a,b] [--config coverage.config.json] [--shares N] [--json]',
491
+ ' (no flags) scan every test file under the current directory; run from the repo root',
492
+ ' --diff <base> report only findings on lines changed since the merge-base with <base>: commits,',
493
+ ' staged and unstaged edits, and untracked test files (whole file)',
494
+ ' --files a,b scan exactly these files; a missing file is an error',
495
+ ` --rules a,b subset of: ${ALL_RULES.map((r) => r.id).join(', ')}`,
496
+ ' --exclude a,b skip files with any of these path segments or prefixes (e.g. dist,vendor,fixtures)',
497
+ ' --shares N also split the files with findings into at most N review shares of about equal finding count',
498
+ ' exit 0 no gate findings · 1 gate findings · 2 usage error. Lead findings never change the exit code.',
499
+ ].join('\n')
500
+
501
+ const VALUE_FLAGS = new Set(['--diff', '--files', '--rules', '--config', '--exclude', '--shares'])
502
+ const list = (v) => (v || '').split(',').map((s) => s.trim()).filter(Boolean)
503
+
504
+ export function runCli(argv, { name = 'lint-tests', cwd = process.cwd() } = {}) {
505
+ if (argv.includes('--help') || argv.includes('-h')) { console.log(USAGE); return 0 }
506
+ const usageError = (msg) => { console.error(`${name}: ${msg}\n\n${USAGE}`); return 2 }
507
+ const flags = {}
508
+ for (let i = 0; i < argv.length; i++) {
509
+ const a = argv[i]
510
+ if (a === '--json') { flags.json = true; continue }
511
+ if (!VALUE_FLAGS.has(a)) return usageError(`unknown argument ${JSON.stringify(a)}`)
512
+ const v = argv[++i]
513
+ if (v === undefined || v.startsWith('--')) return usageError(`${a} needs a value`)
514
+ flags[a.slice(2)] = v
515
+ }
516
+ const ruleIds = flags.rules ? list(flags.rules) : null
517
+ const unknown = (ruleIds || []).filter((id) => !ALL_RULES.some((r) => r.id === id))
518
+ if (unknown.length) return usageError(`unknown rule(s): ${unknown.join(', ')}`)
519
+ const shareCount = flags.shares === undefined ? null : Number(flags.shares)
520
+ if (shareCount !== null && !(Number.isInteger(shareCount) && shareCount > 0)) return usageError('--shares needs a positive whole number')
521
+ const exclude = list(flags.exclude)
522
+ const configPath = flags.config && path.resolve(cwd, flags.config)
523
+ const ctx = { policy: loadMockPolicyFromFile(configPath, cwd) }
524
+
525
+ let findings = []
526
+ if (flags.diff) {
527
+ // Diff from the merge-base to the WORKING TREE, so a writer's uncommitted
528
+ // tests count; git paths are repo-root-relative, so read files from there.
529
+ const git = (args) => execSync(`git ${args}`, { cwd, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })
530
+ const root = git('rev-parse --show-toplevel').trim()
531
+ const base = git(`merge-base ${flags.diff} HEAD`).trim()
532
+ const readAtRoot = (f) => { const p = path.join(root, f); return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null }
533
+ const diff = execSync(`git diff ${base}`, { cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })
534
+ findings = scanDiff(diff, ruleIds, ctx, readAtRoot).filter((f) => !excluded(f.file, exclude))
535
+ const untracked = execSync('git ls-files -o --exclude-standard', { cwd: root, encoding: 'utf8' })
536
+ .split('\n').filter((f) => f && isTestFile(f) && !excluded(f, exclude))
537
+ for (const f of untracked) findings.push(...scanText(f, readAtRoot(f), ruleIds, ctx))
538
+ } else {
539
+ let files
540
+ if (flags.files) {
541
+ files = list(flags.files)
542
+ const missing = files.filter((f) => !fs.existsSync(path.resolve(cwd, f)))
543
+ if (missing.length) return usageError(`no such file(s): ${missing.join(', ')}`)
544
+ } else {
545
+ files = discover(cwd).map((f) => path.relative(cwd, f) || f)
546
+ }
547
+ const entries = files.filter((f) => !excluded(f, exclude))
548
+ .map((f) => ({ file: f, text: fs.readFileSync(path.resolve(cwd, f), 'utf8') }))
549
+ for (const { file, text } of entries) findings.push(...scanText(file, text, ruleIds, ctx))
550
+ if (selectRules(ruleIds).some((r) => r.crossFile)) findings.push(...findDuplicateFiles(entries))
551
+ }
552
+
553
+ const gates = findings.filter((f) => f.severity === 'gate').length
554
+ const leads = findings.length - gates
555
+ const shares = shareCount === null ? null : splitShares(findings, shareCount)
556
+ if (flags.json) {
557
+ console.log(JSON.stringify({ ok: gates === 0, gates, leads, findings, ...(shares && { shares }) }, null, 2))
558
+ } else if (findings.length === 0) {
559
+ console.log(`${name}: ✓ no findings` + (flags.diff ? ' in this change' : ' in scanned test files'))
560
+ } else {
561
+ console.log(`${name}: ${gates ? '✗' : '✓'} ${gates} gate, ${leads} lead finding(s):\n`)
562
+ for (const f of findings) {
563
+ const why = f.detail ? ` → ${f.detail}${f.reason ? ` (${f.reason})` : ''}` : ''
564
+ console.log(` ${f.file}:${f.line} [${f.severity} ${f.rule}/${f.kind}] ${f.snippet}${why}`)
565
+ }
566
+ for (const [i, share] of (shares || []).entries()) {
567
+ console.log(`\nshare ${i + 1}: ${share.findings} finding(s) in ${share.files.length} file(s)\n ${share.files.join('\n ')}`)
568
+ }
569
+ }
570
+ return gates === 0 ? 0 : 1
571
+ }
572
+
573
+ // exitCode, not process.exit(): exiting early truncates piped stdout at 64KB.
574
+ if (isMainModule(import.meta.url)) process.exitCode = runCli(process.argv.slice(2), { name: 'lint-tests' })