@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,748 @@
1
+ from abc import ABC, abstractmethod
2
+ from argparse import ArgumentParser
3
+ from contextlib import contextmanager
4
+ from dataclasses import dataclass
5
+ import enum
6
+ import logging
7
+ import os
8
+ import sys
9
+ import threading
10
+ from typing import List, Dict, Union, Literal, Optional
11
+
12
+ from dateutil.relativedelta import relativedelta
13
+ import pandas as pd
14
+ from pyspark.sql import SparkSession, DataFrame
15
+ from pyspark.sql.functions import *
16
+ from pyspark.sql.types import *
17
+ from pyspark.sql.utils import AnalysisException
18
+ import typer
19
+ from typing_extensions import Annotated
20
+
21
+ from utils.hudi_metadata import hudi_metadata_write_options
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class HudiLayout:
28
+ """Target on-disk layout. Do not pair with Spark/Hudi shuffle parallelism."""
29
+
30
+ files_per_partition: int = 1
31
+ target_file_size_mb: int = 128
32
+
33
+ def write_options(self) -> Dict[str, Union[str, int]]:
34
+ target_bytes = int(self.target_file_size_mb) * 1024 * 1024
35
+ group_bytes = target_bytes * max(1, int(self.files_per_partition))
36
+ return {
37
+ "hoodie.parquet.max.file.size": str(target_bytes),
38
+ "hoodie.parquet.small.file.limit": str(target_bytes),
39
+ "hoodie.copyonwrite.insert.split.size": str(target_bytes),
40
+ "hoodie.clustering.plan.strategy.target.file.max.bytes": str(target_bytes),
41
+ "hoodie.clustering.plan.strategy.max.bytes.per.group": str(group_bytes),
42
+ }
43
+
44
+
45
+ _SPARK_LEASE_LOCK = threading.RLock()
46
+ _SPARK_LEASES = {}
47
+
48
+
49
+ def _classic_spark_runtime_exists() -> bool:
50
+ from pyspark import SparkContext
51
+
52
+ session = (
53
+ SparkSession.getActiveSession()
54
+ or getattr(SparkSession, "_instantiatedSession", None)
55
+ )
56
+ if (
57
+ session is not None
58
+ and getattr(getattr(session, "_sc", None), "_jsc", None) is not None
59
+ ):
60
+ return True
61
+ context = getattr(SparkContext, "_active_spark_context", None)
62
+ return context is not None and getattr(context, "_jsc", None) is not None
63
+
64
+
65
+ def _connect_spark_runtime_exists() -> bool:
66
+ module = sys.modules.get("pyspark.sql.connect.session")
67
+ connect_session = getattr(module, "SparkSession", None) if module else None
68
+ if connect_session is None:
69
+ return False
70
+ return (
71
+ connect_session.getActiveSession() is not None
72
+ or getattr(connect_session, "_default_session", None) is not None
73
+ )
74
+
75
+
76
+ def _spark_runtime_exists(spark_remote: Optional[str]) -> bool:
77
+ connect_mode = bool(
78
+ spark_remote
79
+ or os.environ.get("SPARK_REMOTE")
80
+ or os.environ.get("SPARK_CONNECT_MODE_ENABLED")
81
+ )
82
+ return (
83
+ _connect_spark_runtime_exists()
84
+ if connect_mode
85
+ else _classic_spark_runtime_exists()
86
+ )
87
+
88
+
89
+ def create_spark_session(spark_remote: Optional[str] = None) -> SparkSession:
90
+ """Create a Spark session in Spark Connect or classic spark-submit mode."""
91
+ builder = SparkSession.builder
92
+ if spark_remote:
93
+ builder = builder.remote(spark_remote)
94
+ return builder.getOrCreate()
95
+
96
+
97
+ @contextmanager
98
+ def managed_spark_session(
99
+ spark_remote: Optional[str] = None,
100
+ *,
101
+ builder=None,
102
+ ):
103
+ """Lease a Spark session and stop only a runtime created by this helper."""
104
+ with _SPARK_LEASE_LOCK:
105
+ runtime_existed = _spark_runtime_exists(spark_remote)
106
+ spark = (
107
+ builder.getOrCreate()
108
+ if builder is not None
109
+ else create_spark_session(spark_remote)
110
+ )
111
+ key = id(spark)
112
+ state = _SPARK_LEASES.get(key)
113
+ if state is None:
114
+ state = {
115
+ "leases": 0,
116
+ "helper_owned": not runtime_existed,
117
+ }
118
+ _SPARK_LEASES[key] = state
119
+ state["leases"] += 1
120
+
121
+ try:
122
+ yield spark
123
+ finally:
124
+ with _SPARK_LEASE_LOCK:
125
+ state = _SPARK_LEASES.get(key)
126
+ if state is None:
127
+ return
128
+ state["leases"] -= 1
129
+ if state["leases"] == 0:
130
+ del _SPARK_LEASES[key]
131
+ if state["helper_owned"]:
132
+ spark.stop()
133
+
134
+
135
+ class Etl(ABC):
136
+ """
137
+ The base class for ETL jobs.
138
+
139
+ Attributes:
140
+ ----------
141
+ id: str
142
+ field name of the unique key, required in Hudi table
143
+ ts: str
144
+ field name of the record unique timestamp, used as Hudi `preCombine` field
145
+ filter_by: str
146
+ field name filtering in `extract` function
147
+
148
+ Methods:
149
+ -------
150
+ extract(start_date: str, end_date: str):
151
+ extract table from <`src_db`.`src_tbl`>
152
+
153
+ transform(df: DataFrame):
154
+ performs transformation to df
155
+
156
+ load(df):
157
+ load into <`dst_db`.`dst_tbl`> as `spark_table` or `hudi_table` based on the `table_type` setting
158
+ """
159
+
160
+ table_type: Literal["hudi_table", "spark_table"] = "hudi_table"
161
+ hudi_mode: Literal["upsert", "insert_overwrite", "insert_overwrite_table"] = (
162
+ "upsert"
163
+ )
164
+ src_db = None
165
+ src_tbl = None
166
+ dst_db = None
167
+ dst_tbl = None
168
+ id = None
169
+ ts = None
170
+ filter_by = None
171
+ offset = None
172
+ par_cols = []
173
+ path = None
174
+ repartition = []
175
+ url: str = None
176
+ concurrency_mode: Literal["SINGLE_WRITER", "OPTIMISTIC_CONCURRENCY_CONTROL"] = (
177
+ "SINGLE_WRITER"
178
+ )
179
+ extra_hudi_options: Dict[str, Union[str, bool, int]] = {}
180
+ hudi_layout = HudiLayout(
181
+ files_per_partition=1,
182
+ target_file_size_mb=128,
183
+ )
184
+ zookeeper: str = None
185
+
186
+ def __init__(
187
+ self,
188
+ start_date: str = None,
189
+ end_date: str = None,
190
+ zookeeper: str = None,
191
+ url: str = None,
192
+ bulk: bool = True,
193
+ hudi_mode_override: str = None,
194
+ **kwargs,
195
+ ):
196
+ fields = ["dst_db", "dst_tbl", "path"]
197
+ if self.table_type == "hudi_table":
198
+ fields += ["id", "ts"]
199
+ for field in fields:
200
+ if getattr(self, field) is None:
201
+ raise ValueError(
202
+ f"Field {field} cannot be None. Please set {field} before proceed"
203
+ )
204
+ if len(self.par_cols) == 0:
205
+ logger.warning("`par_cols` is empty; output will not be partitioned")
206
+ if type(self.repartition) != list:
207
+ self.repartition = [self.repartition]
208
+ if self.filter_by is None:
209
+ logger.warning(
210
+ "`filter_by` is not set, will extract full table, please be aware!"
211
+ )
212
+ self.start_date: str = start_date if start_date else None
213
+ self.end_date: str = end_date if end_date else None
214
+ self.zookeeper = zookeeper if zookeeper else self.zookeeper
215
+ self.url = url if url else self.url
216
+ self.bulk = bulk
217
+ # Allow CLI override of hudi_mode (e.g. --hudi-mode bulk_insert)
218
+ if hudi_mode_override:
219
+ self.hudi_mode = hudi_mode_override
220
+ for k, v in kwargs.items():
221
+ setattr(self, k, v)
222
+
223
+ if (
224
+ self.concurrency_mode == "OPTIMISTIC_CONCURRENCY_CONTROL"
225
+ and self.zookeeper is None
226
+ ):
227
+ raise ValueError("concurrency control enabled but zookeeper not set")
228
+ self.spark = SparkSession.getActiveSession()
229
+
230
+ def __call__(self):
231
+ self.process()
232
+
233
+ @classmethod
234
+ def run_from_cli(
235
+ cls,
236
+ start_date: Annotated[str, typer.Option("--start-date")] = None,
237
+ end_date: Annotated[str, typer.Option("--end-date")] = None,
238
+ zookeeper: Annotated[str, typer.Option("--zookeeper")] = None,
239
+ url: Annotated[str, typer.Option("--url")] = None,
240
+ bulk: Annotated[bool, typer.Option("--bulk/--per-day")] = True,
241
+ ):
242
+ obj = cls.__new__(cls)
243
+ obj.__init__(start_date, end_date, zookeeper, url, bulk)
244
+ obj()
245
+ return obj
246
+
247
+ def extract(self, start_date: str = None, end_date: str = None):
248
+ start_date = start_date or self.start_date
249
+ end_date = end_date or self.end_date
250
+ df = self.spark.table(f"{self.src_db}.{self.src_tbl}")
251
+
252
+ if self.offset and start_date:
253
+ from dateutil.relativedelta import relativedelta
254
+ from dateutil.parser import parse as date_parse
255
+
256
+ start_date = date_parse(start_date) - relativedelta(days=self.offset)
257
+ start_date = start_date.strftime("%Y-%m-%d")
258
+
259
+ if self.filter_by and start_date:
260
+ df = df.filter(col(self.filter_by) >= start_date)
261
+ if self.filter_by and end_date:
262
+ df = df.filter(col(self.filter_by) < end_date)
263
+ return df
264
+
265
+ def select_or_none(
266
+ self, df: DataFrame, cols: Union[List, Dict[str, Union[str, DataType]]]
267
+ ) -> DataFrame:
268
+ if type(cols) == list:
269
+ available_cols = [i for i in cols if i in df.columns]
270
+ missing_cols = [i for i in cols if i not in df.columns]
271
+ df = df.select(*available_cols)
272
+
273
+ logger.warning(
274
+ f"Columns {missing_cols} are missing, filling with None and cast as default StringType"
275
+ )
276
+ for col in missing_cols:
277
+ df = df.withColumn(col, lit(None).cast(StringType()))
278
+ elif type(cols) == dict:
279
+ available_cols = [i for i in cols.keys() if i in df.columns]
280
+ missing_cols = [i for i in cols.keys() if i not in df.columns]
281
+ df = df.select(*available_cols)
282
+ for col in missing_cols:
283
+ col_type = cols[col]
284
+ df = df.withColumn(col, lit(None).cast(col_type))
285
+ else:
286
+ raise TypeError(f"cols {cols} should be List or Dict")
287
+ df = df.select(*cols)
288
+ return df
289
+
290
+ @abstractmethod
291
+ def transform(self, df: DataFrame) -> DataFrame:
292
+ return df
293
+
294
+ def _prepare_partition(self, df: DataFrame) -> DataFrame:
295
+ self.spark.conf.set("spark.sql.sources.partitionOverwriteMode", "DYNAMIC")
296
+ new_cols = [i for i in df.columns if i not in self.par_cols] + self.par_cols
297
+ df = df.select(*new_cols)
298
+ if self.start_date and self.filter_by:
299
+ df = df.filter(col(self.filter_by) >= self.start_date)
300
+ return df
301
+
302
+ def _is_existing_hudi_table(self) -> bool:
303
+ """Check if a Hudi table already exists at `self.path`.
304
+
305
+ Used to determine whether we can perform an `upsert` (requires existing
306
+ table metadata at `.hoodie/hoodie.properties`) or need to fall back to
307
+ `bulk_insert` for the initial load.
308
+ """
309
+ try:
310
+ self.spark.read.text(f"{self.path}/.hoodie/hoodie.properties").limit(1).collect()
311
+ return True
312
+ except Exception:
313
+ return False
314
+
315
+ def load_hudi(self, df: DataFrame) -> DataFrame:
316
+ # -----------------------------------------------------------------
317
+ # Hudi write operation auto-detection (added 2026-03-02)
318
+ # -----------------------------------------------------------------
319
+ # In Hudi 0.14+, the `upsert` operation expects an existing Hudi table
320
+ # with `.hoodie/hoodie.properties` on HDFS. Writing to a *new* path
321
+ # with `upsert` fails with `HoodieUpsertException`.
322
+ #
323
+ # Previously (Hudi 0.12), `upsert` could auto-create new tables.
324
+ #
325
+ # Decision: auto-detect whether the table exists. If not, override
326
+ # the operation to `bulk_insert` for the initial load. Subsequent
327
+ # writes use the configured `hudi_mode` (typically `upsert`).
328
+ # -----------------------------------------------------------------
329
+ operation = self.hudi_mode
330
+ needs_existing_table = operation in ("upsert", "insert_overwrite", "insert_overwrite_table")
331
+ if needs_existing_table and not self._is_existing_hudi_table():
332
+ logger.info(
333
+ f"Hudi table not found at {self.path}, "
334
+ f"falling back to 'bulk_insert' for initial load "
335
+ f"(configured mode: {operation})"
336
+ )
337
+ operation = "bulk_insert"
338
+
339
+ hudi_options = {
340
+ "hoodie.table.name": f"{self.dst_db}.{self.dst_tbl}",
341
+ "hoodie.datasource.write.table.type": "COPY_ON_WRITE",
342
+ "hoodie.datasource.write.recordkey.field": self.id,
343
+ "hoodie.datasource.write.partitionpath.field": ",".join(self.par_cols),
344
+ "hoodie.datasource.write.keygenerator.class": "org.apache.hudi.keygen.ComplexKeyGenerator",
345
+ "hoodie.datasource.write.precombine.field": self.ts,
346
+ "hoodie.datasource.write.operation": f"{operation}",
347
+ "hoodie.bulkinsert.sort.mode": "PARTITION_PATH_REPARTITION_AND_SORT",
348
+ "hoodie.datasource.write.reconcile.schema": True,
349
+ # Do not set hoodie.*.shuffle.parallelism or spark.sql.shuffle.partitions:
350
+ # each shuffle task becomes a tiny Hudi file.
351
+ "hoodie.schema.on.read.enable": True,
352
+ # ---------------------------------------------------------------
353
+ # Hudi 0.15 ComplexKeyGenerator regression fix
354
+ # ---------------------------------------------------------------
355
+ # Hudi 0.14.1, 0.15.0, 1.0.x introduced a regression in
356
+ # ComplexKeyGenerator with a single record key field - the record
357
+ # key encoding changed, which can cause duplicate records during
358
+ # upserts. Setting this to `true` ensures consistent encoding
359
+ # for tables created on these versions.
360
+ #
361
+ # Ref: https://hudi.apache.org/releases/release-1.1.1
362
+ # ---------------------------------------------------------------
363
+ "hoodie.write.complex.keygen.new.encoding": True,
364
+ "hoodie.datasource.hive_sync.enable": True,
365
+ "hoodie.datasource.hive_sync.create_managed_table": True,
366
+ "hoodie.datasource.hive_sync.database": self.dst_db,
367
+ "hoodie.datasource.hive_sync.table": self.dst_tbl,
368
+ "hoodie.datasource.hive_sync.skip_ro_suffix": False,
369
+ "hoodie.datasource.hive_sync.support_timestamp": True,
370
+ "hoodie.datasource.write.hive_style_partitioning": True,
371
+ "hoodie.metadata.enable": False,
372
+ "hoodie.write.concurrency.mode": f"{self.concurrency_mode}",
373
+ "hoodie.write.lock.zookeeper.base_path": "/hudi",
374
+ "hoodie.write.lock.zookeeper.lock_key": f"{self.dst_db}.{self.dst_tbl}",
375
+ "hoodie.write.lock.zookeeper.port": "2181",
376
+ "hoodie.write.lock.zookeeper.url": f"{self.zookeeper}",
377
+ "path": self.path,
378
+ }
379
+ hudi_options.update(self.extra_hudi_options)
380
+ hudi_options.update(self.hudi_layout.write_options())
381
+ # Applied last, deliberately. A table inside the daily v3 metadata
382
+ # listing scope must have its Hudi metadata index maintained by every
383
+ # writer; an `extra_hudi_options` opt-out would leave the index stale
384
+ # and make readers see the wrong file list. Tables outside the scope
385
+ # keep `hoodie.metadata.enable=false` exactly as before.
386
+ hudi_options.update(
387
+ hudi_metadata_write_options(f"{self.dst_db}.{self.dst_tbl}")
388
+ )
389
+
390
+ df.write.format("hudi").options(**hudi_options).mode("append").save()
391
+
392
+ def rewrite_hudi_layout(self, partition_filter: Optional[str] = None) -> None:
393
+ """Repair small files on an existing Hudi table.
394
+
395
+ Reads the published path, drops `_hoodie_*` meta columns, and
396
+ ``insert_overwrite``s with ``HudiLayout``. Do not set Spark/Hudi
397
+ shuffle parallelism. Pass a Spark SQL predicate to limit the rewrite
398
+ (for example ``dt = '2026-08-01'``).
399
+ """
400
+ if self.table_type != "hudi_table":
401
+ raise ValueError("rewrite_hudi_layout is for hudi_table only")
402
+ if not self._is_existing_hudi_table():
403
+ raise ValueError(f"no Hudi table at {self.path}")
404
+ source = self.spark.read.format("hudi").load(self.path)
405
+ if partition_filter:
406
+ source = source.where(partition_filter)
407
+ meta = [c for c in source.columns if c.startswith("_hoodie_")]
408
+ source = source.drop(*meta) if meta else source
409
+ previous = self.hudi_mode
410
+ try:
411
+ self.hudi_mode = "insert_overwrite"
412
+ self.load_hudi(source)
413
+ finally:
414
+ self.hudi_mode = previous
415
+
416
+ def load_spark(self, df: DataFrame):
417
+ output = df.repartition(*self.repartition) if self.repartition else df
418
+ try:
419
+ output.write.mode("overwrite").insertInto(f"{self.dst_db}.{self.dst_tbl}")
420
+ except AnalysisException:
421
+ writer = output.write.option("path", self.path).mode("overwrite")
422
+ if len(self.par_cols) == 0:
423
+ writer.saveAsTable(f"{self.dst_db}.{self.dst_tbl}")
424
+ else:
425
+ writer.partitionBy(*self.par_cols).saveAsTable(
426
+ f"{self.dst_db}.{self.dst_tbl}"
427
+ )
428
+
429
+ def load(self, df: DataFrame):
430
+ if len(self.par_cols) > 0:
431
+ df = self._prepare_partition(df)
432
+ if self.table_type == "hudi_table":
433
+ self.load_hudi(df)
434
+ elif self.table_type == "spark_table":
435
+ self.load_spark(df)
436
+ else:
437
+ raise ValueError(f'table_type "{self.table_type}" is incorrect')
438
+
439
+ def etl(self, start_date: str = None, end_date: str = None):
440
+ df = self.extract(start_date=start_date, end_date=end_date)
441
+ res = self.transform(df)
442
+ self.load(res)
443
+
444
+ def process(self):
445
+ if self.bulk:
446
+ self.etl(self.start_date, self.end_date)
447
+ else:
448
+ # 1 Batch per day
449
+ logger.info("Processing ETL task on a per day basis")
450
+ date_rng = pd.date_range(self.start_date, self.end_date)
451
+ for dt in date_rng:
452
+ start_dt = dt.strftime("%Y-%m-%d")
453
+ end_dt = dt + relativedelta(days=1)
454
+ end_dt = end_dt.strftime("%Y-%m-%d")
455
+ logger.info(f"Processing dt {start_dt}")
456
+ self.etl(start_dt, end_dt)
457
+
458
+
459
+ class MongoDbEtl(Etl):
460
+ schema = None
461
+
462
+ def extract(
463
+ self, start_date: str = None, end_date: str = None, schema: StructField = None
464
+ ):
465
+ start_date = start_date or self.start_date
466
+ end_date = end_date or self.end_date
467
+ schema = schema or self.schema
468
+ df = (
469
+ self.spark.read.format("mongodb")
470
+ .option("connection.uri", self.url)
471
+ .option("database", self.src_db)
472
+ .option("collection", self.src_tbl)
473
+ .option("sql.inferSchema.mapTypes.enabled", "true")
474
+ .load(schema=schema)
475
+ )
476
+ if start_date:
477
+ df = df.filter(col(self.filter_by) >= start_date)
478
+ if end_date:
479
+ df = df.filter(col(self.filter_by) < end_date)
480
+ return df
481
+
482
+
483
+ class JdbcEtl(Etl):
484
+ driver = None
485
+
486
+ def __init__(
487
+ self,
488
+ url: str,
489
+ start_date: str = None,
490
+ end_date: str = None,
491
+ bulk=True,
492
+ hudi_mode_override: str = None,
493
+ **kwargs,
494
+ ):
495
+ super().__init__(start_date, end_date, url=url, bulk=bulk, hudi_mode_override=hudi_mode_override, **kwargs)
496
+
497
+ @classmethod
498
+ def run_from_cli(
499
+ cls,
500
+ url: Annotated[str, typer.Option("--url")],
501
+ start_date: Annotated[str, typer.Option("--start-date")] = None,
502
+ end_date: Annotated[str, typer.Option("--end-date")] = None,
503
+ bulk: Annotated[bool, typer.Option("--bulk/--per-day")] = True,
504
+ hudi_mode: Annotated[str, typer.Option("--hudi-mode")] = None,
505
+ ):
506
+ obj = cls.__new__(cls)
507
+ obj.__init__(url, start_date, end_date, bulk, hudi_mode_override=hudi_mode)
508
+ obj()
509
+ return obj
510
+
511
+ def _prepare_query(self, start_date: str = None, end_date: str = None):
512
+ query = f"select * from {self.src_db}.{self.src_tbl} where 1 = 1 "
513
+ if self.filter_by and start_date:
514
+ query += f'and {self.filter_by} >= "{start_date}" '
515
+ if self.filter_by and end_date:
516
+ query += f'and {self.filter_by} < "{end_date}" '
517
+ self.query = f"({query}) as tmp"
518
+ return self.query
519
+
520
+ def extract(self, start_date: str = None, end_date: str = None):
521
+ start_date = start_date or self.start_date
522
+ end_date = end_date or self.end_date
523
+ self._prepare_query(start_date, end_date)
524
+ logger.info(self.query)
525
+ if self.filter_by and start_date and end_date:
526
+ df = (
527
+ self.spark.read.format("jdbc")
528
+ .option("driver", self.driver)
529
+ .option("url", self.url)
530
+ .option("dbtable", self.query)
531
+ .option("partitionColumn", self.ts)
532
+ .option("lowerBound", start_date)
533
+ .option("upperBound", end_date)
534
+ .option("numPartitions", "8")
535
+ .option("fetchsize", "1000")
536
+ .option("pushDownPredicate", "true")
537
+ .option("pushDownAggregate", "true")
538
+ .option("pushDownLimit", "true")
539
+ .option("pushDownOffset", "true")
540
+ .load()
541
+ )
542
+ else:
543
+ df = (
544
+ self.spark.read.format("jdbc")
545
+ .option("driver", self.driver)
546
+ .option("url", self.url)
547
+ .option("dbtable", self.query)
548
+ .option("fetchsize", "1000")
549
+ .option("pushDownPredicate", "true")
550
+ .option("pushDownAggregate", "true")
551
+ .option("pushDownLimit", "true")
552
+ .option("pushDownOffset", "true")
553
+ .load()
554
+ )
555
+ return df
556
+
557
+
558
+ class MySqlEtl(JdbcEtl):
559
+ driver = "com.mysql.cj.jdbc.Driver"
560
+
561
+
562
+ class MsSqlEtl(JdbcEtl):
563
+ """
564
+ ETL base class for Microsoft SQL Server sources via JDBC.
565
+
566
+ JDBC URL format:
567
+ jdbc:sqlserver://host:port;databaseName=db;encrypt=false
568
+
569
+ Differences from MySqlEtl:
570
+ - Driver: com.microsoft.sqlserver.jdbc.SQLServerDriver
571
+ - Date literals use single quotes (ANSI SQL)
572
+ - Identifiers use bracket quoting: [schema].[table]
573
+ """
574
+
575
+ driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
576
+
577
+ def _prepare_query(self, start_date: str = None, end_date: str = None):
578
+ # Use bracket-quoted identifiers for MSSQL
579
+ if self.src_db:
580
+ table_ref = f"[{self.src_db}].[{self.src_tbl}]"
581
+ else:
582
+ table_ref = f"[{self.src_tbl}]"
583
+
584
+ query = f"select * from {table_ref} where 1 = 1 "
585
+ if self.filter_by and start_date:
586
+ query += f"and [{self.filter_by}] >= '{start_date}' "
587
+ if self.filter_by and end_date:
588
+ query += f"and [{self.filter_by}] < '{end_date}' "
589
+ self.query = f"({query}) as tmp"
590
+ return self.query
591
+
592
+ def extract(self, start_date: str = None, end_date: str = None):
593
+ """
594
+ MSSQL-specific extract -- always uses single-partition JDBC read.
595
+
596
+ MSSQL servers often redirect partitioned connections to internal
597
+ hostnames (e.g. 'DB197') that Spark workers cannot resolve.
598
+ Avoid `partitionColumn` / `numPartitions` to prevent this.
599
+
600
+ All semicolon-delimited URL properties (user, password, encrypt, etc.)
601
+ are extracted and passed as explicit Spark JDBC `.option()` calls.
602
+ This is required because:
603
+ - MSSQL JDBC driver v10.2+ defaults `encrypt=true`
604
+ - Spark's JDBC Properties object does NOT merge URL-embedded properties
605
+ - All properties must be in the Properties object for the driver
606
+ """
607
+ start_date = start_date or self.start_date
608
+ end_date = end_date or self.end_date
609
+ self._prepare_query(start_date, end_date)
610
+ logger.info(self.query)
611
+
612
+ # Parse MSSQL JDBC URL: jdbc:sqlserver://host:port;key=val;key=val;...
613
+ parts = self.url.split(";")
614
+ base_url = parts[0] # jdbc:sqlserver://host:port
615
+ jdbc_props = {}
616
+ remaining_url_parts = []
617
+ for part in parts[1:]:
618
+ if "=" in part:
619
+ k, v = part.split("=", 1)
620
+ # databaseName must stay in the URL (required by the driver)
621
+ # redirect must stay in the URL (connection-level property;
622
+ # the driver ignores it when passed as a JDBC Properties entry)
623
+ if k.lower() in ("databasename", "redirect"):
624
+ remaining_url_parts.append(part)
625
+ else:
626
+ jdbc_props[k] = v
627
+ else:
628
+ remaining_url_parts.append(part)
629
+
630
+ clean_url = ";".join([base_url] + remaining_url_parts)
631
+
632
+ # Ensure SSL properties are set (driver v10.2+ defaults encrypt=true)
633
+ jdbc_props.setdefault("encrypt", "false")
634
+ jdbc_props.setdefault("trustServerCertificate", "true")
635
+
636
+ logger.info(f"MSSQL clean URL: {clean_url}")
637
+ logger.info(f"MSSQL props keys: {list(jdbc_props.keys())}")
638
+
639
+ df = (
640
+ self.spark.read.format("jdbc")
641
+ .option("driver", self.driver)
642
+ .option("url", clean_url)
643
+ .option("dbtable", self.query)
644
+ .option("fetchsize", "1000")
645
+ .options(**jdbc_props)
646
+ .load()
647
+ )
648
+ return df
649
+
650
+
651
+ class JsonEtl(Etl):
652
+ src_path = None
653
+ schema = None
654
+
655
+ def __init__(
656
+ self,
657
+ src_path: str,
658
+ start_date: str = None,
659
+ end_date: str = None,
660
+ bulk=True,
661
+ recursive: bool = True,
662
+ **kwargs,
663
+ ):
664
+ super().__init__(start_date, end_date, bulk, **kwargs)
665
+ self.src_path = src_path
666
+ self.recursive = recursive
667
+
668
+ @classmethod
669
+ def run_from_cli(
670
+ cls,
671
+ src_path: Annotated[str, typer.Option("--src-path")] = None,
672
+ start_date: Annotated[str, typer.Option("--start-date")] = None,
673
+ end_date: Annotated[str, typer.Option("--end-date")] = None,
674
+ bulk: Annotated[bool, typer.Option("--bulk/--per-day")] = True,
675
+ recursive: Annotated[
676
+ bool, typer.Option("--recursive/--no-recursive")
677
+ ] = True,
678
+ ):
679
+ obj = cls.__new__(cls)
680
+ obj.__init__(src_path, start_date, end_date, bulk, recursive)
681
+ obj()
682
+ return obj
683
+
684
+ def extract(self, start_date: str = None, end_date: str = None):
685
+ start_date = start_date or self.start_date
686
+ end_date = end_date or self.end_date
687
+ recursive = str(self.recursive).lower()
688
+ if self.schema:
689
+ return (
690
+ self.spark.read.option("recursiveFileLookup", recursive)
691
+ .schema(self.schema)
692
+ .json(self.src_path)
693
+ )
694
+ else:
695
+ return self.spark.read.option("recursiveFileLookup", recursive).json(
696
+ self.src_path
697
+ )
698
+
699
+
700
+ class CsvEtl(Etl):
701
+ src_path = None
702
+ schema = None
703
+
704
+ def __init__(
705
+ self,
706
+ src_path: str,
707
+ start_date: str = None,
708
+ end_date: str = None,
709
+ bulk=True,
710
+ recursive: bool = True,
711
+ **kwargs,
712
+ ):
713
+ super().__init__(start_date, end_date, bulk=bulk, **kwargs)
714
+ self.src_path = src_path
715
+ self.recursive = recursive
716
+
717
+ @classmethod
718
+ def run_from_cli(
719
+ cls,
720
+ src_path: Annotated[str, typer.Option("--src-path")] = None,
721
+ start_date: Annotated[str, typer.Option("--start-date")] = None,
722
+ end_date: Annotated[str, typer.Option("--end-date")] = None,
723
+ bulk: Annotated[bool, typer.Option("--bulk/--per-day")] = True,
724
+ recursive: Annotated[
725
+ bool, typer.Option("--recursive/--no-recursive")
726
+ ] = True,
727
+ ):
728
+ obj = cls.__new__(cls)
729
+ obj.__init__(src_path, start_date, end_date, bulk, recursive)
730
+ obj()
731
+ return obj
732
+
733
+ def extract(self, start_date: str = None, end_date: str = None):
734
+ if self.schema:
735
+ return (
736
+ self.spark.read.option("recursiveFileLookup", self.recursive)
737
+ .option("header", True)
738
+ .option("mode", "DROPMALFORMED")
739
+ .schema(self.schema)
740
+ .csv(self.src_path)
741
+ )
742
+ else:
743
+ return (
744
+ self.spark.read.option("recursiveFileLookup", self.recursive)
745
+ .option("header", True)
746
+ .option("mode", "DROPMALFORMED")
747
+ .csv(self.src_path)
748
+ )