zilmate 1.9.3 → 1.10.1

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 (306) hide show
  1. package/.agents/skills/ad-creative/SKILL.md +362 -0
  2. package/.agents/skills/ad-creative/evals/evals.json +90 -0
  3. package/.agents/skills/ad-creative/references/generative-tools.md +637 -0
  4. package/.agents/skills/ad-creative/references/platform-specs.md +213 -0
  5. package/.agents/skills/ai-seo/SKILL.md +489 -0
  6. package/.agents/skills/ai-seo/evals/evals.json +90 -0
  7. package/.agents/skills/ai-seo/references/content-patterns.md +285 -0
  8. package/.agents/skills/ai-seo/references/content-types.md +71 -0
  9. package/.agents/skills/ai-seo/references/okf.md +104 -0
  10. package/.agents/skills/ai-seo/references/platform-ranking-factors.md +154 -0
  11. package/.agents/skills/clerk-backend-api/SKILL.md +426 -0
  12. package/.agents/skills/clerk-backend-api/evals/evals.json +87 -0
  13. package/.agents/skills/clerk-backend-api/scripts/api-specs-context.sh +30 -0
  14. package/.agents/skills/clerk-backend-api/scripts/execute-request.sh +88 -0
  15. package/.agents/skills/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
  16. package/.agents/skills/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
  17. package/.agents/skills/clerk-backend-api/scripts/extract-tags.js +14 -0
  18. package/.agents/skills/copy-editing/SKILL.md +457 -0
  19. package/.agents/skills/copy-editing/evals/evals.json +89 -0
  20. package/.agents/skills/copy-editing/references/checklist.md +66 -0
  21. package/.agents/skills/copy-editing/references/content-refresh.md +38 -0
  22. package/.agents/skills/copy-editing/references/plain-english-alternatives.md +394 -0
  23. package/.agents/skills/hyperframes/SKILL.md +163 -0
  24. package/.agents/skills/hyperframes-cli/SKILL.md +109 -0
  25. package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
  26. package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +51 -0
  27. package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
  28. package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
  29. package/.agents/skills/hyperframes-cli/references/preview-render.md +109 -0
  30. package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
  31. package/.agents/skills/hyperframes-media/SKILL.md +97 -0
  32. package/.agents/skills/hyperframes-media/assets/sfx/CREDITS.md +35 -0
  33. package/.agents/skills/hyperframes-media/assets/sfx/manifest.json +97 -0
  34. package/.agents/skills/hyperframes-media/references/bgm.md +72 -0
  35. package/.agents/skills/hyperframes-media/references/captions/authoring.md +159 -0
  36. package/.agents/skills/hyperframes-media/references/captions/motion.md +87 -0
  37. package/.agents/skills/hyperframes-media/references/captions/transcript-handling.md +97 -0
  38. package/.agents/skills/hyperframes-media/references/remove-background.md +143 -0
  39. package/.agents/skills/hyperframes-media/references/requirements.md +29 -0
  40. package/.agents/skills/hyperframes-media/references/sfx.md +42 -0
  41. package/.agents/skills/hyperframes-media/references/transcribe.md +52 -0
  42. package/.agents/skills/hyperframes-media/references/tts-to-captions.md +24 -0
  43. package/.agents/skills/hyperframes-media/references/tts.md +135 -0
  44. package/.agents/skills/hyperframes-media/scripts/audio.mjs +282 -0
  45. package/.agents/skills/hyperframes-media/scripts/heygen-tts.mjs +121 -0
  46. package/.agents/skills/hyperframes-media/scripts/lib/bgm.mjs +235 -0
  47. package/.agents/skills/hyperframes-media/scripts/lib/heygen.mjs +131 -0
  48. package/.agents/skills/hyperframes-media/scripts/lib/sfx.mjs +128 -0
  49. package/.agents/skills/hyperframes-media/scripts/lib/tts.mjs +220 -0
  50. package/.agents/skills/hyperframes-media/scripts/lyria-recipe.py +128 -0
  51. package/.agents/skills/hyperframes-media/scripts/wait-bgm.mjs +167 -0
  52. package/.agents/skills/hyperframes-registry/SKILL.md +101 -0
  53. package/.agents/skills/hyperframes-registry/examples/add-block.md +51 -0
  54. package/.agents/skills/hyperframes-registry/examples/add-component.md +73 -0
  55. package/.agents/skills/hyperframes-registry/references/contributing.md +166 -0
  56. package/.agents/skills/hyperframes-registry/references/demo-html-pattern.md +54 -0
  57. package/.agents/skills/hyperframes-registry/references/discovery.md +215 -0
  58. package/.agents/skills/hyperframes-registry/references/install-locations.md +45 -0
  59. package/.agents/skills/hyperframes-registry/references/templates.md +417 -0
  60. package/.agents/skills/hyperframes-registry/references/wiring-blocks.md +61 -0
  61. package/.agents/skills/hyperframes-registry/references/wiring-components.md +39 -0
  62. package/.agents/skills/marketing-psychology/SKILL.md +455 -0
  63. package/.agents/skills/marketing-psychology/evals/evals.json +88 -0
  64. package/.agents/skills/mintlify/SKILL.md +328 -0
  65. package/.agents/skills/product-launch-video/SKILL.md +206 -0
  66. package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
  67. package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
  68. package/.agents/skills/product-launch-video/references/story-design.md +373 -0
  69. package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
  70. package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
  71. package/.agents/skills/product-launch-video/scripts/audio.mjs +253 -0
  72. package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
  73. package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
  74. package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
  75. package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
  76. package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
  77. package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
  78. package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
  79. package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
  80. package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
  81. package/.agents/skills/product-launch-video/scripts/transitions.mjs +320 -0
  82. package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +74 -0
  83. package/.agents/skills/prompt-engineering-patterns/SKILL.md +144 -0
  84. package/.agents/skills/prompt-engineering-patterns/assets/few-shot-examples.json +106 -0
  85. package/.agents/skills/prompt-engineering-patterns/assets/prompt-template-library.md +264 -0
  86. package/.agents/skills/prompt-engineering-patterns/references/chain-of-thought.md +412 -0
  87. package/.agents/skills/prompt-engineering-patterns/references/details.md +338 -0
  88. package/.agents/skills/prompt-engineering-patterns/references/few-shot-learning.md +386 -0
  89. package/.agents/skills/prompt-engineering-patterns/references/prompt-optimization.md +428 -0
  90. package/.agents/skills/prompt-engineering-patterns/references/prompt-templates.md +484 -0
  91. package/.agents/skills/prompt-engineering-patterns/references/system-prompts.md +195 -0
  92. package/.agents/skills/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  93. package/.agents/skills/remotion-to-hyperframes/SKILL.md +127 -0
  94. package/.agents/skills/remotion-to-hyperframes/assets/.gitkeep +0 -0
  95. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/run.sh +249 -0
  96. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/README.md +41 -0
  97. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +26 -0
  98. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +61 -0
  99. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/package.json +14 -0
  100. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/remotion.config.ts +13 -0
  101. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/Root.tsx +13 -0
  102. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/TitleCard.tsx +34 -0
  103. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/src/index.ts +4 -0
  104. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/remotion-src/tsconfig.json +15 -0
  105. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +54 -0
  106. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +29 -0
  107. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +118 -0
  108. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/package.json +14 -0
  109. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/remotion.config.ts +13 -0
  110. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/MultiScene.tsx +105 -0
  111. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/Root.tsx +13 -0
  112. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/src/index.ts +4 -0
  113. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/remotion-src/tsconfig.json +15 -0
  114. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/setup.sh +34 -0
  115. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/README.md +85 -0
  116. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +41 -0
  117. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +269 -0
  118. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/package.json +15 -0
  119. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/remotion.config.ts +13 -0
  120. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Root.tsx +27 -0
  121. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/Stargazed.tsx +37 -0
  122. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/AnimatedNumber.tsx +23 -0
  123. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/StatCard.tsx +59 -0
  124. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/components/UnderlinedText.tsx +47 -0
  125. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/index.ts +4 -0
  126. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/OutroScene.tsx +18 -0
  127. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/StatsScene.tsx +34 -0
  128. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/src/scenes/TitleScene.tsx +55 -0
  129. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/remotion-src/tsconfig.json +15 -0
  130. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/README.md +51 -0
  131. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/01-use-state.tsx +29 -0
  132. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/02-use-effect-deps.tsx +29 -0
  133. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/03-async-metadata.tsx +39 -0
  134. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/04-third-party-react.tsx +30 -0
  135. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/05-lambda-config.tsx +38 -0
  136. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/06-warnings-only.tsx +36 -0
  137. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/07-custom-hook.tsx +28 -0
  138. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/cases/08-mixed.tsx +41 -0
  139. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/expected.json +85 -0
  140. package/.agents/skills/remotion-to-hyperframes/assets/test-corpus/tier-4-escape-hatch/validate.sh +110 -0
  141. package/.agents/skills/remotion-to-hyperframes/references/api-map.md +142 -0
  142. package/.agents/skills/remotion-to-hyperframes/references/escape-hatch.md +115 -0
  143. package/.agents/skills/remotion-to-hyperframes/references/eval.md +140 -0
  144. package/.agents/skills/remotion-to-hyperframes/references/fonts.md +112 -0
  145. package/.agents/skills/remotion-to-hyperframes/references/limitations.md +136 -0
  146. package/.agents/skills/remotion-to-hyperframes/references/lottie.md +121 -0
  147. package/.agents/skills/remotion-to-hyperframes/references/media.md +149 -0
  148. package/.agents/skills/remotion-to-hyperframes/references/parameters.md +167 -0
  149. package/.agents/skills/remotion-to-hyperframes/references/sequencing.md +195 -0
  150. package/.agents/skills/remotion-to-hyperframes/references/timing.md +165 -0
  151. package/.agents/skills/remotion-to-hyperframes/references/transitions.md +114 -0
  152. package/.agents/skills/remotion-to-hyperframes/scripts/.gitkeep +0 -0
  153. package/.agents/skills/remotion-to-hyperframes/scripts/frame_strip.sh +107 -0
  154. package/.agents/skills/remotion-to-hyperframes/scripts/lint_source.py +358 -0
  155. package/.agents/skills/remotion-to-hyperframes/scripts/render_diff.sh +103 -0
  156. package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/blocker.tsx +50 -0
  157. package/.agents/skills/remotion-to-hyperframes/scripts/tests/fixtures/clean.tsx +46 -0
  158. package/.agents/skills/remotion-to-hyperframes/scripts/tests/smoke.sh +90 -0
  159. package/.agents/skills/seo-audit/SKILL.md +497 -0
  160. package/.agents/skills/seo-audit/evals/evals.json +136 -0
  161. package/.agents/skills/seo-audit/references/ai-writing-detection.md +200 -0
  162. package/.agents/skills/seo-audit/references/international-seo.md +230 -0
  163. package/README.md +4 -2
  164. package/SDK.md +64 -1
  165. package/dist/agents/coding.agent.d.ts +632 -0
  166. package/dist/agents/coding.agent.d.ts.map +1 -1
  167. package/dist/agents/coding.agent.js +11 -3
  168. package/dist/agents/coding.agent.js.map +1 -1
  169. package/dist/agents/developer-helper.agent.d.ts +704 -0
  170. package/dist/agents/developer-helper.agent.d.ts.map +1 -1
  171. package/dist/agents/developer-helper.agent.js +6 -0
  172. package/dist/agents/developer-helper.agent.js.map +1 -1
  173. package/dist/agents/manager.d.ts +1009 -4
  174. package/dist/agents/manager.d.ts.map +1 -1
  175. package/dist/agents/manager.js +16 -5
  176. package/dist/agents/manager.js.map +1 -1
  177. package/dist/agents/security.agent.d.ts +157 -3
  178. package/dist/agents/security.agent.d.ts.map +1 -1
  179. package/dist/agents/security.agent.js +3 -0
  180. package/dist/agents/security.agent.js.map +1 -1
  181. package/dist/agents/swarm/main.js +1 -1
  182. package/dist/agents/swarm/main.js.map +1 -1
  183. package/dist/agents/swarm/registry.d.ts.map +1 -1
  184. package/dist/agents/swarm/registry.js +232 -26
  185. package/dist/agents/swarm/registry.js.map +1 -1
  186. package/dist/cli/chat.d.ts +1 -1
  187. package/dist/cli/chat.d.ts.map +1 -1
  188. package/dist/cli/chat.js +32 -10
  189. package/dist/cli/chat.js.map +1 -1
  190. package/dist/cli/doctor.d.ts +14 -0
  191. package/dist/cli/doctor.d.ts.map +1 -1
  192. package/dist/cli/doctor.js +91 -0
  193. package/dist/cli/doctor.js.map +1 -1
  194. package/dist/cli/format.d.ts +1 -0
  195. package/dist/cli/format.d.ts.map +1 -1
  196. package/dist/cli/format.js +68 -14
  197. package/dist/cli/format.js.map +1 -1
  198. package/dist/cli/menu.d.ts.map +1 -1
  199. package/dist/cli/menu.js +102 -0
  200. package/dist/cli/menu.js.map +1 -1
  201. package/dist/cli/setup.d.ts +10 -0
  202. package/dist/cli/setup.d.ts.map +1 -1
  203. package/dist/cli/setup.js +187 -3
  204. package/dist/cli/setup.js.map +1 -1
  205. package/dist/cli/update.d.ts +2 -0
  206. package/dist/cli/update.d.ts.map +1 -1
  207. package/dist/cli/update.js +89 -1
  208. package/dist/cli/update.js.map +1 -1
  209. package/dist/config/env.d.ts +12 -0
  210. package/dist/config/env.d.ts.map +1 -1
  211. package/dist/config/env.js +19 -5
  212. package/dist/config/env.js.map +1 -1
  213. package/dist/index.d.ts +1 -1
  214. package/dist/index.d.ts.map +1 -1
  215. package/dist/index.js +61 -5
  216. package/dist/index.js.map +1 -1
  217. package/dist/jobs/trigger-orchestrator.d.ts +5 -1
  218. package/dist/jobs/trigger-orchestrator.d.ts.map +1 -1
  219. package/dist/jobs/trigger-orchestrator.js +29 -9
  220. package/dist/jobs/trigger-orchestrator.js.map +1 -1
  221. package/dist/jobs/trigger-router.d.ts.map +1 -1
  222. package/dist/jobs/trigger-router.js +3 -2
  223. package/dist/jobs/trigger-router.js.map +1 -1
  224. package/dist/memory/corporate-wiki.d.ts +15 -0
  225. package/dist/memory/corporate-wiki.d.ts.map +1 -0
  226. package/dist/memory/corporate-wiki.js +152 -0
  227. package/dist/memory/corporate-wiki.js.map +1 -0
  228. package/dist/observability/doctor.d.ts.map +1 -1
  229. package/dist/observability/doctor.js +12 -4
  230. package/dist/observability/doctor.js.map +1 -1
  231. package/dist/pre-init.d.ts +2 -0
  232. package/dist/pre-init.d.ts.map +1 -0
  233. package/dist/pre-init.js +3 -0
  234. package/dist/pre-init.js.map +1 -0
  235. package/dist/runtime/chat-bridge.d.ts.map +1 -1
  236. package/dist/runtime/chat-bridge.js +33 -2
  237. package/dist/runtime/chat-bridge.js.map +1 -1
  238. package/dist/runtime/swarm.d.ts +0 -1
  239. package/dist/runtime/swarm.d.ts.map +1 -1
  240. package/dist/runtime/swarm.js +26 -15
  241. package/dist/runtime/swarm.js.map +1 -1
  242. package/dist/safety/approvals.d.ts +2 -2
  243. package/dist/safety/approvals.d.ts.map +1 -1
  244. package/dist/safety/approvals.js +47 -1
  245. package/dist/safety/approvals.js.map +1 -1
  246. package/dist/safety/redaction.d.ts.map +1 -1
  247. package/dist/safety/redaction.js +10 -0
  248. package/dist/safety/redaction.js.map +1 -1
  249. package/dist/server.d.ts +1 -0
  250. package/dist/server.d.ts.map +1 -1
  251. package/dist/server.js +1 -0
  252. package/dist/server.js.map +1 -1
  253. package/dist/tools/cloud.tool.d.ts +394 -0
  254. package/dist/tools/cloud.tool.d.ts.map +1 -0
  255. package/dist/tools/cloud.tool.js +784 -0
  256. package/dist/tools/cloud.tool.js.map +1 -0
  257. package/dist/tools/computer-use.tool.d.ts +6 -6
  258. package/dist/tools/corporate-wiki.tool.d.ts +22 -0
  259. package/dist/tools/corporate-wiki.tool.d.ts.map +1 -0
  260. package/dist/tools/corporate-wiki.tool.js +39 -0
  261. package/dist/tools/corporate-wiki.tool.js.map +1 -0
  262. package/dist/tools/desktop.tool.d.ts +96 -0
  263. package/dist/tools/desktop.tool.d.ts.map +1 -1
  264. package/dist/tools/desktop.tool.js +439 -0
  265. package/dist/tools/desktop.tool.js.map +1 -1
  266. package/dist/tools/devops.tool.d.ts +162 -0
  267. package/dist/tools/devops.tool.d.ts.map +1 -0
  268. package/dist/tools/devops.tool.js +377 -0
  269. package/dist/tools/devops.tool.js.map +1 -0
  270. package/dist/tools/multimedia.tool.d.ts +126 -0
  271. package/dist/tools/multimedia.tool.d.ts.map +1 -0
  272. package/dist/tools/multimedia.tool.js +533 -0
  273. package/dist/tools/multimedia.tool.js.map +1 -0
  274. package/dist/tools/osint.tool.d.ts +6 -6
  275. package/dist/tools/sandbox-dev.tool.d.ts +15 -0
  276. package/dist/tools/sandbox-dev.tool.d.ts.map +1 -0
  277. package/dist/tools/sandbox-dev.tool.js +139 -0
  278. package/dist/tools/sandbox-dev.tool.js.map +1 -0
  279. package/dist/tools/setup-assistant.tool.d.ts +1 -1
  280. package/dist/tools/shell.tool.d.ts +82 -0
  281. package/dist/tools/shell.tool.d.ts.map +1 -1
  282. package/dist/tools/shell.tool.js +192 -1
  283. package/dist/tools/shell.tool.js.map +1 -1
  284. package/dist/tools/swarm-ops.tool.d.ts +14 -0
  285. package/dist/tools/swarm-ops.tool.d.ts.map +1 -1
  286. package/dist/tools/swarm-ops.tool.js +44 -0
  287. package/dist/tools/swarm-ops.tool.js.map +1 -1
  288. package/dist/tools/sysops.tool.d.ts +157 -0
  289. package/dist/tools/sysops.tool.d.ts.map +1 -0
  290. package/dist/tools/sysops.tool.js +643 -0
  291. package/dist/tools/sysops.tool.js.map +1 -0
  292. package/dist/voice/cascade.d.ts +1 -0
  293. package/dist/voice/cascade.d.ts.map +1 -1
  294. package/dist/voice/cascade.js +16 -1
  295. package/dist/voice/cascade.js.map +1 -1
  296. package/dist/voice/deepgram.d.ts.map +1 -1
  297. package/dist/voice/deepgram.js +34 -2
  298. package/dist/voice/deepgram.js.map +1 -1
  299. package/dist/voice/terminal.d.ts +1 -0
  300. package/dist/voice/terminal.d.ts.map +1 -1
  301. package/dist/voice/terminal.js +28 -0
  302. package/dist/voice/terminal.js.map +1 -1
  303. package/dist/voice/types.d.ts +2 -0
  304. package/dist/voice/types.d.ts.map +1 -1
  305. package/package.json +9 -1
  306. package/scripts/release-github.mjs +23 -18
@@ -0,0 +1,34 @@
1
+ import { AbsoluteFill } from "remotion";
2
+ import { StatCard } from "../components/StatCard";
3
+
4
+ interface Stat {
5
+ label: string;
6
+ value: number;
7
+ color: string;
8
+ }
9
+
10
+ interface Props {
11
+ stats: Stat[];
12
+ }
13
+
14
+ export const StatsScene: React.FC<Props> = ({ stats }) => (
15
+ <AbsoluteFill
16
+ style={{
17
+ justifyContent: "center",
18
+ alignItems: "center",
19
+ gap: 48,
20
+ flexDirection: "row",
21
+ fontFamily: "Helvetica, Arial, sans-serif",
22
+ }}
23
+ >
24
+ {stats.map((stat, i) => (
25
+ <StatCard
26
+ key={stat.label}
27
+ label={stat.label}
28
+ value={stat.value}
29
+ color={stat.color}
30
+ delayInFrames={i * 12}
31
+ />
32
+ ))}
33
+ </AbsoluteFill>
34
+ );
@@ -0,0 +1,55 @@
1
+ import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
2
+
3
+ interface Props {
4
+ title: string;
5
+ subtitle: string;
6
+ }
7
+
8
+ export const TitleScene: React.FC<Props> = ({ title, subtitle }) => {
9
+ const frame = useCurrentFrame();
10
+ const { fps } = useVideoConfig();
11
+
12
+ const titleScale = spring({
13
+ frame,
14
+ fps,
15
+ config: { damping: 12, stiffness: 100, mass: 1 },
16
+ });
17
+ const subtitleOpacity = interpolate(frame, [20, 40], [0, 1], {
18
+ extrapolateLeft: "clamp",
19
+ extrapolateRight: "clamp",
20
+ });
21
+
22
+ return (
23
+ <AbsoluteFill
24
+ style={{
25
+ justifyContent: "center",
26
+ alignItems: "center",
27
+ flexDirection: "column",
28
+ fontFamily: "Helvetica, Arial, sans-serif",
29
+ }}
30
+ >
31
+ <div
32
+ style={{
33
+ fontSize: 160,
34
+ fontWeight: 900,
35
+ color: "#ffffff",
36
+ letterSpacing: "0.05em",
37
+ transform: `scale(${titleScale})`,
38
+ }}
39
+ >
40
+ {title}
41
+ </div>
42
+ <div
43
+ style={{
44
+ fontSize: 36,
45
+ fontWeight: 400,
46
+ color: "#9ca3af",
47
+ marginTop: 24,
48
+ opacity: subtitleOpacity,
49
+ }}
50
+ >
51
+ {subtitle}
52
+ </div>
53
+ </AbsoluteFill>
54
+ );
55
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2018",
4
+ "module": "ESNext",
5
+ "jsx": "react-jsx",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "moduleResolution": "node",
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true
13
+ },
14
+ "include": ["src"]
15
+ }
@@ -0,0 +1,51 @@
1
+ # Tier 4 — escape-hatch
2
+
3
+ ## What it tests
4
+
5
+ T4 is the **lint-only** tier. There are no renders to diff — the skill is
6
+ graded on whether it correctly _refuses_ to translate each case (and
7
+ recommends the runtime interop pattern from PR #214 instead) or, where
8
+ appropriate, translates after dropping warning-level decorations.
9
+
10
+ Each `cases/*.tsx` file is a minimal Remotion composition that
11
+ demonstrates one specific pattern. The skill should:
12
+
13
+ 1. Run `scripts/lint_source.py` over the source.
14
+ 2. Compare the JSON output to `expected.json` for that case.
15
+ 3. Take the documented `skill_action`:
16
+ - `refuse_translation_recommend_interop` — print the rationale + link to
17
+ the PR #214 interop guide; do not produce HF output.
18
+ - `drop_lambda_code_translate_remainder_if_clean` — drop the
19
+ `@remotion/lambda` code with a note; translate the rest only if no
20
+ other blockers are present.
21
+ - `translate_after_dropping_wrappers` — translate normally; drop
22
+ `useCallback` / `useMemo` / `delayRender` wrappers.
23
+ - `inline_hook_body_if_pure` — inline the custom hook's body if it's a
24
+ pure derivation of `useCurrentFrame`; otherwise bow out.
25
+
26
+ ## Cases
27
+
28
+ | # | File | Expected finding | Notes |
29
+ | --- | -------------------------- | ----------------------------------- | ----------------------------------------------- |
30
+ | 01 | `01-use-state.tsx` | blocker `r2hf/use-state` | useState driving animation |
31
+ | 02 | `02-use-effect-deps.tsx` | blocker `r2hf/use-effect-deps` | useEffect/useLayoutEffect with non-empty deps |
32
+ | 03 | `03-async-metadata.tsx` | blocker `r2hf/async-metadata` | calculateMetadata returns a Promise |
33
+ | 04 | `04-third-party-react.tsx` | blocker `r2hf/third-party-react-ui` | imports `@mui/material` |
34
+ | 05 | `05-lambda-config.tsx` | warning `r2hf/lambda-import` | imports `@remotion/lambda` — drops, translates |
35
+ | 06 | `06-warnings-only.tsx` | warnings only | delayRender / useCallback / useMemo |
36
+ | 07 | `07-custom-hook.tsx` | warning `r2hf/custom-hook` | locally-defined `useFadeIn` (export const form) |
37
+ | 08 | `08-mixed.tsx` | 3 blockers + 1 warning | aggregate-findings test |
38
+
39
+ ## Validation
40
+
41
+ ```bash
42
+ ./validate.sh
43
+ ```
44
+
45
+ The script runs `lint_source.py` against each case and asserts:
46
+
47
+ - Each expected blocker rule fires with severity `blocker`.
48
+ - Each expected warning rule fires with severity `warning` (or stronger).
49
+ - `lint_source.py`'s exit code is 1 when blockers are expected, 0 otherwise.
50
+
51
+ T4 passes when every case matches its expected output. No renders involved.
@@ -0,0 +1,29 @@
1
+ // T4 case 01 — useState drives animation.
2
+ //
3
+ // Should be detected by lint_source.py as blocker r2hf/use-state.
4
+ // The skill should refuse to translate and recommend the runtime interop
5
+ // pattern from PR #214.
6
+ //
7
+ // Why this is a blocker: useState is React's component-local mutable state.
8
+ // HF's seek-driven model produces deterministic frames from a single time
9
+ // value — there's no per-frame React render cycle to update state on.
10
+
11
+ import React, { useState } from "react";
12
+ import { AbsoluteFill, useCurrentFrame } from "remotion";
13
+
14
+ export const StateDriven: React.FC = () => {
15
+ const frame = useCurrentFrame();
16
+ const [hue, setHue] = useState(0);
17
+
18
+ // Even if this looks innocuous, the setHue call breaks determinism: HF
19
+ // can't reproduce React state mutations across seeks.
20
+ if (frame % 30 === 0 && hue < 360) {
21
+ setHue((h) => h + 30);
22
+ }
23
+
24
+ return (
25
+ <AbsoluteFill style={{ background: `hsl(${hue}, 80%, 50%)` }}>
26
+ <div>frame {frame}</div>
27
+ </AbsoluteFill>
28
+ );
29
+ };
@@ -0,0 +1,29 @@
1
+ // T4 case 02 — useEffect with non-empty deps performs side effects per render.
2
+ //
3
+ // Should be detected by lint_source.py as blocker r2hf/use-effect-deps.
4
+ // The skill should refuse to translate.
5
+ //
6
+ // Why this is a blocker: side effects (network, DOM mutation outside the
7
+ // rendered tree, timers) don't translate to a seek-driven model. HF assumes
8
+ // the page is fully rendered and pure between seeks.
9
+
10
+ import React, { useEffect, useRef } from "react";
11
+ import { AbsoluteFill, useCurrentFrame } from "remotion";
12
+
13
+ export const SideEffectDriven: React.FC = () => {
14
+ const frame = useCurrentFrame();
15
+ const canvasRef = useRef<HTMLCanvasElement>(null);
16
+
17
+ useEffect(() => {
18
+ const canvas = canvasRef.current;
19
+ if (!canvas) return;
20
+ const ctx = canvas.getContext("2d");
21
+ ctx?.fillRect(frame, frame, 10, 10);
22
+ }, [frame]);
23
+
24
+ return (
25
+ <AbsoluteFill>
26
+ <canvas ref={canvasRef} width={1280} height={720} />
27
+ </AbsoluteFill>
28
+ );
29
+ };
@@ -0,0 +1,39 @@
1
+ // T4 case 03 — calculateMetadata returns a Promise.
2
+ //
3
+ // Should be detected by lint_source.py as blocker r2hf/async-metadata.
4
+ // The skill should refuse to translate.
5
+ //
6
+ // Why this is a blocker: HF needs the composition's duration, dimensions,
7
+ // and props known up-front to produce HTML and seed the timeline. Async
8
+ // metadata fetched from a server at render time has no equivalent in HF —
9
+ // the metadata would need to be resolved at build time before the HTML is
10
+ // authored.
11
+
12
+ import React from "react";
13
+ import { AbsoluteFill, useCurrentFrame } from "remotion";
14
+
15
+ interface Props {
16
+ text: string;
17
+ }
18
+
19
+ export const AsyncMetadataDriven: React.FC<Props> = ({ text }) => {
20
+ const frame = useCurrentFrame();
21
+ return (
22
+ <AbsoluteFill>
23
+ <div>
24
+ {text} · frame {frame}
25
+ </div>
26
+ </AbsoluteFill>
27
+ );
28
+ };
29
+
30
+ export const calculateMetadata = async ({ props }: { props: Props }) => {
31
+ const response = await fetch(
32
+ `https://api.example.com/duration?text=${encodeURIComponent(props.text)}`,
33
+ );
34
+ const { durationInFrames } = await response.json();
35
+ return {
36
+ durationInFrames,
37
+ fps: 30,
38
+ };
39
+ };
@@ -0,0 +1,30 @@
1
+ // T4 case 04 — Imports from a third-party React UI library.
2
+ //
3
+ // Should be detected by lint_source.py as blocker r2hf/third-party-react-ui.
4
+ // The skill should refuse to translate.
5
+ //
6
+ // Why this is a blocker: a Material-UI Button (or any React UI library
7
+ // component) is a React-only abstraction with internal hooks, refs, and
8
+ // theme provider context. Translating it to HTML+CSS would require
9
+ // re-implementing the design system, which is out of scope for a video
10
+ // translation skill. Use the runtime interop pattern from PR #214 to keep
11
+ // these components rendering through Remotion's React tree.
12
+
13
+ import React from "react";
14
+ import { Button } from "@mui/material";
15
+ import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
16
+
17
+ export const MuiDriven: React.FC = () => {
18
+ const frame = useCurrentFrame();
19
+ const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
20
+
21
+ return (
22
+ <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
23
+ <div style={{ opacity }}>
24
+ <Button variant="contained" color="primary">
25
+ Click me · frame {frame}
26
+ </Button>
27
+ </div>
28
+ </AbsoluteFill>
29
+ );
30
+ };
@@ -0,0 +1,38 @@
1
+ // T4 case 05 — Imports @remotion/lambda for distributed rendering config.
2
+ //
3
+ // Should be detected by lint_source.py as warning r2hf/lambda-import.
4
+ // The skill drops the Lambda code with a note (HF runs single-machine
5
+ // today) and translates the rest of the composition.
6
+ //
7
+ // Why this is a warning, not a blocker: @remotion/lambda config is
8
+ // orthogonal to the rendered composition — it's deployment configuration,
9
+ // not animation logic. Treating it as a hard blocker would refuse
10
+ // translation for compositions that are otherwise clean. The skill drops
11
+ // the Lambda calls in step 3 (Generate) and writes a TRANSLATION_NOTES.md
12
+ // entry so the user knows to set up HF rendering separately.
13
+
14
+ import React from "react";
15
+ import { renderMediaOnLambda } from "@remotion/lambda";
16
+ import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
17
+
18
+ export const LambdaConfigured: React.FC = () => {
19
+ const frame = useCurrentFrame();
20
+ const opacity = interpolate(frame, [0, 30], [0, 1]);
21
+ return (
22
+ <AbsoluteFill style={{ opacity }}>
23
+ <div>frame {frame}</div>
24
+ </AbsoluteFill>
25
+ );
26
+ };
27
+
28
+ // Rendered at scale via Lambda — no HF equivalent.
29
+ export async function renderViaLambda() {
30
+ return renderMediaOnLambda({
31
+ region: "us-east-1",
32
+ functionName: "remotion-render",
33
+ composition: "LambdaConfigured",
34
+ serveUrl: "https://example.com/bundle",
35
+ inputProps: {},
36
+ codec: "h264",
37
+ });
38
+ }
@@ -0,0 +1,36 @@
1
+ // T4 case 06 — Patterns that warn but don't block.
2
+ //
3
+ // Should be detected by lint_source.py with:
4
+ // - r2hf/delay-render (warning) — drop the call; HF handles asset readiness
5
+ // - r2hf/use-callback (warning) — decorative, drop the wrapper
6
+ // - r2hf/use-memo (warning) — decorative, drop the wrapper
7
+ //
8
+ // 0 blockers expected — the skill should still translate this composition
9
+ // after dropping the wrappers. delayRender is paired with continueRender via
10
+ // an empty-deps useEffect (mount-once side effect), which doesn't trip the
11
+ // use-effect-deps blocker.
12
+
13
+ import React, { useCallback, useMemo } from "react";
14
+ import { AbsoluteFill, delayRender, continueRender, useCurrentFrame, interpolate } from "remotion";
15
+
16
+ const handle = delayRender();
17
+ // Resolve the handle once at module load — no per-frame side effects.
18
+ queueMicrotask(() => continueRender(handle));
19
+
20
+ export const WarningsOnly: React.FC = () => {
21
+ const frame = useCurrentFrame();
22
+
23
+ // useCallback / useMemo — decorative for render-perf in React, no equivalent
24
+ // needed in the seek-driven HF model.
25
+ const opacity = useMemo(
26
+ () => interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" }),
27
+ [frame],
28
+ );
29
+ const onMount = useCallback(() => {}, []);
30
+
31
+ return (
32
+ <AbsoluteFill style={{ opacity }} onClick={onMount}>
33
+ <div>frame {frame}</div>
34
+ </AbsoluteFill>
35
+ );
36
+ };
@@ -0,0 +1,28 @@
1
+ // T4 case 07 — Locally-defined custom hook.
2
+ //
3
+ // Should be detected by lint_source.py as warning r2hf/custom-hook.
4
+ // 0 blockers expected — the skill can attempt translation if the hook body
5
+ // is pure (derives from props/frame alone).
6
+ //
7
+ // Why this is a warning: custom hooks vary widely in what they do. Some are
8
+ // pure derivations of useCurrentFrame (translatable — inline the body); some
9
+ // wrap useState/useEffect (blocker — but those will be caught by the other
10
+ // rules independently). The warning prompts the agent to inspect the body.
11
+
12
+ import React from "react";
13
+ import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
14
+
15
+ // Custom hook — pure derivation from frame, no state. Translates fine.
16
+ function useFadeIn(durationInFrames: number) {
17
+ const frame = useCurrentFrame();
18
+ return interpolate(frame, [0, durationInFrames], [0, 1], { extrapolateRight: "clamp" });
19
+ }
20
+
21
+ export const CustomHookDriven: React.FC = () => {
22
+ const opacity = useFadeIn(30);
23
+ return (
24
+ <AbsoluteFill style={{ opacity }}>
25
+ <div>fading in</div>
26
+ </AbsoluteFill>
27
+ );
28
+ };
@@ -0,0 +1,41 @@
1
+ // T4 case 08 — Multiple blockers + multiple warnings in one file.
2
+ //
3
+ // Should report:
4
+ // blockers: r2hf/use-state, r2hf/use-effect-deps, r2hf/third-party-react-ui
5
+ // warnings: r2hf/use-callback (also r2hf/delay-render via the import chain
6
+ // would only fire if delayRender is actually called)
7
+ //
8
+ // Tests that the linter aggregates findings correctly and does not stop at
9
+ // the first blocker.
10
+
11
+ import React, { useState, useEffect, useCallback } from "react";
12
+ import { AbsoluteFill, useCurrentFrame } from "remotion";
13
+ import { Card } from "@chakra-ui/react";
14
+
15
+ interface Item {
16
+ id: string;
17
+ label: string;
18
+ }
19
+
20
+ export const MixedBlockers: React.FC = () => {
21
+ const frame = useCurrentFrame();
22
+ const [items, setItems] = useState<Item[]>([]);
23
+
24
+ useEffect(() => {
25
+ fetch("/api/items")
26
+ .then((r) => r.json())
27
+ .then(setItems);
28
+ }, [frame]);
29
+
30
+ const onClick = useCallback(() => {
31
+ setItems((prev) => [...prev, { id: String(prev.length), label: "new" }]);
32
+ }, []);
33
+
34
+ return (
35
+ <AbsoluteFill onClick={onClick}>
36
+ {items.map((item) => (
37
+ <Card key={item.id}>{item.label}</Card>
38
+ ))}
39
+ </AbsoluteFill>
40
+ );
41
+ };
@@ -0,0 +1,85 @@
1
+ {
2
+ "tier": 4,
3
+ "name": "escape-hatch",
4
+ "description": "Lint-only fixture set. Each case demonstrates a Remotion pattern the skill cannot or should not translate cleanly. The skill is graded on whether lint_source.py emits the right finding for each case — there are no renders to compare. T4 passes when every case triggers its expected rule and no others.",
5
+ "cases": [
6
+ {
7
+ "file": "01-use-state.tsx",
8
+ "expected": {
9
+ "blockers": [{ "rule": "r2hf/use-state", "min_count": 1 }],
10
+ "warnings": [],
11
+ "skill_action": "refuse_translation_recommend_interop"
12
+ }
13
+ },
14
+ {
15
+ "file": "02-use-effect-deps.tsx",
16
+ "expected": {
17
+ "blockers": [{ "rule": "r2hf/use-effect-deps", "min_count": 1 }],
18
+ "warnings": [],
19
+ "skill_action": "refuse_translation_recommend_interop"
20
+ }
21
+ },
22
+ {
23
+ "file": "03-async-metadata.tsx",
24
+ "expected": {
25
+ "blockers": [{ "rule": "r2hf/async-metadata", "min_count": 1 }],
26
+ "warnings": [],
27
+ "skill_action": "refuse_translation_recommend_interop"
28
+ }
29
+ },
30
+ {
31
+ "file": "04-third-party-react.tsx",
32
+ "expected": {
33
+ "blockers": [{ "rule": "r2hf/third-party-react-ui", "min_count": 1 }],
34
+ "warnings": [],
35
+ "skill_action": "refuse_translation_recommend_interop"
36
+ }
37
+ },
38
+ {
39
+ "file": "05-lambda-config.tsx",
40
+ "expected": {
41
+ "blockers": [],
42
+ "warnings": [{ "rule": "r2hf/lambda-import", "min_count": 1 }],
43
+ "skill_action": "drop_lambda_code_translate_remainder_if_clean"
44
+ }
45
+ },
46
+ {
47
+ "file": "06-warnings-only.tsx",
48
+ "expected": {
49
+ "blockers": [],
50
+ "warnings": [
51
+ { "rule": "r2hf/delay-render", "min_count": 1 },
52
+ { "rule": "r2hf/use-callback", "min_count": 1 },
53
+ { "rule": "r2hf/use-memo", "min_count": 1 }
54
+ ],
55
+ "skill_action": "translate_after_dropping_wrappers"
56
+ }
57
+ },
58
+ {
59
+ "file": "07-custom-hook.tsx",
60
+ "expected": {
61
+ "blockers": [],
62
+ "warnings": [{ "rule": "r2hf/custom-hook", "min_count": 1 }],
63
+ "skill_action": "inline_hook_body_if_pure"
64
+ }
65
+ },
66
+ {
67
+ "file": "08-mixed.tsx",
68
+ "expected": {
69
+ "blockers": [
70
+ { "rule": "r2hf/use-state", "min_count": 1 },
71
+ { "rule": "r2hf/use-effect-deps", "min_count": 1 },
72
+ { "rule": "r2hf/third-party-react-ui", "min_count": 1 }
73
+ ],
74
+ "warnings": [{ "rule": "r2hf/use-callback", "min_count": 1 }],
75
+ "skill_action": "refuse_translation_recommend_interop"
76
+ }
77
+ }
78
+ ],
79
+ "totals": {
80
+ "expected_blocker_cases": 5,
81
+ "expected_warning_only_cases": 3,
82
+ "expected_total_blocker_findings_min": 7,
83
+ "expected_total_warning_findings_min": 6
84
+ }
85
+ }
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # validate.sh — assert lint_source.py output matches expected.json for every T4 case.
3
+ #
4
+ # T4 has no renders to diff. The skill is graded on whether it correctly
5
+ # refuses to translate each case (or drops only the lambda config in case 5,
6
+ # or warns appropriately in cases 6 and 7).
7
+ #
8
+ # Usage:
9
+ # ./validate.sh
10
+ # Exit 0 on pass.
11
+
12
+ set -euo pipefail
13
+
14
+ THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ SCRIPTS_DIR="$(cd "$THIS_DIR/../../../scripts" && pwd)"
16
+ EXPECTED="$THIS_DIR/expected.json"
17
+
18
+ if [[ ! -f "$SCRIPTS_DIR/lint_source.py" ]]; then
19
+ echo "error: lint_source.py not found at $SCRIPTS_DIR/lint_source.py" >&2
20
+ exit 2
21
+ fi
22
+ if [[ ! -f "$EXPECTED" ]]; then
23
+ echo "error: expected.json not found at $EXPECTED" >&2
24
+ exit 2
25
+ fi
26
+
27
+ # Drive lint_file() in-process so the per-case overhead is one Python startup,
28
+ # not N (8 cases × ~80 ms forking python3 was the dominant cost).
29
+ SCRIPTS_DIR="$SCRIPTS_DIR" \
30
+ THIS_DIR="$THIS_DIR" \
31
+ EXPECTED="$EXPECTED" \
32
+ python3 <<'PY'
33
+ import json
34
+ import os
35
+ import sys
36
+ from collections import Counter
37
+ from pathlib import Path
38
+
39
+ scripts_dir = Path(os.environ["SCRIPTS_DIR"])
40
+ this_dir = Path(os.environ["THIS_DIR"])
41
+ expected_path = Path(os.environ["EXPECTED"])
42
+ cases_dir = this_dir / "cases"
43
+
44
+ sys.path.insert(0, str(scripts_dir))
45
+ from lint_source import BLOCKER, WARNING, lint_file # noqa: E402
46
+
47
+ expected = json.loads(expected_path.read_text())
48
+
49
+ fails: list[str] = []
50
+ passes: list[str] = []
51
+
52
+ for case in expected["cases"]:
53
+ file_name = case["file"]
54
+ fixture = cases_dir / file_name
55
+ if not fixture.exists():
56
+ fails.append(f"{file_name}: fixture missing at {fixture}")
57
+ continue
58
+
59
+ findings = lint_file(fixture)
60
+ rule_counts: Counter[str] = Counter()
61
+ severity_by_rule: dict[str, str] = {}
62
+ for f in findings:
63
+ rule_counts[f.rule] += 1
64
+ severity_by_rule[f.rule] = f.severity
65
+
66
+ case_failed = False
67
+
68
+ def assert_rule(expected_entry, expected_severity_floor, kind):
69
+ global case_failed
70
+ rule = expected_entry["rule"]
71
+ min_count = expected_entry["min_count"]
72
+ actual = rule_counts[rule]
73
+ actual_severity = severity_by_rule.get(rule)
74
+ if actual < min_count:
75
+ fails.append(f"{file_name}: expected >={min_count} {kind} findings of rule {rule}, got {actual}")
76
+ case_failed = True
77
+ elif actual_severity not in expected_severity_floor:
78
+ fails.append(
79
+ f"{file_name}: rule {rule} found but severity={actual_severity!r} (expected {kind})"
80
+ )
81
+ case_failed = True
82
+
83
+ for entry in case["expected"]["blockers"]:
84
+ assert_rule(entry, {BLOCKER}, "blocker")
85
+ for entry in case["expected"]["warnings"]:
86
+ assert_rule(entry, {WARNING, BLOCKER}, "warning")
87
+
88
+ # Implied lint exit code: 1 when blockers are expected, 0 otherwise.
89
+ has_blockers = any(f.severity == BLOCKER for f in findings)
90
+ expected_has_blockers = bool(case["expected"]["blockers"])
91
+ if has_blockers != expected_has_blockers:
92
+ fails.append(
93
+ f"{file_name}: implied lint exit {1 if has_blockers else 0}, "
94
+ f"expected {1 if expected_has_blockers else 0} (blockers expected: {expected_has_blockers})"
95
+ )
96
+ case_failed = True
97
+
98
+ if not case_failed:
99
+ passes.append(file_name)
100
+
101
+ print(f"Passed: {len(passes)}")
102
+ for name in passes:
103
+ print(f" ✓ {name}")
104
+ if fails:
105
+ print(f"Failed: {len(fails)}")
106
+ for msg in fails:
107
+ print(f" ✗ {msg}")
108
+ sys.exit(1)
109
+ sys.exit(0)
110
+ PY