@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,274 @@
1
+ # Mergify Delivery Watch Contract
2
+
3
+ Use this contract whenever an agent queues or watches a PR through Mergify, whether the work is a full `resolve-issues` delivery or a simple PR. The goal is to avoid the recurring false states where a dequeued PR is reported as healthy, a not-yet-admitted PR is reported as failed, or a stale GitHub check-rollup is treated as Mergify queue truth.
4
+
5
+ ## A Watch Observes A Progression, Not A State
6
+
7
+ Read this before the state machine — it is the premise the rest of the file rests on, and skipping it is how a correct-looking watcher gets built anyway.
8
+
9
+ The queue lifecycle is a **sequence**, and every verdict worth having is a *transition* between its phases, never a property of a single reading:
10
+
11
+ ```
12
+ NOT_YET_QUEUED ──enqueue accepted──▶ QUEUED ──┬──▶ MERGED
13
+ │ ├──▶ DEQUEUED
14
+ └──▶ STALLED (never admitted) └──▶ CLOSED_UNMERGED
15
+ ```
16
+
17
+ The identical observation means opposite things on either side of queue entry:
18
+
19
+ | Reading | Before entry | After entry |
20
+ |---|---|---|
21
+ | active queue membership absent | `NOT_YET_QUEUED` — healthy, keep waiting | **`DEQUEUED`** — terminal, read the reason |
22
+
23
+ So the watcher's memory *is* the verdict. Five pieces of state must survive across polls:
24
+
25
+ 1. **`seenQueued`** — has queue entry ever been observed? Every downstream transition depends on it. Set from the `queued` label **or** `mergify queue show`.
26
+ 2. **A re-read counter** (`missingActiveReads` / `absentStreak`) — one absence is suspicion, two consecutive *readable* absences settle `DEQUEUED`.
27
+ 3. **Readable vs. absent** — a failed `gh`/`mergify` call is *no information*: it must not advance that counter or overwrite that source's last readable contribution to the stall-state snapshot. During a partial outage, the still-readable sibling remains informative and genuine progress from it still resets the change clock.
28
+ 4. **A change clock** (`lastChangeMs`) — the stall bound is *no observable change*, not elapsed time.
29
+ 5. **The last synthetic batch PR number** — retain
30
+ `mergeability_check.queue_pull_request_number` from every readable active queue
31
+ observation (and accept the older top-level spelling). Mergify removes that relationship
32
+ from `queue show` after dequeue, but the terminal reason belongs to the synthetic batch,
33
+ not the original PR.
34
+
35
+ ### Why a per-poll predicate cannot satisfy this
36
+
37
+ A background-task runner that re-executes a command each tick and reads its exit code is a **stateless predicate evaluator**. There is nowhere to keep items 1–4, so the shape is wrong before the code is. This is not theoretical — pi session `019fae8f` produced four hand-rolled watchers for one PR, and each failed a different one of the four:
38
+
39
+ | Hand-rolled watcher | Failure |
40
+ |---|---|
41
+ | `state ∈ {MERGED, CLOSED}` | A dequeued PR is plain `OPEN`. Dequeue is structurally invisible; the watch runs to its ceiling reporting health. |
42
+ | `.queued // false` | The JSON-shape trap below — never observes entry, so item 1 stays false and the whole dequeue path is dead code. |
43
+ | single absent read → terminal | No item 2. Settled `DEQUEUED` on a PR that was mid-`checking`. |
44
+ | `mergify … \|\| printf '{"queued":false}'` | No item 3. Converts an outage into a false dequeue, *faster* than the stall bound rather than through it. |
45
+
46
+ Two of the four were written *after* the agent had been told to check for dequeue, and one of those reported a false `DEQUEUED` on a healthy PR. Knowing the requirement does not rescue the shape.
47
+
48
+ **Therefore: run [`../scripts/watch-pr-delivery.mjs`](../scripts/watch-pr-delivery.mjs).** It is one long-lived process owning its own poll loop, so items 1–4 live in its locals. Do not wrap it in an outer poller — that reintroduces exactly the shape it exists to replace.
49
+
50
+ ### `phases` — the progression as output
51
+
52
+ The watcher emits `phases`: the ordered list of lifecycle phases actually observed, on both the terminal verdict and every heartbeat. It resolves the one ambiguity the terminal outcome cannot:
53
+
54
+ | terminal | `phases` | meaning | fix |
55
+ |---|---|---|---|
56
+ | `STALLED` | `["NOT_YET_QUEUED","STALLED"]` | never admitted (`reason` is usually `admission-stall`) | enqueue it, or diagnose why admission never happened |
57
+ | `STALLED` | `["NOT_YET_QUEUED","QUEUED","STALLED"]` | admitted, then wedged in the queue (`reason` is usually `stall-bound`) | read the blocking condition |
58
+
59
+ `WAITING` is deliberately not a phase — it is the re-read suspicion state *inside* `QUEUED`, and recording it would oscillate a healthy PR's progression into noise.
60
+
61
+ ## Enqueue Before You Watch
62
+
63
+ The watch observes only — it never posts `@mergifyio queue`. Check membership first, because on a repo configured for manual delivery the answer decides whether a watch is worth starting at all:
64
+
65
+ ```bash
66
+ mergify queue show <number> --json | jq '{position, queue_rule_name, queued_at}' # membership now
67
+ mergify queue show <number> --json | jq '.queue_rule.config.autoqueue' # can it self-queue?
68
+ grep -n "auto_merge_conditions" .mergify.yml # or auto-enqueue?
69
+ ```
70
+
71
+ Read `autoqueue` from the **resolved** config Mergify reports, not from `.mergify.yml`. The authorable key is deprecated, so a correct config never contains it and grepping the file always finds nothing — which tells you nothing. `queue show` reports the effective value either way.
72
+
73
+ If the PR is not queued, `autoqueue` is `false`, **and** the repo has no `auto_merge_conditions`, nothing will ever put it in the queue on its own. A watch started there can only end `STALLED` — enqueue first, then watch. A repo whose `.mergify.yml` says *"delivery stays manual"* is exactly this case.
74
+
75
+ A 👍 on the command comment acknowledges the *command*, not queue entry.
76
+
77
+ ## Batch Delivery — Enqueue The Whole Ready Set Together
78
+
79
+ Batch mode is configured at the queue level (`batch_size`, `batch_max_wait_time`), but whether a batch ever *happens* is a delivery decision. Mergify only batches PRs that are **in the queue together**: it holds the gate open for `batch_max_wait_time`, then tests up to `batch_size` PRs as one CI run. So the batching lever in an agent's hands is *enqueue timing and grouping* — not config. (Config's part is one line: write `batch_size: 3` — the skill's default. It must be explicit: an unset `batch_size` silently runs at Mergify's built-in default of 1, i.e. no batching — see [`configuration.md`](configuration.md#2--3-batch-mode-size-3).)
80
+
81
+ **The default delivery pattern is to enqueue the whole ready set in one pass.** When several PRs against the same base are merge-ready — a stack, an epic's units, a burst of related edits — enqueue them all before watching any:
82
+
83
+ - One batch CI run for N PRs instead of N. Serial delivery — enqueue #1, watch it merge, then enqueue #2 — means each PR rides its own batch: N CI runs and N watch cycles for work that could have landed in one. Being in the queue together is never slower; it is usually several CI runs faster.
84
+ - A red batch is safe: Mergify bisects it and dequeues only the culprit; the innocent PRs carry on.
85
+ - **A `resolve-issues` chain line is one entry.** Its deepest PR carries the others' commits, so enqueue only that PR; its merge lands the line, and GitHub marks the others merged.
86
+ - **"If possible" is the escape hatch, not the rule.** A lone PR, or a set where the rest still wait on review/CI/conflict, is enqueued as-is — `batch_max_wait_time` bounds the wait for company, and a ready PR is never held back for a straggler.
87
+
88
+ **Mergify groups PRs into a batch by relationship, not queue order.** It does not simply take the next N in the queue: priority and queue order seed a batch, **stacks always stay together**, and similarity (shared scopes, then changed directories, then queue time) fills the remaining slots. Consequences:
89
+
90
+ - Related PRs enqueued together reliably ride one batch — that is the pattern to aim for.
91
+ - Unrelated PRs may still split across batches. That is Mergify's choice and it is correct: it keeps one PR's failure from dragging unrelated work into a bisection. Do not force co-batching (e.g. by inventing scopes) to fight it.
92
+
93
+ **Mechanics.** `@mergifyio queue` is per-PR — it is a comment posted on the PR and takes no PR list — so "together" means posting it on each ready PR in the same pass (for a stack, one comment on the top PR queues every member down to the root, and dequeuing a member dequeues the PRs above it), then watching: one `watch-pr-delivery.mjs` per PR, launched together (they are cheap at the default interval), or the batch as a whole via `mergify queue status`. Enqueue promptly once a PR is ready: the batch window is the fill time, and a PR entering after it closes rides the *next* batch.
94
+
95
+ While a batch forms, `waiting_for_batch` just means the gate is still open — keep watching, do not read it as stuck. `bisecting` means the batch went red and Mergify is splitting it — keep watching; only the culprit leaves the queue.
96
+
97
+ ## Source Of Truth
98
+
99
+ Use Mergify and GitHub for different facts:
100
+
101
+ 1. `gh pr view <PR> --json state,mergedAt,mergeCommit,labels,url,comments`
102
+ - authoritative for whether the PR merged or closed
103
+ - source for Mergify lifecycle labels: `queued` and `dequeued`
104
+ - `comments` is not optional padding: it is the only source for *when* a `dequeued` label was applied (Mergify's status-comment payload), which is what distinguishes this attempt's dequeue from a stale label left by a previous one. Omit it and that guard silently degrades to "no reliable timestamp" on every poll. It costs no extra request — same call.
105
+ 2. `mergify queue show <number> --json`
106
+ - authoritative for active queue membership, position, queue rule, CI state, and condition tree
107
+ 3. `gh pr checks <PR>` / check-rollup
108
+ - useful for debugging CI, but not authoritative for queue membership
109
+
110
+ Do not use `Mergify Merge Queue` check-rollup state alone to decide queued/dequeued status. It can be stale, neutral, or in progress while the queue tool says something more precise.
111
+
112
+ ## State Machine
113
+
114
+ A watcher must retain history for the PR it is watching.
115
+
116
+ - `MERGED`: PR state is `MERGED` or `mergedAt` is set. Return the PR merge commit SHA, not the branch head. The caller still owes ancestry verification.
117
+
118
+ On this path Mergify removes `queued` **without ever adding `dequeued`** — so a watcher that waits for a `dequeued` label to decide anything waits forever on the happy path. The label is also a lifecycle mirror that is never cleared on re-enqueue, so its mere presence is not this attempt's outcome; date it against the watch's start before treating it as terminal (see the `#629` guards in the script).
119
+ - `CLOSED_UNMERGED`: PR state is `CLOSED` with no merge. Terminal hand-back.
120
+ - `QUEUED`: `queued` label is present, or `mergify queue show --json` reports active membership. Set `seenQueued = true`.
121
+
122
+ **Do not test for `queued: true` — that field does not exist on a queued PR.** Verified live against PR #1396: a PR *in* the queue returns `position`, `queue_rule_name`, `queued_at`, and `queue_rule` — and no `queued` key at all. Only the *not*-queued answer is literally `{"number": <PR>, "queued": false}`. A watcher keyed on `queued === true` therefore never observes queue entry, so `seenQueued` stays false forever and every downstream transition that depends on it (`DEQUEUED`, the re-read rule) is silently dead. Infer membership the way the bundled script does:
123
+
124
+ ```js
125
+ const queued = parsed.queued === true || Boolean(parsed.queue_rule_name || parsed.queue_rule)
126
+ ```
127
+ - `DEQUEUED`: PR is still open and unmerged, and either:
128
+ - `dequeued` label is present (this-watch timestamp guards apply — see `#629`), or
129
+ - **process-local** membership was observed (`everActive`) and is now absent across two consecutive readable re-reads (`lost-queue-membership`).
130
+ - `NOT_YET_QUEUED`: active queue membership is absent before this process has observed membership. This is not a dequeue. It usually means the queue command has not been processed yet, the PR is waiting for queue conditions, or Mergify status has not refreshed. **`--seen-queued` alone does not leave this state** — that flag is a resume *hint* for callers/phases; it does not prove this process saw membership (epic-824 `#859` first watch false-`DEQUEUED` when the counter started from the seed).
131
+ - `WAITING`: membership was observed, then one readable absence — re-read suspicion, not terminal.
132
+ - `STALLED`: no observable queue/admission state has changed for the configured stall bound. Terminal hand-back, not a merge failure. The bound is **phase-aware**: pre-admission (never observed membership this process) uses the shorter `--admission-stall-minutes` (default 3) and reports `reason: "admission-stall"`, unless a non-Mergify check is still pending: CI running is progress, so the full `--stall-minutes` (default 45) applies. After membership has been observed, the full bound applies and reports `reason: "stall-bound"`. Same terminal outcome and exit code either way — only wall time and `reason` change. A dead admission path (forgotten enqueue, stale Mergify status, unreadable queue API) must not burn the post-entry bound.
133
+
134
+ Before queue membership, a completed failing non-Mergify GitHub check settles
135
+ immediately as `STALLED` with `reason: "admission-checks-failed"` and a
136
+ `failedChecks` summary. This is not `DEQUEUED`: the PR was never admitted.
137
+ Mergify's own queue/protection checks are excluded, pending/neutral/skipped
138
+ checks keep watching, and after membership Mergify remains the lifecycle
139
+ authority for failed batches and dequeue routing.
140
+
141
+ The critical invariant: after a watcher has **observed** queue entry in this process (`everActive`), it must never keep reporting `queued:false` as a routine healthy snapshot. If the PR did not merge, loss of queue membership is a queue exit. Seeding `seenQueued` via `--seen-queued` before the first successful membership poll must not invent that observation.
142
+
143
+ ## Re-read Rule
144
+
145
+ GitHub can briefly expose neither `queued` nor `dequeued` during a label transition. Treat a single missing-label read **after process-local membership** as suspicion, then re-read. Two consecutive missing active-membership reads settle the transition as `DEQUEUED` unless the PR merged in the meantime. Absences before `everActive` stay `NOT_YET_QUEUED`.
146
+
147
+ ## Dequeue Reason
148
+
149
+ Record both fields when the queue gives you a reason:
150
+
151
+ - `rawReason`: the provider's exact string or visible status text
152
+ - `reason`: the routing class used by the caller
153
+
154
+ Suggested routing classes:
155
+
156
+ | raw reason shape | route |
157
+ |---|---|
158
+ | batch check `FAILURE`, `ERROR`, or `TIMED_OUT` | `checks-failed`; fix round, fresh verdict, new enqueue |
159
+ | batch checks cancelled before validation executes | `checks-cancelled`; report the cancelled checks and hand back so an unchanged-SHA requeue can be considered |
160
+ | `pull-request-updated`, `draft-pull-request-changed` | HEAD moved; verdict stale; re-enter delivery after fresh validation |
161
+ | `conflict`, `conflict-with-base-branch` | rebase, regenerate generated files instead of hand-merging them, re-run checks, push, then `@mergifyio queue`. Still "Waiting for `-conflict`" while GitHub says `MERGEABLE`: comment `@mergifyio refresh` |
162
+ | `conditions-unmet`, `frozen`, `manual`, `pr-manually-dequeued`, `checks-timeout` | hand back with the condition/reason quoted |
163
+ | unknown | `unknown`; hand back, fail closed, and preserve `rawReason` |
164
+
165
+ When the reason is not machine-readable, quote the Mergify status comment, queue show condition leaf, or event-log URL in `rawReason` rather than inventing a reason.
166
+
167
+ The selected `state:"dequeued"` Mergify status comment is parsed as structured evidence:
168
+ `dequeuedAtMs`, `rawReason`, its draft PR fallback, and provider-reported failing checks. On
169
+ dequeue, the watcher queries the retained batch PR once and attaches its failed or cancelled
170
+ `statusCheckRollup` entries to the terminal verdict. A missing or deleted batch PR remains a
171
+ truthful `DEQUEUED`: `batchPr.readable` is false, the retained number and `rawReason` survive,
172
+ and any checks named by the provider comment remain available. An unchanged-SHA requeue can
173
+ produce a new synthetic PR; every readable active observation replaces the retained number,
174
+ so the newest batch wins over stale comment fallback.
175
+
176
+ ## Watcher Requirements
177
+
178
+ - Run as one background task or one script invocation; do not spend foreground turns polling.
179
+ - Retry transient `gh`, GraphQL, and Mergify CLI failures. EOF, TLS handshake timeouts, and temporary API failures are not dequeue signals — an unreadable read is *no information*, not a "not queued" observation, and must not advance the counter that settles `DEQUEUED`. Retain that source's last readable stall-state contribution instead; a readable sibling can still report genuine progress and reset the change clock.
180
+ - Bound every subprocess read on its own timeout, in addition to the stall/ceiling bounds below — those are only evaluated after a poll returns, so an unbounded read can hang the watcher past both of them.
181
+ - **Run the bundled script. Do not hand-roll a watcher** — see the progression section above for why the hand-rolled shape cannot hold the contract, whatever care goes into the code. (If you are reading a legacy shell watcher: avoid shell-reserved variable names such as `status`.)
182
+ - Keep enqueue and watch separate. A watch observes only; it does not post `@mergifyio queue`, `@mergifyio dequeue`, or merge.
183
+ - Confirm the PR is queued, or can become queued, *before* launching the watch — see [Enqueue Before You Watch](#enqueue-before-you-watch). A watch that cannot enqueue, on a PR nothing else will enqueue either, can only end `STALLED`.
184
+ - Emit an observation record every poll, not only on the terminal verdict. `NOT_YET_QUEUED` and `QUEUED` are both non-terminal and cover the entire healthy path, so a watcher that speaks only at the end is silent for essentially its whole life — indistinguishable, to the caller trying to decide whether delivery is progressing, from a hung or dead process. Keep the heartbeat off the machine-contract stream: stdout carries exactly the one terminal JSON document callers parse, stderr carries the per-poll records.
185
+ - Bound on stalled state, not total elapsed time alone. `waiting_for_previous_batches` can be slow but healthy if position/state changes.
186
+
187
+ ## Script
188
+
189
+ Use the bundled watcher for simple PR delivery checks:
190
+
191
+ ```bash
192
+ node ~/.agents/skills/mergify/scripts/watch-pr-delivery.mjs \
193
+ --repo <owner>/<repo> \
194
+ --pr 1302
195
+ ```
196
+
197
+ Options:
198
+
199
+ | flag | default | meaning |
200
+ |---|---:|---|
201
+ | `--interval-seconds` | `90` | poll interval |
202
+ | `--stall-minutes` | `45` | post-entry terminal stall bound — no observable delivery-state change for this long *after queue membership has been observed* is `STALLED` with `reason: "stall-bound"`. A *no-change* bound, not elapsed time: any movement in position, label, or queue state resets it, so a slow-but-healthy queue is never cut off by it. A running queue check changes nothing it can see until the run ends, so the bound must exceed the repo's queue CI duration (20-48 min on this fleet) |
203
+ | `--admission-stall-minutes` | `3` | pre-admission terminal stall bound — no observable change *before this process has observed membership* is `STALLED` with `reason: "admission-stall"`. Shorter on purpose: after enqueue, queue entry should appear quickly, and a dead admission path must not wait out the full post-entry bound. Capped at `--stall-minutes` so a caller that shortens the post-entry bound keeps both phases tight. Applies only while no non-Mergify check is pending; while CI runs, the post-entry bound applies, so a CI-gated admission needs no relaunch |
204
+ | `--max-minutes` | `60` | absolute safety ceiling; bounds the watch even while the queue keeps reporting change, so a busy queue whose position keeps moving cannot keep the stall clock resetting forever. **60 minutes is also a hard maximum** — a larger value exits `2` rather than being silently clamped, because a caller that believes it has a four-hour watch and has one hour is worse off than one told plainly to relaunch. An hour without a verdict is a queue to hand back to a human or relaunch. The sibling `resolve-issues` watcher enforces the same ceiling; a cap honoured by one of two watchers is not a cap |
205
+ | `--slow-job-minutes` | `5` | warn when a single CI job runs longer than this (see [CI profile](#ci-profile)) |
206
+ | `--quiet` | off | suppress the per-poll progress heartbeat on stderr (slow-job warnings and the closing profile summary still print) |
207
+
208
+ Output streams:
209
+
210
+ | stream | content |
211
+ |---|---|
212
+ | stdout | exactly one pretty-printed terminal JSON verdict, at exit — carrying `phases`, `seenQueued`, the outcome, and `ciProfile` |
213
+ | stderr | one compact JSON record per poll (`progress: true`), with the live outcome, PR labels, queue membership/position, `phases`, `changed`, and `sinceChangeMs` — the stall clock made visible; plus one `{"warning":"slow-ci-job"}` record per slow CI job; and, last, one `{"ciProfileSummary":true}` line after the verdict |
214
+
215
+ Each regular-poll subprocess read (`gh pr view`, `mergify queue show`) also carries its own bounded
216
+ timeout — a small multiple of `--interval-seconds`, clamped to never exceed the **effective
217
+ phase stall bound** (pre-admission: `--admission-stall-minutes`; post-entry: `--stall-minutes`)
218
+ capped by `--max-minutes`, then divided by the number of reads made per poll (2 here:
219
+ `gh pr view` + `mergify queue show`). The two reads together cannot outlive the bound that will
220
+ actually fire `STALLED` on this poll — so a hung pre-admission poll cannot outlive the short
221
+ admission clock, and a post-entry poll is not permanently clamped to the admission value.
222
+ At the end of the watch, one additional bounded `gh pr view` reads the synthetic batch PR, when
223
+ one was observed: after a dequeue to classify it, and on every terminal verdict to profile the
224
+ queue's CI (a single read serves both). It enriches the already-settled verdict and never participates
225
+ in queue membership, the absence counter, or the stall clock.
226
+ Those stall/ceiling bounds are only evaluated after a poll returns, and a single hung read
227
+ cannot alone consume more than half of a small stall/ceiling bound. (The sibling watcher makes
228
+ one read per poll, so its clamp divides by 1 and the single-read guarantee is the whole bound
229
+ there.)
230
+
231
+ The `--interval-seconds` default is already set for scale. Two API calls per poll at 90s is
232
+ 80/hour per watched PR — roughly 60 concurrent watchers before an authenticated GitHub token's
233
+ 5,000/hour limit is in range, which comfortably covers an epic-sized `resolve-issues` delivery.
234
+ (At the former 30s default it was 240/hour, or about 20 watchers.) The cost is at most 90s of
235
+ extra merge-detection latency against a queue that takes minutes per batch — a cheap trade.
236
+ Shorten it only for a single foreground watch where that latency actually matters.
237
+
238
+ Exit codes:
239
+
240
+ | code | meaning |
241
+ |---:|---|
242
+ | 0 | merged; JSON includes `outcome:"MERGED"` and `mergedSha` |
243
+ | 4 | terminal but not merged: `DEQUEUED`, `CLOSED_UNMERGED`, or `STALLED` |
244
+ | 2 | usage error |
245
+
246
+ The script emits JSON on stdout. The caller is responsible for recording the delivery attempt/outcome and proving ancestry for `MERGED`.
247
+
248
+ ## CI profile
249
+
250
+ Every watch also profiles the CI it waited on, at no extra poll cost: each `gh pr view` already
251
+ returns the PR's check runs with their start and finish times.
252
+
253
+ - **Jobs.** Every non-Mergify check run on the PR, plus the checks on the queue's batch PR (read
254
+ once at the end). Mergify's own checks are left out; their duration is queue wait, not CI work.
255
+ A job still running is timed up to now, so a slow job shows up while it is still running.
256
+ - **Slow-job warning.** A job that runs longer than `--slow-job-minutes` (default 5) gets one
257
+ `{"warning":"slow-ci-job","name",…,"durationSec","thresholdSec","detailsUrl"}` line on stderr,
258
+ once per job run, even under `--quiet`.
259
+ - **Verdict field.** The terminal JSON carries `ciProfile`: `verdict` (`ok`, `slow`, or
260
+ `no-timing-data`), `slowestJob`, `slowJobs`, every job's `durationSec` slowest-first, and
261
+ `phases` with how long the PR spent in each lifecycle phase (waiting for admission, in the
262
+ queue).
263
+
264
+ - **Closing summary line.** After the verdict, the last stderr line is
265
+ `{"ciProfileSummary":true,"outcome","verdict","jobCount","slowJobMinutes","slowest","slowJobs"}`,
266
+ with at most three slow jobs named as `"<name> <sec>s"`. The verdict is pretty-printed over
267
+ many lines, so a short log tail lands inside `phases` and misses the profile; this line is
268
+ what a 10-line tail shows. A background runner that reports only the exit code (Pi's
269
+ `bg_task_status`) shows neither: read the log tail (`bg_task_log`) when the watch ends, and
270
+ report the profile. Identity #894 (2026-09-29) warned about a 338s job that no one read.
271
+
272
+ Profiling is observation only: it never changes the outcome or the exit code. A slow-job
273
+ warning is a lead for whoever owns the CI (split the job, cache, move it to a nightly lane) —
274
+ the `github-actions` skill covers those fixes.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const DAY_MS = 24 * 60 * 60 * 1000;
9
+ const WEEK_MS = 7 * DAY_MS;
10
+ const here = dirname(fileURLToPath(import.meta.url));
11
+
12
+ function readJson(path, fallback = {}) {
13
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return fallback; }
14
+ }
15
+
16
+ function frontmatterValue(text, key) {
17
+ const frontmatter = /^---\n([\s\S]*?)\n---/.exec(text)?.[1] ?? '';
18
+ const match = new RegExp('^' + key + ':\\s*([^\\n]+)$', 'm').exec(frontmatter);
19
+ return match?.[1].trim().replace(/^['"]|['"]$/g, '') ?? null;
20
+ }
21
+
22
+ function repositoryOf(source) {
23
+ const value = String(source ?? '').trim();
24
+ const match = /^(?:git@github\.com:|https:\/\/github\.com\/)?([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/.exec(value);
25
+ return match?.[1] ?? null;
26
+ }
27
+
28
+ function stableVersion(value) {
29
+ const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(String(value ?? '').trim());
30
+ return match ? match.slice(1).map(Number) : null;
31
+ }
32
+
33
+ function newer(left, right) {
34
+ for (let index = 0; index < 3; index += 1) {
35
+ if (left[index] !== right[index]) return left[index] > right[index];
36
+ }
37
+ return false;
38
+ }
39
+
40
+ function writeCache(path, value) {
41
+ try {
42
+ mkdirSync(dirname(path), { recursive: true });
43
+ writeFileSync(path, JSON.stringify(value));
44
+ } catch { /* A read-only cache must not block skill use. */ }
45
+ }
46
+
47
+ function main(now = Date.now()) {
48
+ if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
+ const name = frontmatterValue(skill, 'name');
51
+ const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
52
+ if (!name || !localVersion) return;
53
+
54
+ const lock = readJson(join(process.env.HOME || homedir(), '.agents', '.skill-lock.json'));
55
+ const entry = lock.skills?.[name];
56
+ const repository = repositoryOf(entry?.sourceUrl || entry?.source);
57
+ if (!repository) return;
58
+
59
+ const cacheRoot = process.env.XDG_CACHE_HOME || join(process.env.HOME || homedir(), '.cache');
60
+ const cachePath = join(cacheRoot, '1aboveio-skills', 'release-check.json');
61
+ const all = readJson(cachePath);
62
+ const cache = all[repository] && typeof all[repository] === 'object' ? all[repository] : {};
63
+
64
+ if (!cache.lastCheck || now - cache.lastCheck >= DAY_MS) {
65
+ const result = spawnSync('gh', ['api', 'repos/' + repository + '/releases/latest', '--jq', '.tag_name'], {
66
+ encoding: 'utf8',
67
+ stdio: ['ignore', 'pipe', 'ignore'],
68
+ timeout: 1500,
69
+ });
70
+ cache.lastCheck = now;
71
+ if (result.status === 0 && stableVersion(result.stdout)) cache.latestTag = result.stdout.trim();
72
+ else delete cache.latestTag;
73
+ all[repository] = cache;
74
+ writeCache(cachePath, all);
75
+ }
76
+
77
+ const latestVersion = stableVersion(cache.latestTag);
78
+ if (!latestVersion || !newer(latestVersion, localVersion)) return;
79
+ if (cache.notifiedTag === cache.latestTag && cache.notifiedAt && now - cache.notifiedAt < WEEK_MS) return;
80
+
81
+ cache.notifiedTag = cache.latestTag;
82
+ cache.notifiedAt = now;
83
+ all[repository] = cache;
84
+ writeCache(cachePath, all);
85
+ const command = `npx skills@1.5.23 add '${repository}#${cache.latestTag}' --global --agent universal claude-code --skill ${name} --yes`;
86
+ process.stdout.write(
87
+ `UPDATE_AVAILABLE: A newer 1AboveIO skills release (${cache.latestTag}) is available; this skill is `
88
+ + `v${localVersion.join('.')}. Ask once whether to refresh ${name}. On approval run \`${command}\`. `
89
+ + 'The refreshed instructions apply next session. Either way, continue the current task and do not ask again this session.\n',
90
+ );
91
+ }
92
+
93
+ try { main(); } catch { /* Update checks are advisory and always fail silent. */ }
@@ -0,0 +1,208 @@
1
+ // Import-only core for auditing a repo's .mergify.yml against this skill's best-practice default.
2
+ //
3
+ // node audit.mjs [--file .mergify.yml] [--workflows .github/workflows] [--allow-warn] [--json]
4
+ //
5
+ // Like ensure-coverage's ci-audit.mjs, this is a BENCHMARK THAT ABSTAINS, not a YAML
6
+ // parser (the repo ships no YAML dependency). Every check works on the raw text and
7
+ // returns `abstain` when it cannot decide, rather than risk a false verdict. A check
8
+ // that can't find the structure it needs is reported `abstain`, never `pass`.
9
+ //
10
+ // Exit: 2 if any FAIL, 1 if any WARN (unless --allow-warn), else 0.
11
+ import fs from 'node:fs'
12
+ import path from 'node:path'
13
+
14
+ // The lines indented UNDER a `key:` (by the first child's indent), or null if the key
15
+ // is absent. Enough structure-awareness to scope a check to one block without a parser.
16
+ export function blockUnder(text, key) {
17
+ const lines = text.split('\n')
18
+ const i = lines.findIndex((l) => new RegExp(`^(\\s*)${key}\\s*:`).test(l))
19
+ if (i < 0) return null
20
+ const keyIndent = lines[i].match(/^(\s*)/)[1].length
21
+ const out = []
22
+ for (let j = i + 1; j < lines.length; j++) {
23
+ const l = lines[j]
24
+ if (l.trim() === '' || l.trim().startsWith('#')) { out.push(l); continue }
25
+ const ind = l.match(/^(\s*)/)[1].length
26
+ if (ind <= keyIndent) break
27
+ out.push(l)
28
+ }
29
+ return out.join('\n')
30
+ }
31
+
32
+ // Strip `#` comments so a check never trips on prose that merely NAMES a bad pattern.
33
+ const uncommented = (text) => text.split('\n').map((l) => l.replace(/(^|\s)#.*$/, '')).join('\n')
34
+
35
+ // The base branches a workflow's `pull_request` trigger is limited to, or null when it runs for any
36
+ // base (no `branches:` filter, or one that matches every branch). Text-level, like the rest.
37
+ export function pullRequestBranches(raw) {
38
+ const text = uncommented(raw)
39
+ const on = blockUnder(text, 'on') ?? ''
40
+ const inline = text.match(/^on\s*:\s*(.+)$/m)?.[1] ?? ''
41
+ if (!/pull_request\b(?!_target)/.test(on + inline)) return null
42
+ const flow = (on + inline).match(/pull_request\s*:\s*\{[^}]*branches\s*:\s*\[([^\]]*)\]/)?.[1]
43
+ const pr = blockUnder(on, 'pull_request')
44
+ // Any branches-ignore skips some base, and a stacked PR's base is a branch like any other.
45
+ const ignored = (on + inline).match(/pull_request\s*:\s*\{[^}]*branches-ignore\s*:\s*\[([^\]]*)\]/)?.[1]
46
+ ?? pr?.match(/^\s*branches-ignore\s*:\s*\[([^\]]*)\]/m)?.[1]
47
+ ?? ((blockUnder(pr ?? '', 'branches-ignore') ?? '').split('\n').map((l) => l.match(/^\s*-\s*(.+)$/)?.[1]).filter(Boolean).join(',') || null)
48
+ if (ignored) return ignored.split(',').map((b) => `!${b.trim().replace(/^['"]|['"]$/g, '')}`).filter((b) => b !== '!')
49
+ const listed = flow ?? pr?.match(/^\s*branches\s*:\s*\[([^\]]*)\]/m)?.[1]
50
+ ?? (blockUnder(pr ?? '', 'branches') ?? '').split('\n').map((l) => l.match(/^\s*-\s*(.+)$/)?.[1]).filter(Boolean).join(',')
51
+ if (!listed) return null
52
+ const branches = listed.split(',').map((b) => b.trim().replace(/^['"]|['"]$/g, '')).filter(Boolean)
53
+ return branches.some((b) => b === '**' || b === '*') ? null : branches
54
+ }
55
+
56
+ export function auditText(raw, { workflows } = {}) {
57
+ const text = uncommented(raw)
58
+ const f = []
59
+ const add = (check, status, detail) => f.push({ check, status, detail })
60
+
61
+ // 1. Deprecated auto-queue forms — decidable, always wrong.
62
+ if (/\bauto_merge\s*:\s*true/.test(text)) add('deprecated-forms', 'fail', '`auto_merge: true` is deprecated — use auto_merge_conditions (configuration.md §1).')
63
+ else if (/\bautoqueue\s*:/.test(text)) add('deprecated-forms', 'fail', '`queue_rules[].autoqueue` is deprecated — use auto_merge_conditions (configuration.md §1).')
64
+ else add('deprecated-forms', 'pass', 'no deprecated auto_merge/autoqueue.')
65
+
66
+ // 2. Enqueue policy: hybrid (auto scoped to a passed review) is the default.
67
+ const amc = blockUnder(text, 'auto_merge_conditions')
68
+ if (amc == null) {
69
+ if (/auto_merge_conditions\s*:/.test(text)) add('enqueue-policy', 'abstain', 'auto_merge_conditions present but its block could not be read — check by hand.')
70
+ else add('enqueue-policy', 'note', 'no auto_merge_conditions → fully MANUAL enqueue. OK if deliberate (say so in a comment); the default is hybrid (configuration.md §1).')
71
+ } else if (!/approved-reviews-by/.test(amc)) {
72
+ add('enqueue-policy', 'warn', 'auto_merge_conditions has no review arm (`approved-reviews-by`) — this is the retired indiscriminate form; scope auto-enqueue to a passed review (configuration.md §1).')
73
+ } else if (!/changes-requested-reviews-by/.test(amc)) {
74
+ add('enqueue-policy', 'warn', 'auto_merge_conditions approves without also requiring `#changes-requested-reviews-by = 0` — an open change request could be out-voted; add it (configuration.md §1).')
75
+ } else {
76
+ add('enqueue-policy', 'pass', 'hybrid: auto-enqueue scoped to a passed review.')
77
+ }
78
+
79
+ // 3. branch_protection_injection_mode: merge (only meaningful with a queue).
80
+ if (!/queue_rules\s*:/.test(text)) add('injection-mode', 'abstain', 'no queue_rules found — not a queue config?')
81
+ else if (/branch_protection_injection_mode\s*:\s*merge/.test(text)) add('injection-mode', 'pass', 'branch_protection_injection_mode: merge.')
82
+ else add('injection-mode', 'warn', 'branch_protection_injection_mode is not `merge` — a required Mergify check can then block queue ENTRY (traps.md).')
83
+
84
+ // 4. Gate defined once (anchor) and asserted in multiple places.
85
+ if (!/&gate\b/.test(text)) add('gate-anchor', 'abstain', 'no `&gate` anchor — cannot check the define-once/assert-thrice shape.')
86
+ else {
87
+ const refs = (text.match(/\*gate\b/g) || []).length
88
+ if (refs >= 2) add('gate-anchor', 'pass', `gate anchor referenced ${refs}× (define once, assert many).`)
89
+ else add('gate-anchor', 'warn', `gate anchor referenced only ${refs}× — assert it in queue_conditions AND merge_conditions (and the merge protection), not hand-copied.`)
90
+ }
91
+
92
+ // 5. `-draft` must NOT sit in queue_conditions/merge_conditions (epic-986 incident).
93
+ // It belongs in auto_merge_conditions only.
94
+ for (const key of ['queue_conditions', 'merge_conditions']) {
95
+ const block = blockUnder(text, key)
96
+ if (block == null) continue
97
+ if (/(^|\s)-\s*-draft\b|(^|\s)-draft\b/.test(block)) {
98
+ add('draft-placement', 'warn', `\`-draft\` appears in ${key} — GitHub can't merge a draft anyway, and a queued draft strands the whole batch (epic-986). Keep -draft in auto_merge_conditions only (traps.md, \`-draft\` note).`)
99
+ }
100
+ }
101
+ if (!f.some((x) => x.check === 'draft-placement')) add('draft-placement', 'pass', '-draft not misplaced in queue/merge conditions.')
102
+
103
+ // 6. Batch mode is on per this skill's default. The standard is `batch_size: 3`,
104
+ // written explicitly: unset, `batch_size` silently runs at Mergify's built-in
105
+ // default of 1 (no batching), so the absence reads as a silent no-batch, not a
106
+ // batched default. Dynamic {min, max} is batching too (configuration.md §2-3).
107
+ const qr = blockUnder(text, 'queue_rules')
108
+ if (qr == null) {
109
+ add('batch-mode', 'abstain', 'no queue_rules found — nothing to batch.')
110
+ } else {
111
+ const inline = qr.match(/^\s*batch_size\s*:\s*(\S.*)$/m)?.[1]
112
+ const under = blockUnder(qr, 'batch_size')
113
+ const bs = inline ?? (under != null && under.trim() !== '' ? under : null)
114
+ if (bs == null) add('batch-mode', 'warn', 'no `batch_size` on the queue rule — it silently runs at Mergify\'s built-in 1, so batches NEVER form; set batch_size: 3 (this skill\'s default), or say deliberately why not (configuration.md §2-3).')
115
+ else if (/^\s*min\s*:/.test(bs)) add('batch-mode', 'pass', 'dynamic batch_size {min, max} — batching on.')
116
+ else if (/^\s*[2-9]\d*\s*$/.test(bs.trim())) add('batch-mode', 'pass', `batch_size ${bs.trim()} — batching on.`)
117
+ else add('batch-mode', 'warn', `batch_size: ${bs.trim()} — batches never form (must be > 1); raise it to 3 (this skill's default), or say deliberately why not (configuration.md §2-3).`)
118
+ }
119
+
120
+ // 7. No checks timeout. Unset means `auto` (p95 of recent runs + margin): a queue run
121
+ // that only waits for busy runners crosses it and green PRs get dequeued and bisected
122
+ // (2026-10-10). Every queue rule needs `checks_timeout: null`, set on the rule or in
123
+ // `defaults.queue_rule` (configuration.md, "Set no checks timeout").
124
+ if (qr == null) {
125
+ add('checks-timeout', 'abstain', 'no queue_rules found — no checks timeout to check.')
126
+ } else {
127
+ const defaults = blockUnder(blockUnder(text, 'defaults') ?? '', 'queue_rule')
128
+ const defaultValue = defaults?.match(/^\s*checks_timeout\s*:\s*(\S.*?)\s*$/m)?.[1]
129
+ const rules = qr.split(/^(?=\s*-\s+name\s*:)/m).filter((r) => /^\s*-\s+name\s*:/.test(r))
130
+ const capped = []
131
+ for (const rule of rules) {
132
+ const name = rule.match(/name\s*:\s*(\S+)/)[1]
133
+ const value = rule.match(/^\s*checks_timeout\s*:\s*(\S.*?)\s*$/m)?.[1] ?? defaultValue
134
+ if (value !== 'null' && value !== '~') capped.push(`${name} (${value ?? 'unset = auto'})`)
135
+ }
136
+ if (rules.length === 0) add('checks-timeout', 'abstain', 'no named queue rule could be read — check checks_timeout by hand.')
137
+ else if (capped.length) add('checks-timeout', 'warn', `queue rule(s) with a checks timeout: ${capped.join(', ')} — a run slowed by busy runners gets green PRs dequeued and bisected; set checks_timeout: null and let each job's timeout-minutes end a hang (configuration.md, "Set no checks timeout").`)
138
+ else add('checks-timeout', 'pass', 'every queue rule sets checks_timeout: null.')
139
+ }
140
+
141
+ // 8. The queue keeps the PR's commits. Squash and rebase rewrite them, so a PR built on another
142
+ // conflicts once its parent lands: in a Depends-On stack or a chained PR, not a GitHub-native
143
+ // stack, which Mergify rebases itself. A note, not a warning (configuration.md, "Stack-ready").
144
+ if (qr == null) {
145
+ add('merge-method', 'abstain', 'no queue_rules found — no merge method to check.')
146
+ } else {
147
+ const rules = qr.split(/^(?=\s*-\s+name\s*:)/m).filter((r) => /^\s*-\s+name\s*:/.test(r))
148
+ const methods = rules.map((r) => [r.match(/name\s*:\s*(\S+)/)[1], r.match(/^\s*merge_method\s*:\s*(\S+)/m)?.[1]?.replace(/^['"]|['"]$/g, '')])
149
+ const rewriting = methods.filter(([, m]) => m === 'squash' || m === 'rebase')
150
+ const unknown = methods.filter(([, m]) => m !== undefined && !['merge', 'merge-batch', 'fast-forward', 'squash', 'rebase'].includes(m))
151
+ if (rules.length === 0) add('merge-method', 'abstain', 'no named queue rule could be read — check merge_method by hand.')
152
+ else if (unknown.length && !rewriting.length) add('merge-method', 'abstain', `unrecognized merge_method: ${unknown.map(([n, m]) => `${n} (${m})`).join(', ')} — check by hand that it keeps the PR's commits.`)
153
+ else if (rewriting.length) add('merge-method', 'note', `queue rule(s) that rewrite commits: ${rewriting.map(([n, m]) => `${n} (${m})`).join(', ')} — fine for GitHub-native stacks; a Depends-On stack (--no-github-native) or a resolve-issues chain conflicts after its parent lands, so use merge-batch (batch_size > 1) or merge for those (configuration.md, "Stack-ready").`)
154
+ else add('merge-method', 'pass', 'every queue rule keeps the PR\'s commits.')
155
+ }
156
+
157
+ // 9. CI runs on stacked PRs: a `pull_request` trigger filtered to the base branch never runs on a
158
+ // PR based on another PR's branch, so a Depends-On stack gets no CI. A GitHub-native stack runs
159
+ // CI as if each PR targets the stack base, so this is a note (configuration.md, "Stack-ready").
160
+ if (!workflows) {
161
+ add('stacked-pr-ci', 'abstain', 'no workflows read — pass --workflows <dir> to check that CI runs on stacked PRs.')
162
+ } else {
163
+ const filtered = workflows.map((w) => [w.name, pullRequestBranches(w.text)]).filter(([, b]) => b)
164
+ if (!workflows.length) add('stacked-pr-ci', 'abstain', 'no workflow files found — check the triggers behind the required checks by hand.')
165
+ else if (filtered.length) add('stacked-pr-ci', 'note', `pull_request limited to base branches in: ${filtered.map(([n, b]) => `${n} [${b.join(', ')}]`).join(', ')} — fine for GitHub-native stacks; a Depends-On stack (--no-github-native) gets no CI from these, so drop the branches filter (or use "**") on every workflow behind a required check if you use one (configuration.md, "Stack-ready").`)
166
+ else add('stacked-pr-ci', 'pass', 'pull_request triggers run for PRs on any base branch.')
167
+ }
168
+
169
+ return f
170
+ }
171
+
172
+ function verdict(findings) {
173
+ if (findings.some((x) => x.status === 'fail')) return 'FAIL'
174
+ if (findings.some((x) => x.status === 'warn')) return 'WARN'
175
+ return 'PASS'
176
+ }
177
+
178
+ export function runCli(argv) {
179
+ const flags = { file: '.mergify.yml', json: false, allowWarn: false, workflows: undefined }
180
+ for (let i = 0; i < argv.length; i++) {
181
+ const a = argv[i]
182
+ if (a === '--file') flags.file = argv[++i]
183
+ else if (a === '--workflows') flags.workflows = argv[++i]
184
+ else if (a === '--json') flags.json = true
185
+ else if (a === '--allow-warn') flags.allowWarn = true
186
+ }
187
+ if (!fs.existsSync(flags.file)) {
188
+ console.error(`mergify-audit: no config at ${flags.file} (pass --file <path>).`)
189
+ return 2
190
+ }
191
+ // Workflows: --workflows, else .github/workflows beside the config when it exists.
192
+ const dir = flags.workflows ?? path.join(path.dirname(path.resolve(flags.file)), '.github', 'workflows')
193
+ const workflows = fs.existsSync(dir) && fs.statSync(dir).isDirectory()
194
+ ? fs.readdirSync(dir).filter((n) => /\.ya?ml$/.test(n)).sort().map((n) => ({ name: n, text: fs.readFileSync(path.join(dir, n), 'utf8') }))
195
+ : undefined
196
+ const findings = auditText(fs.readFileSync(flags.file, 'utf8'), { workflows })
197
+ const v = verdict(findings)
198
+ if (flags.json) {
199
+ console.log(JSON.stringify({ file: flags.file, verdict: v, findings }, null, 2))
200
+ } else {
201
+ const glyph = { pass: '✓', note: '·', abstain: '?', warn: '⚠', fail: '✗' }
202
+ for (const x of findings) console.log(`${glyph[x.status] || '?'} [${x.status}] ${x.check}: ${x.detail}`)
203
+ console.log(`\n${v} — ${flags.file}`)
204
+ }
205
+ if (v === 'FAIL') return 2
206
+ if (v === 'WARN') return flags.allowWarn ? 0 : 1
207
+ return 0
208
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './audit-core.mjs'
3
+
4
+ process.exitCode = runCli(process.argv.slice(2))