@hybridlabor-api/aos 4.6.6 → 4.7.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 (503) hide show
  1. package/.agents/agents.md +9 -3
  2. package/.agents/nodes.json +7 -2
  3. package/.agents/state.schema.json +26 -1
  4. package/.agents/vendor-manifest.json +56 -0
  5. package/.agents/workflows/startcycle-graph.md +2 -2
  6. package/.claude/agents/architect.md +7 -2
  7. package/.claude/agents/security-reviewer.md +1 -1
  8. package/.claude/agents/techlead.md +2 -0
  9. package/.claude/hooks/memb-inject.mjs +257 -135
  10. package/.claude/hooks/trail-relay.mjs +18 -0
  11. package/.claude/settings.json +70 -0
  12. package/.claude/workflows/startcycle-dispatch.mjs +203 -17
  13. package/.opencode/agents/architect.md +7 -2
  14. package/.opencode/agents/techlead.md +2 -0
  15. package/.opencode/plugins/bdb-aos.js +75 -0
  16. package/THIRD_PARTY_NOTICES.md +57 -1
  17. package/bin/aos-doctor.mjs +366 -0
  18. package/bin/aos-store.mjs +167 -0
  19. package/bin/aos-uninstall.mjs +55 -1
  20. package/docs/AUDIT_BRIEF_ECC_STORE.md +122 -0
  21. package/docs/PLAN_AOS_STORE_PHASE1.md +190 -0
  22. package/docs/handover/agenttrail-for-ao.md +58 -0
  23. package/docs/handover/deja-for-ao.md +51 -0
  24. package/installer.js +182 -38
  25. package/lib/aos-archify-contract.mjs +277 -0
  26. package/lib/ecc-store-index.json +1 -0
  27. package/mcp_config.json +8 -0
  28. package/mcps/mcsc/README.md +24 -0
  29. package/mcps/mcsc/package-lock.json +1234 -0
  30. package/mcps/mcsc/package.json +14 -0
  31. package/mcps/mcsc/packages/core/bin/mcsc.js +162 -0
  32. package/mcps/mcsc/packages/core/rulebook.default.yaml +49 -0
  33. package/mcps/mcsc/packages/core/schema/rulebook.schema.json +56 -0
  34. package/mcps/mcsc/packages/core/src/adapters/agy.js +107 -0
  35. package/mcps/mcsc/packages/core/src/adapters/codex.js +179 -0
  36. package/mcps/mcsc/packages/core/src/adapters/native.js +14 -0
  37. package/mcps/mcsc/packages/core/src/adapters/opencode.js +183 -0
  38. package/mcps/mcsc/packages/core/src/adapters/types.js +21 -0
  39. package/mcps/mcsc/packages/core/src/capabilities/buildOfferTable.js +34 -0
  40. package/mcps/mcsc/packages/core/src/capabilities/registry.js +99 -0
  41. package/mcps/mcsc/packages/core/src/capabilities/types.js +24 -0
  42. package/mcps/mcsc/packages/core/src/detect/cache.js +47 -0
  43. package/mcps/mcsc/packages/core/src/detect/inventory.js +73 -0
  44. package/mcps/mcsc/packages/core/src/detect/probes.js +158 -0
  45. package/mcps/mcsc/packages/core/src/detect/types.js +26 -0
  46. package/mcps/mcsc/packages/core/src/rulebook/load.js +53 -0
  47. package/mcps/mcsc/packages/core/src/rulebook/resolve.js +227 -0
  48. package/mcps/mcsc/packages/core/src/rulebook/rulebook.types.js +89 -0
  49. package/mcps/mcsc/packages/core/src/trail.js +17 -0
  50. package/mcps/mcsc/packages/mcp/server.js +302 -0
  51. package/mcps/mcsc/server.js +2 -0
  52. package/package.json +8 -2
  53. package/scripts/build-ecc-store-index.mjs +147 -0
  54. package/skills/basic/bdbmediastorm/SKILL.md +1 -1
  55. package/skills/basic/godmode-ui-ux/SKILL.md +2 -2
  56. package/skills/basic/godmode-ui-ux/references/anti-slop-rules.md +44 -40
  57. package/skills/basic/godmode-ui-ux/references/taste-archetypes.md +49 -0
  58. package/skills/basic/startcycle/SKILL.md +7 -0
  59. package/skills/basic/startcycle-graph/SKILL.md +25 -0
  60. package/skills/basic/startcycle-graph-user/SKILL.md +3 -0
  61. package/skills/basic/teamwork-preview/SKILL.md +2 -0
  62. package/skills/bdbrainstorm/SKILL.md +1 -0
  63. package/skills/global_config/agenttrail/LICENSE +21 -0
  64. package/skills/global_config/agenttrail/SKILL.md +48 -0
  65. package/skills/global_config/agenttrail/bin/agenttrail.mjs +1113 -0
  66. package/skills/global_config/agenttrail/public/index.html +662 -0
  67. package/skills/global_config/aos-project-init/SKILL.md +25 -5
  68. package/skills/global_config/aos-setup/SKILL.md +34 -1
  69. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +115 -1
  70. package/skills/global_config/archify/LICENSE +22 -0
  71. package/skills/global_config/archify/SKILL.md +140 -0
  72. package/skills/global_config/archify/THIRD_PARTY_NOTICES.md +69 -0
  73. package/skills/global_config/archify/assets/JetBrainsMono-OFL.txt +93 -0
  74. package/skills/global_config/archify/assets/template.html +14937 -0
  75. package/skills/global_config/archify/bin/archify.mjs +2139 -0
  76. package/skills/global_config/archify/bin/open-artifact.mjs +86 -0
  77. package/skills/global_config/archify/bin/preview.mjs +653 -0
  78. package/skills/global_config/archify/bin/visual-check.mjs +829 -0
  79. package/skills/global_config/archify/brand-marks/README.md +31 -0
  80. package/skills/global_config/archify/brand-marks/catalog.json +131 -0
  81. package/skills/global_config/archify/delta/architecture-delta.mjs +1232 -0
  82. package/skills/global_config/archify/examples/agent-run.lifecycle.json +60 -0
  83. package/skills/global_config/archify/examples/agent-tool-call.workflow.json +94 -0
  84. package/skills/global_config/archify/examples/async-job-roundtrip.sequence.json +61 -0
  85. package/skills/global_config/archify/examples/brand-aware-delivery.architecture.json +47 -0
  86. package/skills/global_config/archify/examples/cache-miss-request.sequence.json +82 -0
  87. package/skills/global_config/archify/examples/checkout-platform.base.architecture.json +31 -0
  88. package/skills/global_config/archify/examples/checkout-platform.head.architecture.json +31 -0
  89. package/skills/global_config/archify/examples/dataflow-product-analytics.html +15048 -0
  90. package/skills/global_config/archify/examples/deployment-release.lifecycle.json +49 -0
  91. package/skills/global_config/archify/examples/event-stream.dataflow.json +57 -0
  92. package/skills/global_config/archify/examples/incident-response.workflow.json +64 -0
  93. package/skills/global_config/archify/examples/lifecycle-agent-run.html +14983 -0
  94. package/skills/global_config/archify/examples/product-analytics.dataflow.json +76 -0
  95. package/skills/global_config/archify/examples/production-deployment.architecture.json +71 -0
  96. package/skills/global_config/archify/examples/release-delivery.workflow.json +62 -0
  97. package/skills/global_config/archify/examples/sequence-cache-miss-request.html +15063 -0
  98. package/skills/global_config/archify/examples/web-app-rendered.html +15012 -0
  99. package/skills/global_config/archify/examples/web-app.architecture.json +46 -0
  100. package/skills/global_config/archify/examples/workflow-agent-tool-call-rendered.html +15054 -0
  101. package/skills/global_config/archify/migrations/workflow-v2.mjs +279 -0
  102. package/skills/global_config/archify/package.json +39 -0
  103. package/skills/global_config/archify/recipes/scenarios.mjs +391 -0
  104. package/skills/global_config/archify/references/authoring-contract.md +247 -0
  105. package/skills/global_config/archify/references/brand-marks.md +65 -0
  106. package/skills/global_config/archify/references/delivery-contract.md +138 -0
  107. package/skills/global_config/archify/references/viewer-runtime.md +45 -0
  108. package/skills/global_config/archify/renderers/architecture/grid.mjs +62 -0
  109. package/skills/global_config/archify/renderers/architecture/render-architecture.mjs +1078 -0
  110. package/skills/global_config/archify/renderers/dataflow/README.md +104 -0
  111. package/skills/global_config/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  112. package/skills/global_config/archify/renderers/lifecycle/README.md +115 -0
  113. package/skills/global_config/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  114. package/skills/global_config/archify/renderers/sequence/README.md +114 -0
  115. package/skills/global_config/archify/renderers/sequence/render-sequence.mjs +464 -0
  116. package/skills/global_config/archify/renderers/shared/brand-marks.mjs +563 -0
  117. package/skills/global_config/archify/renderers/shared/cli.mjs +218 -0
  118. package/skills/global_config/archify/renderers/shared/desktop-readability.mjs +26 -0
  119. package/skills/global_config/archify/renderers/shared/diagnostics.mjs +160 -0
  120. package/skills/global_config/archify/renderers/shared/engineering-profiles.mjs +157 -0
  121. package/skills/global_config/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  122. package/skills/global_config/archify/renderers/shared/generated-validators.mjs +13 -0
  123. package/skills/global_config/archify/renderers/shared/geometry.mjs +1423 -0
  124. package/skills/global_config/archify/renderers/shared/i18n.mjs +595 -0
  125. package/skills/global_config/archify/renderers/shared/layout-report.mjs +40 -0
  126. package/skills/global_config/archify/renderers/shared/legend.mjs +217 -0
  127. package/skills/global_config/archify/renderers/shared/output-path.mjs +340 -0
  128. package/skills/global_config/archify/renderers/shared/repository-evidence.mjs +239 -0
  129. package/skills/global_config/archify/renderers/shared/repository-location.mjs +58 -0
  130. package/skills/global_config/archify/renderers/shared/text-fit.mjs +49 -0
  131. package/skills/global_config/archify/renderers/shared/utils.mjs +232 -0
  132. package/skills/global_config/archify/renderers/shared/validator.mjs +86 -0
  133. package/skills/global_config/archify/renderers/workflow/README.md +223 -0
  134. package/skills/global_config/archify/renderers/workflow/render-workflow.mjs +35 -0
  135. package/skills/global_config/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  136. package/skills/global_config/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  137. package/skills/global_config/archify/schemas/README.md +211 -0
  138. package/skills/global_config/archify/schemas/architecture.schema.json +178 -0
  139. package/skills/global_config/archify/schemas/common.schema.json +115 -0
  140. package/skills/global_config/archify/schemas/dataflow.schema.json +243 -0
  141. package/skills/global_config/archify/schemas/lifecycle.schema.json +266 -0
  142. package/skills/global_config/archify/schemas/sequence.schema.json +223 -0
  143. package/skills/global_config/archify/schemas/workflow.schema.json +428 -0
  144. package/skills/global_config/archify/scripts/check-render-output.mjs +916 -0
  145. package/skills/global_config/archify/scripts/generate-brand-marks.mjs +141 -0
  146. package/skills/global_config/archify/scripts/generate-validators.mjs +66 -0
  147. package/skills/global_config/archify/scripts/render-examples.mjs +26 -0
  148. package/skills/global_config/archify/skill-release.json +10 -0
  149. package/skills/global_config/ask-tim/SKILL.md +10 -0
  150. package/skills/global_config/bdb-security-audit/SKILL.md +109 -0
  151. package/skills/global_config/bdb-security-audit/references/audit-checklists.md +172 -0
  152. package/skills/global_config/bdb-security-audit/references/skylos-guide.md +139 -0
  153. package/skills/global_config/bdb-security-audit/references/xalgorix-bridge.md +110 -0
  154. package/skills/global_config/bdbdesignpro/SKILL.md +4 -4
  155. package/skills/global_config/bdbdesignpro/references/bdb-motion-tokens.md +52 -31
  156. package/skills/global_config/bdbresilience/SKILL.md +10 -0
  157. package/skills/global_config/brag/SKILL.md +179 -0
  158. package/skills/global_config/brag/assets/music/README.md +20 -0
  159. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
  160. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
  161. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
  162. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
  163. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
  164. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
  165. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
  166. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
  167. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
  168. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
  169. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
  170. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
  171. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
  172. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
  173. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
  174. package/skills/global_config/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
  175. package/skills/global_config/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
  176. package/skills/global_config/brag/assets/sfx/casino/card-place-1.ogg +0 -0
  177. package/skills/global_config/brag/assets/sfx/casino/card-place-2.ogg +0 -0
  178. package/skills/global_config/brag/assets/sfx/casino/card-place-3.ogg +0 -0
  179. package/skills/global_config/brag/assets/sfx/casino/card-place-4.ogg +0 -0
  180. package/skills/global_config/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
  181. package/skills/global_config/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
  182. package/skills/global_config/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
  183. package/skills/global_config/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
  184. package/skills/global_config/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
  185. package/skills/global_config/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
  186. package/skills/global_config/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
  187. package/skills/global_config/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
  188. package/skills/global_config/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
  189. package/skills/global_config/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
  190. package/skills/global_config/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
  191. package/skills/global_config/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
  192. package/skills/global_config/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
  193. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
  194. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
  195. package/skills/global_config/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
  196. package/skills/global_config/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
  197. package/skills/global_config/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
  198. package/skills/global_config/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
  199. package/skills/global_config/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
  200. package/skills/global_config/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
  201. package/skills/global_config/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
  202. package/skills/global_config/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
  203. package/skills/global_config/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
  204. package/skills/global_config/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
  205. package/skills/global_config/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
  206. package/skills/global_config/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
  207. package/skills/global_config/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
  208. package/skills/global_config/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
  209. package/skills/global_config/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
  210. package/skills/global_config/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
  211. package/skills/global_config/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
  212. package/skills/global_config/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
  213. package/skills/global_config/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
  214. package/skills/global_config/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
  215. package/skills/global_config/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
  216. package/skills/global_config/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
  217. package/skills/global_config/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
  218. package/skills/global_config/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
  219. package/skills/global_config/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
  220. package/skills/global_config/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
  221. package/skills/global_config/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
  222. package/skills/global_config/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
  223. package/skills/global_config/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
  224. package/skills/global_config/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
  225. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
  226. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
  227. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
  228. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
  229. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
  230. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
  231. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
  232. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
  233. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
  234. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
  235. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
  236. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
  237. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
  238. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
  239. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
  240. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
  241. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
  242. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
  243. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
  244. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
  245. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
  246. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
  247. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
  248. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
  249. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
  250. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
  251. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
  252. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
  253. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
  254. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
  255. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
  256. package/skills/global_config/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
  257. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
  258. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
  259. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
  260. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
  261. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
  262. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
  263. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
  264. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
  265. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
  266. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
  267. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
  268. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
  269. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
  270. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
  271. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
  272. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
  273. package/skills/global_config/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
  274. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
  275. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
  276. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
  277. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
  278. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
  279. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
  280. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
  281. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
  282. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
  283. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
  284. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
  285. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
  286. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
  287. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
  288. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
  289. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
  290. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
  291. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
  292. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
  293. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
  294. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
  295. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
  296. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
  297. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
  298. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
  299. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
  300. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
  301. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
  302. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
  303. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
  304. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
  305. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
  306. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
  307. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
  308. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
  309. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
  310. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
  311. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
  312. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
  313. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
  314. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
  315. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
  316. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
  317. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
  318. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
  319. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
  320. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
  321. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
  322. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
  323. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
  324. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
  325. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
  326. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
  327. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
  328. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
  329. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
  330. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
  331. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
  332. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
  333. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
  334. package/skills/global_config/brag/assets/sfx/interface/bong_001.ogg +0 -0
  335. package/skills/global_config/brag/assets/sfx/interface/click_001.ogg +0 -0
  336. package/skills/global_config/brag/assets/sfx/interface/click_002.ogg +0 -0
  337. package/skills/global_config/brag/assets/sfx/interface/click_003.ogg +0 -0
  338. package/skills/global_config/brag/assets/sfx/interface/click_004.ogg +0 -0
  339. package/skills/global_config/brag/assets/sfx/interface/click_005.ogg +0 -0
  340. package/skills/global_config/brag/assets/sfx/interface/drop_001.ogg +0 -0
  341. package/skills/global_config/brag/assets/sfx/interface/drop_002.ogg +0 -0
  342. package/skills/global_config/brag/assets/sfx/interface/drop_003.ogg +0 -0
  343. package/skills/global_config/brag/assets/sfx/interface/error_005.ogg +0 -0
  344. package/skills/global_config/brag/assets/sfx/interface/error_006.ogg +0 -0
  345. package/skills/global_config/brag/assets/sfx/interface/glitch_002.ogg +0 -0
  346. package/skills/global_config/brag/assets/sfx/interface/glitch_004.ogg +0 -0
  347. package/skills/global_config/brag/assets/sfx/interface/select_008.ogg +0 -0
  348. package/skills/global_config/brag/assets/sfx/interface/switch_001.ogg +0 -0
  349. package/skills/global_config/brag/assets/sfx/interface/switch_002.ogg +0 -0
  350. package/skills/global_config/brag/assets/sfx/interface/switch_004.ogg +0 -0
  351. package/skills/global_config/brag/assets/sfx/interface/switch_005.ogg +0 -0
  352. package/skills/global_config/brag/assets/sfx/interface/switch_006.ogg +0 -0
  353. package/skills/global_config/brag/assets/sfx/interface/switch_007.ogg +0 -0
  354. package/skills/global_config/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
  355. package/skills/global_config/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
  356. package/skills/global_config/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
  357. package/skills/global_config/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
  358. package/skills/global_config/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
  359. package/skills/global_config/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
  360. package/skills/global_config/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
  361. package/skills/global_config/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
  362. package/skills/global_config/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
  363. package/skills/global_config/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
  364. package/skills/global_config/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
  365. package/skills/global_config/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
  366. package/skills/global_config/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
  367. package/skills/global_config/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
  368. package/skills/global_config/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
  369. package/skills/global_config/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
  370. package/skills/global_config/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
  371. package/skills/global_config/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
  372. package/skills/global_config/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
  373. package/skills/global_config/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
  374. package/skills/global_config/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
  375. package/skills/global_config/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
  376. package/skills/global_config/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
  377. package/skills/global_config/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
  378. package/skills/global_config/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
  379. package/skills/global_config/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
  380. package/skills/global_config/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
  381. package/skills/global_config/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
  382. package/skills/global_config/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
  383. package/skills/global_config/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
  384. package/skills/global_config/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
  385. package/skills/global_config/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
  386. package/skills/global_config/brag/assets/sfx/sfx-analysis.json +15584 -0
  387. package/skills/global_config/brag/assets/sfx/sfx-analysis.md +75 -0
  388. package/skills/global_config/brag/assets/sfx/ui/click1.ogg +0 -0
  389. package/skills/global_config/brag/assets/sfx/ui/click2.ogg +0 -0
  390. package/skills/global_config/brag/assets/sfx/ui/click3.ogg +0 -0
  391. package/skills/global_config/brag/assets/sfx/ui/click4.ogg +0 -0
  392. package/skills/global_config/brag/assets/sfx/ui/click5.ogg +0 -0
  393. package/skills/global_config/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
  394. package/skills/global_config/brag/assets/sfx/ui/rollover1.ogg +0 -0
  395. package/skills/global_config/brag/assets/sfx/ui/rollover2.ogg +0 -0
  396. package/skills/global_config/brag/assets/sfx/ui/rollover4.ogg +0 -0
  397. package/skills/global_config/brag/assets/sfx/ui/rollover5.ogg +0 -0
  398. package/skills/global_config/brag/assets/sfx/ui/switch1.ogg +0 -0
  399. package/skills/global_config/brag/assets/sfx/ui/switch10.ogg +0 -0
  400. package/skills/global_config/brag/assets/sfx/ui/switch11.ogg +0 -0
  401. package/skills/global_config/brag/assets/sfx/ui/switch12.ogg +0 -0
  402. package/skills/global_config/brag/assets/sfx/ui/switch13.ogg +0 -0
  403. package/skills/global_config/brag/assets/sfx/ui/switch14.ogg +0 -0
  404. package/skills/global_config/brag/assets/sfx/ui/switch15.ogg +0 -0
  405. package/skills/global_config/brag/assets/sfx/ui/switch16.ogg +0 -0
  406. package/skills/global_config/brag/assets/sfx/ui/switch17.ogg +0 -0
  407. package/skills/global_config/brag/assets/sfx/ui/switch18.ogg +0 -0
  408. package/skills/global_config/brag/assets/sfx/ui/switch19.ogg +0 -0
  409. package/skills/global_config/brag/assets/sfx/ui/switch2.ogg +0 -0
  410. package/skills/global_config/brag/assets/sfx/ui/switch20.ogg +0 -0
  411. package/skills/global_config/brag/assets/sfx/ui/switch21.ogg +0 -0
  412. package/skills/global_config/brag/assets/sfx/ui/switch22.ogg +0 -0
  413. package/skills/global_config/brag/assets/sfx/ui/switch23.ogg +0 -0
  414. package/skills/global_config/brag/assets/sfx/ui/switch24.ogg +0 -0
  415. package/skills/global_config/brag/assets/sfx/ui/switch25.ogg +0 -0
  416. package/skills/global_config/brag/assets/sfx/ui/switch26.ogg +0 -0
  417. package/skills/global_config/brag/assets/sfx/ui/switch27.ogg +0 -0
  418. package/skills/global_config/brag/assets/sfx/ui/switch28.ogg +0 -0
  419. package/skills/global_config/brag/assets/sfx/ui/switch29.ogg +0 -0
  420. package/skills/global_config/brag/assets/sfx/ui/switch3.ogg +0 -0
  421. package/skills/global_config/brag/assets/sfx/ui/switch30.ogg +0 -0
  422. package/skills/global_config/brag/assets/sfx/ui/switch31.ogg +0 -0
  423. package/skills/global_config/brag/assets/sfx/ui/switch32.ogg +0 -0
  424. package/skills/global_config/brag/assets/sfx/ui/switch33.ogg +0 -0
  425. package/skills/global_config/brag/assets/sfx/ui/switch34.ogg +0 -0
  426. package/skills/global_config/brag/assets/sfx/ui/switch35.ogg +0 -0
  427. package/skills/global_config/brag/assets/sfx/ui/switch36.ogg +0 -0
  428. package/skills/global_config/brag/assets/sfx/ui/switch37.ogg +0 -0
  429. package/skills/global_config/brag/assets/sfx/ui/switch38.ogg +0 -0
  430. package/skills/global_config/brag/assets/sfx/ui/switch4.ogg +0 -0
  431. package/skills/global_config/brag/assets/sfx/ui/switch5.ogg +0 -0
  432. package/skills/global_config/brag/assets/sfx/ui/switch6.ogg +0 -0
  433. package/skills/global_config/brag/assets/sfx/ui/switch7.ogg +0 -0
  434. package/skills/global_config/brag/assets/sfx/ui/switch8.ogg +0 -0
  435. package/skills/global_config/brag/assets/sfx/ui/switch9.ogg +0 -0
  436. package/skills/global_config/brag/references/audio.md +307 -0
  437. package/skills/global_config/brag/references/step-1-inspect.md +133 -0
  438. package/skills/global_config/brag/references/step-2-plan.md +234 -0
  439. package/skills/global_config/brag/references/step-3-compose.md +210 -0
  440. package/skills/global_config/brag/references/step-4-deliver.md +168 -0
  441. package/skills/global_config/brag/references/tones.md +231 -0
  442. package/skills/global_config/brag/scripts/analyze_music_cues.py +309 -0
  443. package/skills/global_config/brag/scripts/pyproject.toml +14 -0
  444. package/skills/global_config/brag/scripts/uv.lock +819 -0
  445. package/skills/global_config/deja-memory/SKILL.md +42 -0
  446. package/skills/global_config/editable-design/LICENSE +202 -0
  447. package/skills/global_config/editable-design/README.md +34 -0
  448. package/skills/global_config/editable-design/SKILL.md +630 -0
  449. package/skills/global_config/editable-design/THIRD_PARTY_NOTICES.md +10 -0
  450. package/skills/global_config/editable-design/agents/openai.yaml +4 -0
  451. package/skills/global_config/editable-design/assets/editor/layer-editor.css +262 -0
  452. package/skills/global_config/editable-design/assets/editor/layer-editor.js +1929 -0
  453. package/skills/global_config/editable-design/assets/poster-starter/README.md +92 -0
  454. package/skills/global_config/editable-design/assets/poster-starter/assets/.gitkeep +0 -0
  455. package/skills/global_config/editable-design/assets/poster-starter/index.html +91 -0
  456. package/skills/global_config/editable-design/assets/poster-starter/poster.json +6 -0
  457. package/skills/global_config/editable-design/assets/poster-starter/reference/.gitkeep +1 -0
  458. package/skills/global_config/editable-design/assets/replay/icons/assets.png +0 -0
  459. package/skills/global_config/editable-design/assets/replay/icons/html.png +0 -0
  460. package/skills/global_config/editable-design/assets/replay/icons/input.png +0 -0
  461. package/skills/global_config/editable-design/assets/replay/icons/layers.png +0 -0
  462. package/skills/global_config/editable-design/assets/replay/icons/plan.png +0 -0
  463. package/skills/global_config/editable-design/assets/replay/icons/reference.png +0 -0
  464. package/skills/global_config/editable-design/assets/replay/icons/review.png +0 -0
  465. package/skills/global_config/editable-design/assets/replay/index.html +59 -0
  466. package/skills/global_config/editable-design/assets/replay/replay-motion.js +410 -0
  467. package/skills/global_config/editable-design/assets/replay/replay.css +380 -0
  468. package/skills/global_config/editable-design/assets/replay/replay.js +374 -0
  469. package/skills/global_config/editable-design/font-kit/package-lock.json +115 -0
  470. package/skills/global_config/editable-design/font-kit/package.json +19 -0
  471. package/skills/global_config/editable-design/pack.sh +20 -0
  472. package/skills/global_config/editable-design/references/asset-architecture.md +72 -0
  473. package/skills/global_config/editable-design/references/editor-pitfalls.md +70 -0
  474. package/skills/global_config/editable-design/references/editor-runtime.md +131 -0
  475. package/skills/global_config/editable-design/references/font-system.md +89 -0
  476. package/skills/global_config/editable-design/references/imagery.md +145 -0
  477. package/skills/global_config/editable-design/references/layout-typography.md +116 -0
  478. package/skills/global_config/editable-design/references/replay-contract.md +169 -0
  479. package/skills/global_config/editable-design/scripts/_browser.mjs +247 -0
  480. package/skills/global_config/editable-design/scripts/bake.mjs +87 -0
  481. package/skills/global_config/editable-design/scripts/build-replay.mjs +316 -0
  482. package/skills/global_config/editable-design/scripts/check-contract.mjs +293 -0
  483. package/skills/global_config/editable-design/scripts/check-fonts.sh +252 -0
  484. package/skills/global_config/editable-design/scripts/check-poster.sh +70 -0
  485. package/skills/global_config/editable-design/scripts/doctor.sh +78 -0
  486. package/skills/global_config/editable-design/scripts/explode.mjs +115 -0
  487. package/skills/global_config/editable-design/scripts/font-kit.mjs +187 -0
  488. package/skills/global_config/editable-design/scripts/import-asset.sh +23 -0
  489. package/skills/global_config/editable-design/scripts/init-poster.sh +64 -0
  490. package/skills/global_config/editable-design/scripts/install-font-kit.sh +13 -0
  491. package/skills/global_config/editable-design/scripts/package-lock.json +334 -0
  492. package/skills/global_config/editable-design/scripts/package.json +14 -0
  493. package/skills/global_config/editable-design/scripts/remove-chroma-key.py +201 -0
  494. package/skills/global_config/editable-design/scripts/render-poster.sh +202 -0
  495. package/skills/global_config/editable-design/scripts/trace.sh +159 -0
  496. package/skills/global_config/editable-design/scripts/verify-replay.mjs +97 -0
  497. package/skills/global_config/editable-design/scripts/verify.mjs +782 -0
  498. package/skills/global_config/editable-design/scripts/wire-editor.mjs +104 -0
  499. package/skills/global_config/editable-design/tests/fixtures/small-explode/index.html +86 -0
  500. package/skills/global_config/mcsc/SKILL.md +60 -0
  501. package/skills/global_config/plan-canvas/SKILL.md +8 -0
  502. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +31 -6
  503. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +4 -2
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+ """Turn a flat chroma-key image into a PNG or WebP with alpha."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ from pathlib import Path
8
+ import re
9
+ from statistics import median
10
+ import sys
11
+
12
+
13
+ def fail(message: str) -> None:
14
+ print(f"remove-chroma-key: {message}", file=sys.stderr)
15
+ raise SystemExit(2)
16
+
17
+
18
+ def load_pillow():
19
+ try:
20
+ from PIL import Image, ImageFilter
21
+ except ImportError:
22
+ fail("Pillow is required; install it with `python3 -m pip install pillow`.")
23
+ return Image, ImageFilter
24
+
25
+
26
+ def parse_color(value: str) -> tuple[int, int, int]:
27
+ match = re.fullmatch(r"#?([0-9a-fA-F]{6})", value.strip())
28
+ if not match:
29
+ fail("--key-color must be a six-digit RGB hex value such as #00ff00")
30
+ raw = match.group(1)
31
+ return tuple(int(raw[index : index + 2], 16) for index in (0, 2, 4))
32
+
33
+
34
+ def sample_key(image, mode: str) -> tuple[int, int, int]:
35
+ width, height = image.size
36
+ pixels = image.load()
37
+ samples: list[tuple[int, int, int]] = []
38
+ band = max(1, min(width, height, 6))
39
+
40
+ if mode == "corners":
41
+ patch = max(1, min(width, height, 12))
42
+ boxes = (
43
+ (0, 0, patch, patch),
44
+ (width - patch, 0, width, patch),
45
+ (0, height - patch, patch, height),
46
+ (width - patch, height - patch, width, height),
47
+ )
48
+ for left, top, right, bottom in boxes:
49
+ for y in range(top, bottom):
50
+ for x in range(left, right):
51
+ samples.append(tuple(pixels[x, y][:3]))
52
+ else:
53
+ step = max(1, min(width, height) // 256)
54
+ for x in range(0, width, step):
55
+ for offset in range(band):
56
+ samples.append(tuple(pixels[x, offset][:3]))
57
+ samples.append(tuple(pixels[x, height - 1 - offset][:3]))
58
+ for y in range(0, height, step):
59
+ for offset in range(band):
60
+ samples.append(tuple(pixels[offset, y][:3]))
61
+ samples.append(tuple(pixels[width - 1 - offset, y][:3]))
62
+
63
+ if not samples:
64
+ fail("could not sample a key color from the image border")
65
+ return tuple(int(round(median(pixel[channel] for pixel in samples))) for channel in range(3))
66
+
67
+
68
+ def smoothstep(value: float) -> float:
69
+ value = max(0.0, min(1.0, value))
70
+ return value * value * (3.0 - 2.0 * value)
71
+
72
+
73
+ def matte_alpha(distance: int, transparent: float, opaque: float) -> int:
74
+ if distance <= transparent:
75
+ return 0
76
+ if distance >= opaque:
77
+ return 255
78
+ return round(255 * smoothstep((distance - transparent) / (opaque - transparent)))
79
+
80
+
81
+ def spill_channels(key: tuple[int, int, int]) -> list[int]:
82
+ maximum = max(key)
83
+ if maximum < 128:
84
+ return []
85
+ return [index for index, value in enumerate(key) if value >= maximum - 16 and value >= 128]
86
+
87
+
88
+ def dominance(rgb: tuple[int, int, int], key: tuple[int, int, int]) -> float:
89
+ spills = spill_channels(key)
90
+ if not spills:
91
+ return 0.0
92
+ others = [index for index in range(3) if index not in spills]
93
+ key_strength = min(rgb[index] for index in spills)
94
+ other_strength = max((rgb[index] for index in others), default=0)
95
+ return float(key_strength - other_strength)
96
+
97
+
98
+ def dominance_alpha(rgb: tuple[int, int, int], key: tuple[int, int, int]) -> int:
99
+ amount = dominance(rgb, key)
100
+ if amount <= 0:
101
+ return 255
102
+ spills = spill_channels(key)
103
+ others = [index for index in range(3) if index not in spills]
104
+ other_strength = max((rgb[index] for index in others), default=0)
105
+ denominator = max(1.0, max(key) - other_strength)
106
+ return round(255 * (1.0 - min(1.0, amount / denominator)))
107
+
108
+
109
+ def despill(rgb: tuple[int, int, int], key: tuple[int, int, int], alpha: int) -> tuple[int, int, int]:
110
+ if alpha >= 252:
111
+ return rgb
112
+ spills = spill_channels(key)
113
+ others = [index for index in range(3) if index not in spills]
114
+ if not spills or not others:
115
+ return rgb
116
+ values = list(rgb)
117
+ cap = max(0, max(values[index] for index in others) - 1)
118
+ for index in spills:
119
+ values[index] = min(values[index], cap)
120
+ return tuple(values)
121
+
122
+
123
+ def build_parser() -> argparse.ArgumentParser:
124
+ parser = argparse.ArgumentParser(description=__doc__)
125
+ parser.add_argument("--input", required=True)
126
+ parser.add_argument("--out", required=True)
127
+ parser.add_argument("--key-color", default="#00ff00")
128
+ parser.add_argument("--auto-key", choices=("none", "corners", "border"), default="border")
129
+ parser.add_argument("--soft-matte", action="store_true")
130
+ parser.add_argument("--transparent-threshold", type=float, default=12.0)
131
+ parser.add_argument("--opaque-threshold", type=float, default=220.0)
132
+ parser.add_argument("--despill", action="store_true")
133
+ parser.add_argument("--edge-contract", type=int, default=0)
134
+ parser.add_argument("--edge-feather", type=float, default=0.0)
135
+ parser.add_argument("--force", action="store_true")
136
+ return parser
137
+
138
+
139
+ def main() -> None:
140
+ args = build_parser().parse_args()
141
+ source = Path(args.input)
142
+ output = Path(args.out)
143
+ if not source.is_file():
144
+ fail(f"input image not found: {source}")
145
+ if output.exists() and not args.force:
146
+ fail(f"output already exists: {output}; pass --force to replace it")
147
+ if output.suffix.lower() not in {".png", ".webp"}:
148
+ fail("--out must end in .png or .webp")
149
+ if not 0 <= args.transparent_threshold < args.opaque_threshold <= 255:
150
+ fail("matte thresholds must satisfy 0 <= transparent < opaque <= 255")
151
+ if not 0 <= args.edge_contract <= 16 or not 0 <= args.edge_feather <= 64:
152
+ fail("edge contract must be 0..16 and edge feather must be 0..64")
153
+
154
+ Image, ImageFilter = load_pillow()
155
+ with Image.open(source) as opened:
156
+ image = opened.convert("RGBA")
157
+ key = sample_key(image, args.auto_key) if args.auto_key != "none" else parse_color(args.key_color)
158
+ pixels = image.load()
159
+ width, height = image.size
160
+
161
+ for y in range(height):
162
+ for x in range(width):
163
+ red, green, blue, original_alpha = pixels[x, y]
164
+ rgb = (red, green, blue)
165
+ distance = max(abs(rgb[index] - key[index]) for index in range(3))
166
+ key_like = distance <= 32 or dominance(rgb, key) >= 16
167
+ alpha = 255
168
+ if key_like:
169
+ alpha = min(
170
+ matte_alpha(distance, args.transparent_threshold, args.opaque_threshold),
171
+ dominance_alpha(rgb, key),
172
+ )
173
+ alpha = round(alpha * original_alpha / 255)
174
+ if alpha <= 8:
175
+ pixels[x, y] = (0, 0, 0, 0)
176
+ continue
177
+ if args.despill and key_like:
178
+ red, green, blue = despill(rgb, key, alpha)
179
+ pixels[x, y] = (red, green, blue, alpha)
180
+
181
+ alpha_channel = image.getchannel("A")
182
+ for _ in range(args.edge_contract):
183
+ alpha_channel = alpha_channel.filter(ImageFilter.MinFilter(3))
184
+ if args.edge_feather:
185
+ alpha_channel = alpha_channel.filter(ImageFilter.GaussianBlur(args.edge_feather))
186
+ image.putalpha(alpha_channel)
187
+
188
+ histogram = alpha_channel.histogram()
189
+ total = width * height
190
+ transparent = sum(histogram[:13])
191
+ partial = sum(histogram[13:255])
192
+ output.parent.mkdir(parents=True, exist_ok=True)
193
+ image.save(output)
194
+ print(f"wrote {output}")
195
+ print(f"key color: #{key[0]:02x}{key[1]:02x}{key[2]:02x}")
196
+ print(f"transparent pixels: {transparent}/{total}")
197
+ print(f"partially transparent pixels: {partial}/{total}")
198
+
199
+
200
+ if __name__ == "__main__":
201
+ main()
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env bash
2
+ # Render the poster with the host browser, then read the real pixel dimensions
3
+ # back and compare them with the declared canvas.
4
+ #
5
+ # The read-back is not redundant: a browser silently produces a different size
6
+ # when the window and the content disagree, and the resulting image looks
7
+ # perfectly normal. Trust the artefact, not the renderer's exit code.
8
+ set -euo pipefail
9
+
10
+ find_browser() {
11
+ local candidates=(
12
+ "${EDITABLE_DESIGN_BROWSER:-}"
13
+ "${PUPPETEER_EXECUTABLE_PATH:-}"
14
+ "${POSTER_BROWSER:-}"
15
+ "${CHROME_PATH:-}"
16
+ )
17
+ local candidate
18
+ for candidate in "${candidates[@]}"; do
19
+ [[ -n "$candidate" && -x "$candidate" ]] && { printf '%s' "$candidate"; return 0; }
20
+ done
21
+ for candidate in chromium google-chrome chrome; do
22
+ local found
23
+ found="$(command -v "$candidate" 2>/dev/null || true)"
24
+ [[ -n "$found" ]] && { printf '%s' "$found"; return 0; }
25
+ done
26
+ candidates=(
27
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
28
+ "/Applications/Chromium.app/Contents/MacOS/Chromium"
29
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
30
+ "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
31
+ )
32
+ for candidate in "${candidates[@]}"; do
33
+ [[ -x "$candidate" ]] && { printf '%s' "$candidate"; return 0; }
34
+ done
35
+ return 1
36
+ }
37
+
38
+ if [[ "${1:-}" == "--probe" ]]; then
39
+ if browser="$(find_browser)"; then
40
+ echo "renderer $browser"
41
+ exit 0
42
+ fi
43
+ echo "renderer no Chromium-based browser found - rendering unavailable" >&2
44
+ echo " install Google Chrome, or set EDITABLE_DESIGN_BROWSER=<executable>" >&2
45
+ exit 1
46
+ fi
47
+
48
+ html="${1:?usage: render-poster.sh HTML [OUT_PNG] [--scale N | --dpi N]}"
49
+ shift
50
+ if [[ $# -gt 0 && "$1" != --* ]]; then
51
+ out="$1"
52
+ shift
53
+ else
54
+ out="$(cd "$(dirname "$html")" 2>/dev/null && pwd)/out/poster.png"
55
+ fi
56
+
57
+ scale=""
58
+ dpi=""
59
+ while [[ $# -gt 0 ]]; do
60
+ case "$1" in
61
+ --scale) scale="$2"; shift 2 ;;
62
+ --dpi) dpi="$2"; shift 2 ;;
63
+ *) echo "unknown argument: $1" >&2; exit 2 ;;
64
+ esac
65
+ done
66
+
67
+ test -f "$html" || { echo "no such file: $html" >&2; exit 2; }
68
+ project="$(cd "$(dirname "$html")" && pwd)"
69
+ config="$project/poster.json"
70
+
71
+ # poster.json's shape is fixed by the starter, so sed is enough and this script
72
+ # stays free of a JSON dependency.
73
+ read_num() {
74
+ sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p" "$2" | head -1
75
+ }
76
+
77
+ if [[ -f "$config" ]]; then
78
+ width="$(read_num width "$config")"
79
+ height="$(read_num height "$config")"
80
+ [[ -z "$scale" ]] && scale="$(read_num scale "$config")"
81
+ [[ -z "$dpi" ]] && dpi="$(read_num dpi "$config")"
82
+ fi
83
+
84
+ [[ -n "${width:-}" && -n "${height:-}" ]] || {
85
+ echo "cannot read canvas.width / canvas.height from poster.json" >&2
86
+ exit 2
87
+ }
88
+
89
+ # Print path converts at 96 px/inch, so a given dpi decides the scale factor.
90
+ if [[ -n "$dpi" && "$dpi" != "0" ]]; then
91
+ scale="$(awk -v d="$dpi" 'BEGIN { printf "%.4g", d / 96 }')"
92
+ fi
93
+ [[ -n "$scale" && "$scale" != "0" ]] || scale=2
94
+
95
+ browser="$(find_browser)" || {
96
+ echo "no Chromium-based browser found. Set EDITABLE_DESIGN_BROWSER=<executable>" >&2
97
+ exit 1
98
+ }
99
+
100
+ mkdir -p "$(dirname "$out")"
101
+ rm -f "$out"
102
+
103
+ # Never attach to the user's running browser, and never share a profile between
104
+ # poster renders. A shared profile makes concurrent jobs fight over its lock and
105
+ # lets a stale Chrome child poison the next run.
106
+ profile="$(mktemp -d "${TMPDIR:-/tmp}/poster-render-profile.XXXXXX")"
107
+ renderer_pid=""
108
+
109
+ profile_pids() {
110
+ ps -axo pid=,command= \
111
+ | awk -v marker="--user-data-dir=$profile" \
112
+ 'index($0, marker) && index($0, "awk -v marker=") == 0 { print $1 }'
113
+ }
114
+
115
+ stop_renderer() {
116
+ local pids=""
117
+ local _
118
+
119
+ pids="$(profile_pids)"
120
+ [[ -n "$pids" ]] && kill $pids 2>/dev/null || true
121
+
122
+ for _ in $(seq 1 20); do
123
+ pids="$(profile_pids)"
124
+ [[ -z "$pids" ]] && break
125
+ sleep 0.1
126
+ done
127
+
128
+ pids="$(profile_pids)"
129
+ [[ -n "$pids" ]] && kill -KILL $pids 2>/dev/null || true
130
+
131
+ if [[ -n "$renderer_pid" ]]; then
132
+ wait "$renderer_pid" 2>/dev/null || true
133
+ fi
134
+ }
135
+
136
+ cleanup() {
137
+ stop_renderer
138
+ if [[ -n "$profile" && -d "$profile" && "$profile" == *"/poster-render-profile."* ]]; then
139
+ rm -rf -- "$profile"
140
+ fi
141
+ }
142
+
143
+ trap cleanup EXIT INT TERM
144
+
145
+ "$browser" \
146
+ --headless \
147
+ --disable-gpu \
148
+ --hide-scrollbars \
149
+ --no-first-run \
150
+ --no-default-browser-check \
151
+ --disable-extensions \
152
+ --disable-background-networking \
153
+ --disable-sync \
154
+ --disable-default-apps \
155
+ --mute-audio \
156
+ --user-data-dir="$profile" \
157
+ --force-device-scale-factor="$scale" \
158
+ --window-size="${width},${height}" \
159
+ --screenshot="$out" \
160
+ "file://$(cd "$(dirname "$html")" && pwd)/$(basename "$html")" \
161
+ >/dev/null 2>&1 &
162
+ renderer_pid=$!
163
+
164
+ # headless occasionally does not exit on its own, so wait for the artefact
165
+ # rather than for the process.
166
+ for _ in $(seq 1 120); do
167
+ kill -0 "$renderer_pid" 2>/dev/null || break
168
+ [[ -s "$out" ]] && { sleep 0.4; break; }
169
+ sleep 0.5
170
+ done
171
+ stop_renderer
172
+ renderer_pid=""
173
+
174
+ test -s "$out" || { echo "render produced no $out" >&2; exit 3; }
175
+
176
+ # A PNG's IHDR carries width and height at bytes 16..23, big-endian. Reading
177
+ # them this way needs neither sips nor Pillow. macOS od emits a trailing blank
178
+ # line, so split on words instead of processing line by line.
179
+ png_dimensions() {
180
+ local file="$1"
181
+ # shellcheck disable=SC2046
182
+ set -- $(od -An -tu1 -j16 -N8 "$file")
183
+ printf '%d %d' \
184
+ "$(( $1 * 16777216 + $2 * 65536 + $3 * 256 + $4 ))" \
185
+ "$(( $5 * 16777216 + $6 * 65536 + $7 * 256 + $8 ))"
186
+ }
187
+
188
+ read -r actual_w actual_h <<<"$(png_dimensions "$out")"
189
+ expect_w="$(awk -v w="$width" -v s="$scale" 'BEGIN { printf "%d", w * s + 0.5 }')"
190
+ expect_h="$(awk -v h="$height" -v s="$scale" 'BEGIN { printf "%d", h * s + 0.5 }')"
191
+
192
+ if [[ "$actual_w" != "$expect_w" || "$actual_h" != "$expect_h" ]]; then
193
+ echo "render does not match the declared canvas: got ${actual_w}x${actual_h}, expected ${expect_w}x${expect_h}" >&2
194
+ echo "canvas ${width}x${height} @${scale}x. Check that the root element's width/height equal its data-canvas-* values." >&2
195
+ exit 3
196
+ fi
197
+
198
+ echo "$out ${actual_w}x${actual_h} (canvas ${width}x${height} @${scale}x)"
199
+ echo
200
+ echo "Next: read this image and say every piece of text on it out loud, line by line."
201
+ echo "Confirm nothing is clipped, covered, or carried in from the generated artwork."
202
+ echo "A DOM check cannot see any of that."
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env bash
2
+ # Append-only execution trace for one poster build.
3
+ #
4
+ # Writes into `.trace/` inside the poster project: a manifest, one JSONL event
5
+ # stream, per-step command output, and copies of intermediate artefacts. The
6
+ # point is that "what was decided, and why" survives after the conversation is
7
+ # gone — a rendered poster on its own cannot tell you which prompt produced its
8
+ # backdrop or why a canvas size was chosen.
9
+ #
10
+ # Pure bash on purpose: this plugin has no runtime dependencies, and adding one
11
+ # just to keep a log would be a bad trade.
12
+ set -euo pipefail
13
+
14
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ project_root="$(cd "$script_dir/.." && pwd)"
16
+ cd "$project_root"
17
+
18
+ trace_dir="${POSTER_TRACE_DIR:-$project_root/.trace}"
19
+ events="$trace_dir/events.jsonl"
20
+
21
+ now() { date +"%Y-%m-%dT%H:%M:%S%z" | sed 's/\(..\)$/:\1/'; }
22
+
23
+ # Minimal JSON string escaping: backslash, quote, then control characters.
24
+ esc() {
25
+ printf '%s' "$1" \
26
+ | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' \
27
+ | awk 'BEGIN { ORS = "" } { if (NR > 1) print "\\n"; print }'
28
+ }
29
+
30
+ require_trace() {
31
+ test -f "$events" || {
32
+ echo "trace: no active trace. Run trace.sh init first" >&2
33
+ exit 2
34
+ }
35
+ }
36
+
37
+ append() {
38
+ require_trace
39
+ local seq
40
+ seq=$(( $(wc -l <"$events") + 1 ))
41
+ printf '{"seq":%d,"ts":"%s",%s}\n' "$seq" "$(now)" "$1" >>"$events"
42
+ }
43
+
44
+ cmd="${1:-}"
45
+ shift || true
46
+
47
+ case "$cmd" in
48
+ init)
49
+ label="${1:-}"
50
+ mkdir -p "$trace_dir/steps" "$trace_dir/artifacts"
51
+ : >"$events"
52
+ printf '{\n "schema": "runlog/v1",\n "run_id": "%s",\n "created_at": "%s",\n "skill": "poster-building",\n "workspace": "%s",\n "label": "%s",\n "status": "running"\n}\n' \
53
+ "$(date +%Y%m%d-%H%M%S)" "$(now)" "$(esc "$PWD")" "$(esc "$label")" \
54
+ >"$trace_dir/manifest.json"
55
+ append "\"type\":\"run_start\",\"label\":\"$(esc "$label")\""
56
+ echo "$trace_dir"
57
+ ;;
58
+
59
+ start)
60
+ step="${1:?usage: trace.sh start STEP [TITLE]}"
61
+ append "\"type\":\"step_start\",\"step\":\"$(esc "$step")\",\"title\":\"$(esc "${2:-}")\""
62
+ ;;
63
+
64
+ done)
65
+ step="${1:?usage: trace.sh done STEP [STATUS] [NOTE]}"
66
+ append "\"type\":\"step_done\",\"step\":\"$(esc "$step")\",\"status\":\"${2:-ok}\",\"note\":\"$(esc "${3:-}")\""
67
+ ;;
68
+
69
+ decision)
70
+ key="${1:?usage: trace.sh decision KEY VALUE WHY [STEP]}"
71
+ value="${2:?}"
72
+ why="${3:?why is required — a decision without a reason is not worth recording}"
73
+ append "\"type\":\"decision\",\"step\":\"$(esc "${4:-}")\",\"key\":\"$(esc "$key")\",\"value\":\"$(esc "$value")\",\"why\":\"$(esc "$why")\""
74
+ ;;
75
+
76
+ note)
77
+ append "\"type\":\"note\",\"step\":\"$(esc "${2:-}")\",\"text\":\"$(esc "${1:?usage: trace.sh note TEXT [STEP]}")\""
78
+ ;;
79
+
80
+ artifact)
81
+ src="${1:?usage: trace.sh artifact PATH [STEP]}"
82
+ step="${2:-_unassigned}"
83
+ test -e "$src" || { echo "trace: no such path: $src" >&2; exit 2; }
84
+ dest="$trace_dir/artifacts/$step"
85
+ mkdir -p "$dest"
86
+ # -X drops extended attributes on macOS; GNU cp does not accept the same
87
+ # option, so keep the portable path everywhere else.
88
+ if [[ "$(uname -s)" == "Darwin" ]]; then
89
+ cp -RX "$src" "$dest/"
90
+ else
91
+ cp -R "$src" "$dest/"
92
+ fi
93
+ copied="$dest/$(basename "$src")"
94
+ find "$dest" -name '._*' -delete 2>/dev/null || true
95
+ if [[ -d "$copied" ]]; then
96
+ size=$(find "$copied" -type f -exec wc -c {} + | tail -1 | awk '{print $1}')
97
+ else
98
+ size=$(wc -c <"$copied" | tr -d ' ')
99
+ fi
100
+ append "\"type\":\"artifact\",\"step\":\"$(esc "$step")\",\"name\":\"$(esc "$(basename "$src")")\",\"path\":\"artifacts/$(esc "$step")/$(esc "$(basename "$src")")\",\"bytes\":$size"
101
+ ;;
102
+
103
+ run)
104
+ step="${1:?usage: trace.sh run STEP -- COMMAND...}"
105
+ shift
106
+ [[ "${1:-}" == "--" ]] && shift
107
+ [[ $# -gt 0 ]] || { echo "trace: no command given" >&2; exit 2; }
108
+
109
+ step_dir="$trace_dir/steps/$step"
110
+ mkdir -p "$step_dir"
111
+ index=$(( $(find "$step_dir" -name 'exec-*.json' | wc -l) + 1 ))
112
+ stem=$(printf 'exec-%02d' "$index")
113
+
114
+ append "\"type\":\"exec_start\",\"step\":\"$(esc "$step")\",\"label\":\"$stem\",\"command\":\"$(esc "$*")\""
115
+
116
+ started=$(date +%s)
117
+ set +e
118
+ "$@" >"$step_dir/$stem.stdout" 2>"$step_dir/$stem.stderr"
119
+ code=$?
120
+ set -e
121
+ elapsed=$(( $(date +%s) - started ))
122
+
123
+ printf '{\n "label": "%s",\n "command": "%s",\n "exit_code": %d,\n "duration_s": %d,\n "stdout": "%s.stdout",\n "stderr": "%s.stderr"\n}\n' \
124
+ "$stem" "$(esc "$*")" "$code" "$elapsed" "$stem" "$stem" >"$step_dir/$stem.json"
125
+
126
+ append "\"type\":\"exec_done\",\"step\":\"$(esc "$step")\",\"label\":\"$stem\",\"exit_code\":$code,\"duration_s\":$elapsed"
127
+
128
+ cat "$step_dir/$stem.stdout"
129
+ cat "$step_dir/$stem.stderr" >&2
130
+ exit $code
131
+ ;;
132
+
133
+ finish)
134
+ require_trace
135
+ status="${1:-ok}"
136
+ append "\"type\":\"run_finish\",\"status\":\"$status\""
137
+ sed -i.bak "s/\"status\": \"running\"/\"status\": \"$status\"/" "$trace_dir/manifest.json"
138
+ rm -f "$trace_dir/manifest.json.bak"
139
+ printf '{\n "status": "%s",\n "finished_at": "%s",\n "events": %d\n}\n' \
140
+ "$status" "$(now)" "$(wc -l <"$events")" >"$trace_dir/result.json"
141
+ echo "$trace_dir"
142
+ ;;
143
+
144
+ *)
145
+ cat >&2 <<'USAGE'
146
+ usage: trace.sh <command>
147
+
148
+ init [LABEL] start a trace in ./.trace/
149
+ start STEP [TITLE] mark a step as started
150
+ done STEP [ok|skip|fail] [NOTE] mark a step as finished
151
+ decision KEY VALUE WHY [STEP] record a choice and its reason (why is required)
152
+ note TEXT [STEP] record an observation
153
+ artifact PATH [STEP] copy an intermediate artefact into the trace
154
+ run STEP -- COMMAND... run a command, capturing output and exit code
155
+ finish [ok|fail] close the trace
156
+ USAGE
157
+ exit 2
158
+ ;;
159
+ esac
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import vm from 'node:vm';
5
+
6
+ const input = resolve(process.argv[2] || join(process.cwd(), 'replay', 'index.html'));
7
+ const replayDir = dirname(input);
8
+ const required = ['index.html', 'replay.css', 'replay.js', 'replay-motion.js', 'data.js'];
9
+ let failed = false;
10
+
11
+ for (const file of required) {
12
+ const ok = existsSync(join(replayDir, file));
13
+ console.log(` ${ok ? '✅' : '❌'} ${file}`);
14
+ failed ||= !ok;
15
+ }
16
+
17
+ if (!failed) {
18
+ const html = readFileSync(input, 'utf8');
19
+ const replayCSS = readFileSync(join(replayDir, 'replay.css'), 'utf8');
20
+ const replayJS = readFileSync(join(replayDir, 'replay.js'), 'utf8');
21
+ const motionJS = readFileSync(join(replayDir, 'replay-motion.js'), 'utf8');
22
+ const script = readFileSync(join(replayDir, 'data.js'), 'utf8');
23
+ const sandbox = { window: {} };
24
+ vm.runInNewContext(script, sandbox, { timeout: 1000 });
25
+ const data = sandbox.window.__POSTER_REPLAY__;
26
+ const ids = data?.stages?.map((stage) => stage.id) || [];
27
+ const expected = ['input', 'reference', 'plan', 'assets', 'html', 'review', 'layers'];
28
+ const schemaOK = data?.schema === 'poster-replay/v1';
29
+ const stagesOK = expected.every((id, index) => ids[index] === id);
30
+ const offlineOK = !/https?:\/\//i.test(html) && !/\bfetch\s*\(/.test(replayJS) && !/\bfetch\s*\(/.test(motionJS);
31
+ const sourceOK = data.stages.every((stage) => Array.isArray(stage.sources));
32
+ const phaseNavOK = html.includes('id="phase-nav"') && ['理解与规划', '生成与构建', '观察与修正'].every((label) => html.includes(label));
33
+ const phaseIconsOK = ['icons/plan.png', 'icons/html.png', 'icons/review.png'].every((source) => html.includes(source));
34
+ const noDefaultDetailOK = !html.includes('data-default-stage') && replayJS.includes('document.body.dataset.defaultStage ? stageById.get(document.body.dataset.defaultStage) : null');
35
+ const arrowSizingOK = /markerUnits=["']userSpaceOnUse["']/.test(replayJS);
36
+ const motionOK = html.includes('id="play-replay"') && ['runIntro', 'startAmbient', 'playReplay'].every((name) => motionJS.includes(name));
37
+ const reducedMotionOK = motionJS.includes('prefers-reduced-motion');
38
+ const flowIds = ['flow-input-reference', 'flow-reference-plan', 'flow-plan-assets', 'flow-assets-html', 'flow-html-review', 'flow-review-html-repair', 'flow-review-layers'];
39
+ const playbackPathOK = flowIds.every((id) => replayJS.includes(`id="${id}"`) && motionJS.includes(id));
40
+ const ambientCadenceOK = motionJS.includes('randomBetween(1800, 3000)') && motionJS.includes('randomBetween(2600, 4000)') && motionJS.includes('isRepair ? 1100 : 400') && motionJS.includes('strong ? 720 : 1150') && motionJS.includes('scale >= .20');
41
+ const introTimingOK = motionJS.includes('INTRO_DURATION = 4700') && replayCSS.includes('.is-intro .detail-panel');
42
+ const assetOverviewOK = replayJS.includes('asset-overview-grid') && replayCSS.includes('.asset-overview-grid');
43
+ const planMatrixOK = replayJS.includes('hasRecordedRects') && replayJS.includes('slots.length >= 5 ? 3') && replayJS.includes('slot.role || slot.form || slot.aspect || slot.prompt') && replayJS.includes('plan-empty');
44
+ const scaledPhaseNavOK = html.indexOf('id="canvas-world"') < html.indexOf('id="phase-nav"') && html.indexOf('id="phase-nav"') < html.indexOf('id="flow-lines"') && replayCSS.includes('width: 2540px');
45
+ const largePhaseNavOK = replayCSS.includes('width: 210px') && replayCSS.includes('min-height: 210px') && replayCSS.includes('width: 56px; height: 56px') && replayCSS.includes('font-size: 19px');
46
+ const assetStageOK = replayJS.includes("assets: { x: 800, y: 640, w: 480, h: 390 }") && replayJS.includes("shorten(first.prompt || '未记录素材 Prompt。', 320)");
47
+ const adaptiveAssetsOK = replayJS.includes('assetItems.length === 1') && replayJS.includes("className: 'asset single-asset'") && replayJS.includes('x: 260, y: 610, w: 500, h: 440') && replayJS.includes('x: 260 + (index % 2) * 260') && replayJS.includes('y: 610 + Math.floor(index / 2) * 230') && replayJS.includes('assetCount === 1') && replayJS.includes('.slice(0, assetCount)');
48
+ const assetShowcaseOK = motionJS.includes('scheduleAssetShowcase') && motionJS.includes('duration: 700') && motionJS.includes('randomBetween(4000, 6000)') && motionJS.includes("transform: 'scale(1.015)'");
49
+ const feedbackLabelOK = replayJS.includes('class="flow-label prove-label"') && replayCSS.includes('.flow-label text { fill: #47765c');
50
+ const layersStage = data.stages.find((stage) => stage.id === 'layers');
51
+ const liveLayerAnimationOK = !layersStage?.preview?.includes('motion=0') && replayJS.includes('可编辑分层动画(HTML)');
52
+ const synchronizedLayerAnimationOK = replayJS.includes('data-animation-src') && replayJS.includes('restartLayerAnimation') && replayJS.includes("id === 'layers'") && motionJS.includes('wait(3650)');
53
+ const motionHandshakeOK = replayJS.includes('__POSTER_REPLAY_VIEWER__') && motionJS.includes('__POSTER_REPLAY_VIEWER__') && motionJS.includes("document.body.dataset.motionState = 'recovered'");
54
+ const referencePlacementOK = replayJS.includes("reference: { x: 820, y: 110, w: 450, h: 330 }") && replayJS.includes("x: 1538, y: 110, w: 270, h: 360") && replayJS.includes('class="flow-artifact flow-path think"') && replayJS.includes('d="M1270 290 L2075 290"') && !replayJS.includes('L1900 70 C2030 70 2010 275 2075 275');
55
+ const rightColumnPlanOK = replayJS.includes("plan: { x: 2075, y: 110, w: 420, h: 360 }") && replayJS.includes("x: 2075, y: 670, w: 420, h: 560") && replayJS.includes("x: 2075, y: 1320, w: 420, h: 360");
56
+ const featuredMediaOK = replayJS.includes("x: 2075, y: 670, w: 420, h: 560") && replayJS.includes("x: 2075, y: 1320, w: 420, h: 360");
57
+ const detailPanelOK = replayCSS.includes('width: min(620px, calc(100vw - 36px))') && replayCSS.includes('#detail-content { display: flex') && replayCSS.includes('overflow-y: auto') && replayJS.includes("event.target.closest('.detail-panel,.media-lightbox')");
58
+ const mediaLightboxOK = html.includes('id="media-lightbox"') && ['openMode: \'image\'', "openMode: 'animation'", 'url.searchParams.delete(\'motion\')'].every((token) => replayJS.includes(token)) && replayCSS.includes('.media-lightbox.is-open');
59
+ const fullPngContainOK = replayCSS.includes('.media-lightbox-frame img { display: block; width: auto; height: auto; max-width: 100%; max-height: 100%; min-width: 0; min-height: 0; object-fit: contain; }');
60
+ console.log(` ${schemaOK ? '✅' : '❌'} poster-replay/v1 schema`);
61
+ console.log(` ${stagesOK ? '✅' : '❌'} seven ordered creative stages`);
62
+ console.log(` ${offlineOK ? '✅' : '❌'} offline file:// runtime`);
63
+ console.log(` ${sourceOK ? '✅' : '❌'} source provenance on every stage`);
64
+ console.log(` ${phaseNavOK ? '✅' : '❌'} three persistent phase controls`);
65
+ console.log(` ${phaseIconsOK ? '✅' : '❌'} phase control icons`);
66
+ console.log(` ${noDefaultDetailOK ? '✅' : '❌'} clean overview with no default detail`);
67
+ console.log(` ${arrowSizingOK ? '✅' : '❌'} bounded flow-arrow markers`);
68
+ console.log(` ${motionOK ? '✅' : '❌'} intro, ambient, and playback motion states`);
69
+ console.log(` ${reducedMotionOK ? '✅' : '❌'} reduced-motion fallback`);
70
+ console.log(` ${playbackPathOK ? '✅' : '❌'} playback path-to-stage mapping`);
71
+ console.log(` ${ambientCadenceOK ? '✅' : '❌'} faster ambient and repair cadence`);
72
+ console.log(` ${introTimingOK ? '✅' : '❌'} 4.7s intro with hidden detail panel`);
73
+ console.log(` ${assetOverviewOK ? '✅' : '❌'} all-assets visual overview`);
74
+ console.log(` ${planMatrixOK ? '✅' : '❌'} recorded-plan slot matrix fallback`);
75
+ console.log(` ${scaledPhaseNavOK ? '✅' : '❌'} phase controls scale with the canvas world`);
76
+ console.log(` ${largePhaseNavOK ? '✅' : '❌'} enlarged readable phase controls`);
77
+ console.log(` ${assetStageOK ? '✅' : '❌'} compact Assets stage with Prompt summary`);
78
+ console.log(` ${adaptiveAssetsOK ? '✅' : '❌'} adaptive single/full and multi 2x2 Assets previews`);
79
+ console.log(` ${assetShowcaseOK ? '✅' : '❌'} 700ms Assets gallery showcase with 4-6s rest`);
80
+ console.log(` ${feedbackLabelOK ? '✅' : '❌'} feedback label on green observation path`);
81
+ console.log(` ${liveLayerAnimationOK ? '✅' : '❌'} live HTML layer animation`);
82
+ console.log(` ${synchronizedLayerAnimationOK ? '✅' : '❌'} layer animation synchronized with intro and playback`);
83
+ console.log(` ${motionHandshakeOK ? '✅' : '❌'} resilient motion initialization handshake`);
84
+ console.log(` ${referencePlacementOK ? '✅' : '❌'} centered reference on a straight dashed connector`);
85
+ console.log(` ${rightColumnPlanOK ? '✅' : '❌'} Design Plan aligned with the right output column`);
86
+ console.log(` ${featuredMediaOK ? '✅' : '❌'} enlarged right-side output column`);
87
+ console.log(` ${detailPanelOK ? '✅' : '❌'} wide independently scrolling detail panel`);
88
+ console.log(` ${mediaLightboxOK ? '✅' : '❌'} image and animation lightbox behaviors`);
89
+ console.log(` ${fullPngContainOK ? '✅' : '❌'} full uncropped PNG in image lightbox`);
90
+ failed ||= !schemaOK || !stagesOK || !offlineOK || !sourceOK || !phaseNavOK || !phaseIconsOK || !noDefaultDetailOK || !arrowSizingOK || !motionOK || !reducedMotionOK || !playbackPathOK || !ambientCadenceOK || !introTimingOK || !assetOverviewOK || !planMatrixOK || !scaledPhaseNavOK || !largePhaseNavOK || !assetStageOK || !adaptiveAssetsOK || !assetShowcaseOK || !feedbackLabelOK || !liveLayerAnimationOK || !synchronizedLayerAnimationOK || !motionHandshakeOK || !referencePlacementOK || !rightColumnPlanOK || !featuredMediaOK || !detailPanelOK || !mediaLightboxOK || !fullPngContainOK;
91
+
92
+ const missing = data.stages.filter((stage) => /缺失|不完整|未生成/.test(stage.statusLabel));
93
+ if (missing.length) console.log(` ⚠️ missing evidence is shown, not reconstructed: ${missing.map((stage) => stage.id).join(', ')}`);
94
+ }
95
+
96
+ if (failed) process.exit(1);
97
+ console.log(`\n✅ Replay contract passed: ${input}\n`);