@prismer/runtime 2.0.8 → 2.2.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (633) hide show
  1. package/CHANGELOG.md +4004 -0
  2. package/README.md +34 -12
  3. package/apc/skills/FIELD-DICTIONARY.md +111 -0
  4. package/apc/skills/bug-reproduce/SKILL.md +150 -0
  5. package/apc/skills/bug-reproduce/skill.json +96 -0
  6. package/apc/skills/code-review/SKILL.md +198 -0
  7. package/apc/skills/code-review/skill.json +124 -0
  8. package/apc/skills/design-review/SKILL.md +122 -0
  9. package/apc/skills/design-review/skill.json +88 -0
  10. package/apc/skills/doc-sync/SKILL.md +168 -0
  11. package/apc/skills/doc-sync/skill.json +81 -0
  12. package/apc/skills/env-doctor/SKILL.md +194 -0
  13. package/apc/skills/env-doctor/skill.json +209 -0
  14. package/apc/skills/git-ops/SKILL.md +189 -0
  15. package/apc/skills/git-ops/skill.json +94 -0
  16. package/apc/skills/impact-trace/SKILL.md +168 -0
  17. package/apc/skills/impact-trace/skill.json +104 -0
  18. package/apc/skills/observability/SKILL.md +195 -0
  19. package/apc/skills/observability/skill.json +116 -0
  20. package/apc/skills/release-db-config-sync/SKILL.md +186 -0
  21. package/apc/skills/release-db-config-sync/skill.json +109 -0
  22. package/apc/skills/release-ota-promote/SKILL.md +195 -0
  23. package/apc/skills/release-ota-promote/skill.json +176 -0
  24. package/apc/skills/release-preflight/SKILL.md +174 -0
  25. package/apc/skills/release-preflight/skill.json +175 -0
  26. package/apc/skills/release-rollback/SKILL.md +214 -0
  27. package/apc/skills/release-rollback/skill.json +230 -0
  28. package/apc/skills/release-tag/SKILL.md +194 -0
  29. package/apc/skills/release-tag/skill.json +94 -0
  30. package/apc/skills/releasing-prod/SKILL.md +49 -0
  31. package/apc/skills/releasing-test/SKILL.md +144 -0
  32. package/apc/skills/sdk-release/SKILL.md +200 -0
  33. package/apc/skills/spec-intake/SKILL.md +169 -0
  34. package/apc/skills/spec-intake/skill.json +93 -0
  35. package/apc/skills/test-result-feedback/SKILL.md +239 -0
  36. package/apc/skills/test-result-feedback/skill.json +193 -0
  37. package/apc/skills/test-runner/SKILL.md +169 -0
  38. package/apc/skills/test-runner/skill.json +103 -0
  39. package/apc/skills/ui-align/SKILL.md +209 -0
  40. package/apc/skills/ui-align/skill.json +114 -0
  41. package/apc/skills/ui-canvas/SKILL.md +148 -0
  42. package/apc/skills/ui-canvas/skill.json +127 -0
  43. package/built-in-skills/agent-coordination/SKILL.md +77 -38
  44. package/built-in-skills/agent-meta/SKILL.md +12 -0
  45. package/built-in-skills/assets/SKILL.md +9 -7
  46. package/built-in-skills/browser-use/SKILL.md +93 -0
  47. package/built-in-skills/canvas-design/SKILL.md +4 -3
  48. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-OFL.txt +93 -0
  49. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
  50. package/built-in-skills/canvas-design/canvas-fonts/LICENSE-SOURCES.md +16 -0
  51. package/built-in-skills/claim-agent-ownership/SKILL.md +10 -5
  52. package/built-in-skills/claude-api/SKILL.md +2 -1
  53. package/built-in-skills/claude-api/python/claude-api/README.md +2 -2
  54. package/built-in-skills/claude-api/python/claude-api/tool-use.md +9 -5
  55. package/built-in-skills/claude-api/python/managed-agents/README.md +11 -1
  56. package/built-in-skills/claude-api/shared/error-codes.md +4 -4
  57. package/built-in-skills/claude-api/shared/managed-agents-client-patterns.md +4 -2
  58. package/built-in-skills/claude-api/shared/managed-agents-core.md +1 -2
  59. package/built-in-skills/claude-api/shared/managed-agents-events.md +41 -22
  60. package/built-in-skills/claude-api/shared/managed-agents-multiagent.md +5 -3
  61. package/built-in-skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +1 -1
  62. package/built-in-skills/claude-api/shared/managed-agents-tools.md +2 -3
  63. package/built-in-skills/claude-api/shared/model-migration.md +3 -3
  64. package/built-in-skills/claude-api/shared/models.md +1 -1
  65. package/built-in-skills/claude-api/typescript/claude-api/batches.md +1 -1
  66. package/built-in-skills/claude-api/typescript/claude-api/files-api.md +3 -1
  67. package/built-in-skills/claude-api/typescript/claude-api/tool-use.md +8 -2
  68. package/built-in-skills/claude-api/typescript/managed-agents/README.md +8 -2
  69. package/built-in-skills/codebase-design/DEEPENING.md +37 -0
  70. package/built-in-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  71. package/built-in-skills/codebase-design/LICENSE +21 -0
  72. package/built-in-skills/codebase-design/SKILL.md +130 -0
  73. package/built-in-skills/codebase-design/references/requesting-code-review/GUIDE.md +291 -0
  74. package/built-in-skills/codebase-design/references/requesting-code-review/LICENSE +21 -0
  75. package/built-in-skills/codebase-design/references/requesting-code-review/LICENSE.superpowers +21 -0
  76. package/built-in-skills/codebase-design/references/requesting-code-review/NOTICE.md +37 -0
  77. package/built-in-skills/codebase-design/references/sdlc-review/GUIDE.md +130 -0
  78. package/built-in-skills/codebase-design/references/sdlc-review/LICENSE +21 -0
  79. package/built-in-skills/codebase-design/references/sdlc-review/NOTICE.md +17 -0
  80. package/built-in-skills/codebase-design/references/simplify-code/GUIDE.md +269 -0
  81. package/built-in-skills/codebase-design/references/simplify-code/LICENSE +21 -0
  82. package/built-in-skills/codebase-design/references/simplify-code/NOTICE.md +37 -0
  83. package/built-in-skills/competitor-intelligence/LICENSE +21 -0
  84. package/built-in-skills/competitor-intelligence/NOTICE.md +6 -0
  85. package/built-in-skills/competitor-intelligence/SKILL.md +125 -0
  86. package/built-in-skills/competitor-intelligence/scripts/watch_state.py +160 -0
  87. package/built-in-skills/conversation-compaction/SKILL.md +117 -0
  88. package/built-in-skills/council-creator/SKILL.md +427 -0
  89. package/built-in-skills/diagnosing-bugs/LICENSE +21 -0
  90. package/built-in-skills/diagnosing-bugs/SKILL.md +148 -0
  91. package/built-in-skills/diagnosing-bugs/references/desktop-dom/GUIDE.md +64 -0
  92. package/built-in-skills/diagnosing-bugs/references/desktop-dom/LICENSE +21 -0
  93. package/built-in-skills/diagnosing-bugs/references/desktop-dom/NOTICE.md +17 -0
  94. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/GUIDE.md +410 -0
  95. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/LICENSE +21 -0
  96. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/LICENSE.superpowers +21 -0
  97. package/built-in-skills/diagnosing-bugs/references/systematic-debugging/NOTICE.md +37 -0
  98. package/built-in-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  99. package/built-in-skills/doc-coauthoring/SKILL.md +7 -0
  100. package/built-in-skills/doc-coauthoring/references/humanizer/GUIDE.md +647 -0
  101. package/built-in-skills/doc-coauthoring/references/humanizer/LICENSE +21 -0
  102. package/built-in-skills/doc-coauthoring/references/humanizer/LICENSE.hermes +21 -0
  103. package/built-in-skills/doc-coauthoring/references/humanizer/PROVENANCE.md +7 -0
  104. package/built-in-skills/document-actions/LICENSE +21 -0
  105. package/built-in-skills/document-actions/NOTICE.md +21 -0
  106. package/built-in-skills/document-actions/SKILL.md +46 -0
  107. package/built-in-skills/domain-modeling/ADR-FORMAT.md +47 -0
  108. package/built-in-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  109. package/built-in-skills/domain-modeling/LICENSE +21 -0
  110. package/built-in-skills/domain-modeling/SKILL.md +76 -0
  111. package/built-in-skills/evidence-citations/LICENSE +21 -0
  112. package/built-in-skills/evidence-citations/NOTICE.md +21 -0
  113. package/built-in-skills/evidence-citations/SKILL.md +46 -0
  114. package/built-in-skills/evidence-citations/references/citation-formats.md +68 -0
  115. package/built-in-skills/evidence-citations/references/grounding-rationale.md +64 -0
  116. package/built-in-skills/evidence-citations/scripts/sources.py +720 -0
  117. package/built-in-skills/frontend-design/SKILL.md +7 -0
  118. package/built-in-skills/frontend-design/references/claude-design/GUIDE.md +650 -0
  119. package/built-in-skills/frontend-design/references/claude-design/LICENSE +21 -0
  120. package/built-in-skills/frontend-design/references/claude-design/LICENSE.hermes +21 -0
  121. package/built-in-skills/frontend-design/references/claude-design/PROVENANCE.md +7 -0
  122. package/built-in-skills/human-approval/SKILL.md +17 -2
  123. package/built-in-skills/image-generate/SKILL.md +107 -301
  124. package/built-in-skills/image-generate/scripts/generate-and-deliver.mjs +310 -0
  125. package/built-in-skills/ingest/SKILL.md +13 -45
  126. package/built-in-skills/internal-comms/SKILL.md +1 -0
  127. package/built-in-skills/liteparse/SKILL.md +130 -110
  128. package/built-in-skills/mcp-builder/SKILL.md +1 -0
  129. package/built-in-skills/mcp-builder/reference/evaluation.md +11 -2
  130. package/built-in-skills/mcp-builder/reference/node_mcp_server.md +34 -5
  131. package/built-in-skills/mcp-builder/reference/python_mcp_server.md +6 -2
  132. package/built-in-skills/mcp-builder/scripts/connections.py +1 -1
  133. package/built-in-skills/mcp-builder/scripts/evaluation.py +52 -34
  134. package/built-in-skills/mcp-builder/scripts/requirements.txt +2 -2
  135. package/built-in-skills/meeting-actions/LICENSE +21 -0
  136. package/built-in-skills/meeting-actions/NOTICE.md +21 -0
  137. package/built-in-skills/meeting-actions/SKILL.md +48 -0
  138. package/built-in-skills/memory/SKILL.md +444 -55
  139. package/built-in-skills/memory/references/llm-wiki/GUIDE.md +468 -0
  140. package/built-in-skills/memory/references/llm-wiki/LICENSE +21 -0
  141. package/built-in-skills/memory/references/llm-wiki/NOTICE.md +4 -0
  142. package/built-in-skills/memory/references/llm-wiki/scripts/lint_wiki.py +84 -0
  143. package/built-in-skills/memory/references/llm-wiki/scripts/requirements.txt +1 -0
  144. package/built-in-skills/memory/references/llm-wiki/templates/SCHEMA.md +19 -0
  145. package/built-in-skills/memory/references/llm-wiki/templates/index.md +9 -0
  146. package/built-in-skills/memory/references/llm-wiki/templates/log.md +4 -0
  147. package/built-in-skills/memory-dream/SKILL.md +332 -0
  148. package/built-in-skills/memory-import/SKILL.md +51 -0
  149. package/built-in-skills/office-artifacts/NOTICE.md +15 -0
  150. package/built-in-skills/office-artifacts/SKILL.md +37 -4
  151. package/built-in-skills/office-artifacts/references/RESOURCE-LIMITS.md +44 -0
  152. package/built-in-skills/office-artifacts/references/document-generation/GUIDE.md +105 -0
  153. package/built-in-skills/office-artifacts/references/docx/GUIDE.md +196 -0
  154. package/built-in-skills/office-artifacts/references/docx/LICENSE +21 -0
  155. package/built-in-skills/office-artifacts/references/docx/NOTICE.md +21 -0
  156. package/built-in-skills/office-artifacts/references/docx/references/revisions-and-comments.md +88 -0
  157. package/built-in-skills/office-artifacts/references/docx/scripts/docx_comments.py +292 -0
  158. package/built-in-skills/office-artifacts/references/docx/scripts/docx_common.py +93 -0
  159. package/built-in-skills/office-artifacts/references/docx/scripts/docx_create.py +177 -0
  160. package/built-in-skills/office-artifacts/references/docx/scripts/docx_edit.py +250 -0
  161. package/built-in-skills/office-artifacts/references/docx/scripts/docx_read.py +169 -0
  162. package/built-in-skills/office-artifacts/references/docx/scripts/docx_revisions.py +149 -0
  163. package/built-in-skills/office-artifacts/references/docx/scripts/docx_template.py +70 -0
  164. package/built-in-skills/office-artifacts/references/docx/scripts/docx_validate.py +155 -0
  165. package/built-in-skills/office-artifacts/references/docx/tests/test_docx_skill.py +525 -0
  166. package/built-in-skills/office-artifacts/references/pdf/GUIDE.md +125 -0
  167. package/built-in-skills/office-artifacts/references/pdf/LICENSE +21 -0
  168. package/built-in-skills/office-artifacts/references/pdf/NOTICE.md +21 -0
  169. package/built-in-skills/office-artifacts/references/pdf/references/forms.md +99 -0
  170. package/built-in-skills/office-artifacts/references/pdf/references/nano-pdf-editing.md +40 -0
  171. package/built-in-skills/office-artifacts/references/pdf/references/ocr-extraction.md +165 -0
  172. package/built-in-skills/office-artifacts/references/pdf/scripts/_raster.py +87 -0
  173. package/built-in-skills/office-artifacts/references/pdf/scripts/extract_marker.py +95 -0
  174. package/built-in-skills/office-artifacts/references/pdf/scripts/extract_pymupdf.py +107 -0
  175. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_create.py +153 -0
  176. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_fill_form.py +97 -0
  177. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_form_layout.py +168 -0
  178. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_make_form.py +145 -0
  179. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_merge.py +50 -0
  180. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_meta.py +123 -0
  181. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_page_image.py +101 -0
  182. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_read.py +153 -0
  183. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_secure.py +71 -0
  184. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_split.py +86 -0
  185. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_stamp.py +145 -0
  186. package/built-in-skills/office-artifacts/references/pdf/scripts/pdf_watermark.py +51 -0
  187. package/built-in-skills/office-artifacts/references/pdf/tests/test_pdf_skill.py +414 -0
  188. package/built-in-skills/office-artifacts/references/powerpoint/GUIDE.md +220 -0
  189. package/built-in-skills/office-artifacts/references/powerpoint/LICENSE +21 -0
  190. package/built-in-skills/office-artifacts/references/powerpoint/NOTICE.md +21 -0
  191. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_create.py +214 -0
  192. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_edit.py +442 -0
  193. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_from_template.py +88 -0
  194. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_read.py +131 -0
  195. package/built-in-skills/office-artifacts/references/powerpoint/scripts/pptx_render.py +104 -0
  196. package/built-in-skills/office-artifacts/references/powerpoint/tests/test_powerpoint_skill.py +478 -0
  197. package/built-in-skills/office-artifacts/references/xlsx/GUIDE.md +196 -0
  198. package/built-in-skills/office-artifacts/references/xlsx/LICENSE +21 -0
  199. package/built-in-skills/office-artifacts/references/xlsx/NOTICE.md +21 -0
  200. package/built-in-skills/office-artifacts/references/xlsx/references/restructuring.md +74 -0
  201. package/built-in-skills/office-artifacts/references/xlsx/scripts/csv_to_xlsx.py +108 -0
  202. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_create.py +259 -0
  203. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_edit.py +269 -0
  204. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_read.py +160 -0
  205. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_recalc.py +111 -0
  206. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_restructure.py +418 -0
  207. package/built-in-skills/office-artifacts/references/xlsx/scripts/xlsx_to_csv.py +69 -0
  208. package/built-in-skills/office-artifacts/references/xlsx/tests/test_xlsx_skill.py +542 -0
  209. package/built-in-skills/office-artifacts/tests/test_resource_regressions.py +272 -0
  210. package/built-in-skills/okr/SKILL.md +154 -0
  211. package/built-in-skills/persona/SKILL.md +81 -0
  212. package/built-in-skills/persona-generator/SKILL.md +296 -0
  213. package/built-in-skills/pkf-svg/SKILL.md +258 -0
  214. package/built-in-skills/pkf-svg/references/architecture-diagram/GUIDE.md +148 -0
  215. package/built-in-skills/pkf-svg/references/architecture-diagram/LICENSE +21 -0
  216. package/built-in-skills/pkf-svg/references/architecture-diagram/LICENSE.hermes +21 -0
  217. package/built-in-skills/pkf-svg/references/architecture-diagram/PROVENANCE.md +7 -0
  218. package/built-in-skills/pkf-svg/references/architecture-diagram/templates/template.html +345 -0
  219. package/built-in-skills/pkf-writing/SKILL.md +258 -0
  220. package/built-in-skills/prismer-airtable/LICENSE +21 -0
  221. package/built-in-skills/prismer-airtable/NOTICE.md +11 -0
  222. package/built-in-skills/prismer-airtable/SKILL.md +248 -0
  223. package/built-in-skills/prismer-airtable/scripts/airtable_client.py +76 -0
  224. package/built-in-skills/prismer-apple-notes/LICENSE +21 -0
  225. package/built-in-skills/prismer-apple-notes/NOTICE.md +17 -0
  226. package/built-in-skills/prismer-apple-notes/SKILL.md +118 -0
  227. package/built-in-skills/prismer-apple-notes/scripts/readiness.py +47 -0
  228. package/built-in-skills/prismer-apple-reminders/LICENSE +21 -0
  229. package/built-in-skills/prismer-apple-reminders/NOTICE.md +17 -0
  230. package/built-in-skills/prismer-apple-reminders/SKILL.md +155 -0
  231. package/built-in-skills/prismer-apple-reminders/scripts/readiness.py +47 -0
  232. package/built-in-skills/prismer-ascii-video/LICENSE +21 -0
  233. package/built-in-skills/prismer-ascii-video/LICENSE.hermes +21 -0
  234. package/built-in-skills/prismer-ascii-video/PROVENANCE.md +7 -0
  235. package/built-in-skills/prismer-ascii-video/README.md +290 -0
  236. package/built-in-skills/prismer-ascii-video/SKILL.md +252 -0
  237. package/built-in-skills/prismer-ascii-video/provenance/category-DESCRIPTION.md +3 -0
  238. package/built-in-skills/prismer-ascii-video/references/architecture.md +807 -0
  239. package/built-in-skills/prismer-ascii-video/references/composition.md +900 -0
  240. package/built-in-skills/prismer-ascii-video/references/effects.md +1877 -0
  241. package/built-in-skills/prismer-ascii-video/references/inputs.md +693 -0
  242. package/built-in-skills/prismer-ascii-video/references/optimization.md +688 -0
  243. package/built-in-skills/prismer-ascii-video/references/scenes.md +1018 -0
  244. package/built-in-skills/prismer-ascii-video/references/shaders.md +1408 -0
  245. package/built-in-skills/prismer-ascii-video/references/troubleshooting.md +360 -0
  246. package/built-in-skills/prismer-baoyu-infographic/LICENSE +21 -0
  247. package/built-in-skills/prismer-baoyu-infographic/LICENSE.hermes +21 -0
  248. package/built-in-skills/prismer-baoyu-infographic/LICENSE.origin +21 -0
  249. package/built-in-skills/prismer-baoyu-infographic/PORT_NOTES.md +37 -0
  250. package/built-in-skills/prismer-baoyu-infographic/PROVENANCE.md +7 -0
  251. package/built-in-skills/prismer-baoyu-infographic/SKILL.md +240 -0
  252. package/built-in-skills/prismer-baoyu-infographic/references/analysis-framework.md +182 -0
  253. package/built-in-skills/prismer-baoyu-infographic/references/base-prompt.md +43 -0
  254. package/built-in-skills/prismer-baoyu-infographic/references/layouts/bento-grid.md +41 -0
  255. package/built-in-skills/prismer-baoyu-infographic/references/layouts/binary-comparison.md +48 -0
  256. package/built-in-skills/prismer-baoyu-infographic/references/layouts/bridge.md +41 -0
  257. package/built-in-skills/prismer-baoyu-infographic/references/layouts/circular-flow.md +41 -0
  258. package/built-in-skills/prismer-baoyu-infographic/references/layouts/comic-strip.md +41 -0
  259. package/built-in-skills/prismer-baoyu-infographic/references/layouts/comparison-matrix.md +41 -0
  260. package/built-in-skills/prismer-baoyu-infographic/references/layouts/dashboard.md +41 -0
  261. package/built-in-skills/prismer-baoyu-infographic/references/layouts/dense-modules.md +72 -0
  262. package/built-in-skills/prismer-baoyu-infographic/references/layouts/funnel.md +41 -0
  263. package/built-in-skills/prismer-baoyu-infographic/references/layouts/hierarchical-layers.md +48 -0
  264. package/built-in-skills/prismer-baoyu-infographic/references/layouts/hub-spoke.md +41 -0
  265. package/built-in-skills/prismer-baoyu-infographic/references/layouts/iceberg.md +41 -0
  266. package/built-in-skills/prismer-baoyu-infographic/references/layouts/isometric-map.md +41 -0
  267. package/built-in-skills/prismer-baoyu-infographic/references/layouts/jigsaw.md +41 -0
  268. package/built-in-skills/prismer-baoyu-infographic/references/layouts/linear-progression.md +48 -0
  269. package/built-in-skills/prismer-baoyu-infographic/references/layouts/periodic-table.md +41 -0
  270. package/built-in-skills/prismer-baoyu-infographic/references/layouts/story-mountain.md +41 -0
  271. package/built-in-skills/prismer-baoyu-infographic/references/layouts/structural-breakdown.md +48 -0
  272. package/built-in-skills/prismer-baoyu-infographic/references/layouts/tree-branching.md +41 -0
  273. package/built-in-skills/prismer-baoyu-infographic/references/layouts/venn-diagram.md +41 -0
  274. package/built-in-skills/prismer-baoyu-infographic/references/layouts/winding-roadmap.md +41 -0
  275. package/built-in-skills/prismer-baoyu-infographic/references/structured-content-template.md +244 -0
  276. package/built-in-skills/prismer-baoyu-infographic/references/styles/aged-academia.md +36 -0
  277. package/built-in-skills/prismer-baoyu-infographic/references/styles/bold-graphic.md +36 -0
  278. package/built-in-skills/prismer-baoyu-infographic/references/styles/chalkboard.md +61 -0
  279. package/built-in-skills/prismer-baoyu-infographic/references/styles/claymation.md +29 -0
  280. package/built-in-skills/prismer-baoyu-infographic/references/styles/corporate-memphis.md +29 -0
  281. package/built-in-skills/prismer-baoyu-infographic/references/styles/craft-handmade.md +44 -0
  282. package/built-in-skills/prismer-baoyu-infographic/references/styles/cyberpunk-neon.md +29 -0
  283. package/built-in-skills/prismer-baoyu-infographic/references/styles/hand-drawn-edu.md +63 -0
  284. package/built-in-skills/prismer-baoyu-infographic/references/styles/ikea-manual.md +29 -0
  285. package/built-in-skills/prismer-baoyu-infographic/references/styles/kawaii.md +29 -0
  286. package/built-in-skills/prismer-baoyu-infographic/references/styles/knolling.md +29 -0
  287. package/built-in-skills/prismer-baoyu-infographic/references/styles/lego-brick.md +29 -0
  288. package/built-in-skills/prismer-baoyu-infographic/references/styles/morandi-journal.md +60 -0
  289. package/built-in-skills/prismer-baoyu-infographic/references/styles/origami.md +29 -0
  290. package/built-in-skills/prismer-baoyu-infographic/references/styles/pixel-art.md +29 -0
  291. package/built-in-skills/prismer-baoyu-infographic/references/styles/pop-laboratory.md +48 -0
  292. package/built-in-skills/prismer-baoyu-infographic/references/styles/retro-pop-grid.md +47 -0
  293. package/built-in-skills/prismer-baoyu-infographic/references/styles/storybook-watercolor.md +29 -0
  294. package/built-in-skills/prismer-baoyu-infographic/references/styles/subway-map.md +29 -0
  295. package/built-in-skills/prismer-baoyu-infographic/references/styles/technical-schematic.md +36 -0
  296. package/built-in-skills/prismer-baoyu-infographic/references/styles/ui-wireframe.md +29 -0
  297. package/built-in-skills/prismer-blocked-page-recovery/LICENSE +21 -0
  298. package/built-in-skills/prismer-blocked-page-recovery/NOTICE.md +17 -0
  299. package/built-in-skills/prismer-blocked-page-recovery/SKILL.md +158 -0
  300. package/built-in-skills/prismer-blocked-page-recovery/scripts/recover_page.py +311 -0
  301. package/built-in-skills/prismer-box/LICENSE +21 -0
  302. package/built-in-skills/prismer-box/NOTICE.md +11 -0
  303. package/built-in-skills/prismer-box/SKILL.md +147 -0
  304. package/built-in-skills/prismer-box/references/bulk-operations.md +43 -0
  305. package/built-in-skills/prismer-box/references/cli-guide.md +133 -0
  306. package/built-in-skills/prismer-box/references/content-workflows.md +86 -0
  307. package/built-in-skills/prismer-box/references/hubs.md +77 -0
  308. package/built-in-skills/prismer-box/references/oauth-setup.md +92 -0
  309. package/built-in-skills/prismer-box/references/rest-api.md +37 -0
  310. package/built-in-skills/prismer-box/references/sdk-development.md +85 -0
  311. package/built-in-skills/prismer-box/references/search-and-ai.md +147 -0
  312. package/built-in-skills/prismer-box/references/troubleshooting.md +33 -0
  313. package/built-in-skills/prismer-box/references/webhooks-and-events.md +43 -0
  314. package/built-in-skills/prismer-codebase-inspection/LICENSE +21 -0
  315. package/built-in-skills/prismer-codebase-inspection/NOTICE.md +17 -0
  316. package/built-in-skills/prismer-codebase-inspection/SKILL.md +141 -0
  317. package/built-in-skills/prismer-computer-use/LICENSE +21 -0
  318. package/built-in-skills/prismer-computer-use/NOTICE.md +17 -0
  319. package/built-in-skills/prismer-computer-use/SKILL.md +372 -0
  320. package/built-in-skills/prismer-design-md/LICENSE +21 -0
  321. package/built-in-skills/prismer-design-md/LICENSE.hermes +21 -0
  322. package/built-in-skills/prismer-design-md/PROVENANCE.md +7 -0
  323. package/built-in-skills/prismer-design-md/SKILL.md +227 -0
  324. package/built-in-skills/prismer-design-md/templates/starter.md +99 -0
  325. package/built-in-skills/prismer-email-inbox-triage/LICENSE +21 -0
  326. package/built-in-skills/prismer-email-inbox-triage/NOTICE.md +11 -0
  327. package/built-in-skills/prismer-email-inbox-triage/SKILL.md +116 -0
  328. package/built-in-skills/prismer-findmy/LICENSE +21 -0
  329. package/built-in-skills/prismer-findmy/NOTICE.md +17 -0
  330. package/built-in-skills/prismer-findmy/SKILL.md +150 -0
  331. package/built-in-skills/prismer-findmy/scripts/readiness.py +47 -0
  332. package/built-in-skills/prismer-gif-search/LICENSE +21 -0
  333. package/built-in-skills/prismer-gif-search/LICENSE.hermes +21 -0
  334. package/built-in-skills/prismer-gif-search/PROVENANCE.md +7 -0
  335. package/built-in-skills/prismer-gif-search/SKILL.md +97 -0
  336. package/built-in-skills/prismer-gif-search/scripts/tenor.py +81 -0
  337. package/built-in-skills/prismer-github/LICENSE +21 -0
  338. package/built-in-skills/prismer-github/NOTICE.md +17 -0
  339. package/built-in-skills/prismer-github/SKILL.md +74 -0
  340. package/built-in-skills/prismer-github/references/auth.md +91 -0
  341. package/built-in-skills/prismer-github/references/ci-troubleshooting.md +193 -0
  342. package/built-in-skills/prismer-github/references/code-review.md +465 -0
  343. package/built-in-skills/prismer-github/references/conventional-commits.md +81 -0
  344. package/built-in-skills/prismer-github/references/github-api-cheatsheet.md +177 -0
  345. package/built-in-skills/prismer-github/references/issue-to-pr.md +74 -0
  346. package/built-in-skills/prismer-github/references/issues.md +336 -0
  347. package/built-in-skills/prismer-github/references/pr-workflow.md +346 -0
  348. package/built-in-skills/prismer-github/references/repo-management.md +484 -0
  349. package/built-in-skills/prismer-github/references/review-output-template.md +84 -0
  350. package/built-in-skills/prismer-github/scripts/ensure-gh.py +132 -0
  351. package/built-in-skills/prismer-github/scripts/gh-env.sh +25 -0
  352. package/built-in-skills/prismer-github/scripts/git-credential-token.py +68 -0
  353. package/built-in-skills/prismer-github/scripts/github-api.py +71 -0
  354. package/built-in-skills/prismer-github/scripts/github-context.py +28 -0
  355. package/built-in-skills/prismer-github/templates/bug-report.md +35 -0
  356. package/built-in-skills/prismer-github/templates/feature-request.md +31 -0
  357. package/built-in-skills/prismer-github/templates/pr-body-bugfix.md +35 -0
  358. package/built-in-skills/prismer-github/templates/pr-body-feature.md +33 -0
  359. package/built-in-skills/prismer-google-workspace/LICENSE +21 -0
  360. package/built-in-skills/prismer-google-workspace/NOTICE.md +11 -0
  361. package/built-in-skills/prismer-google-workspace/SKILL.md +369 -0
  362. package/built-in-skills/prismer-google-workspace/references/daily-brief.md +65 -0
  363. package/built-in-skills/prismer-google-workspace/references/gmail-search-syntax.md +63 -0
  364. package/built-in-skills/prismer-google-workspace/scripts/_hermes_home.py +17 -0
  365. package/built-in-skills/prismer-google-workspace/scripts/_private_state.py +39 -0
  366. package/built-in-skills/prismer-google-workspace/scripts/google_api.py +1339 -0
  367. package/built-in-skills/prismer-google-workspace/scripts/gws_bridge.py +121 -0
  368. package/built-in-skills/prismer-google-workspace/scripts/setup.py +563 -0
  369. package/built-in-skills/prismer-google-workspace/tests/test_productivity_regressions.py +214 -0
  370. package/built-in-skills/prismer-hermes-agent/LICENSE +21 -0
  371. package/built-in-skills/prismer-hermes-agent/NOTICE.md +17 -0
  372. package/built-in-skills/prismer-hermes-agent/SKILL.md +247 -0
  373. package/built-in-skills/prismer-hermes-agent/references/background-systems.md +108 -0
  374. package/built-in-skills/prismer-hermes-agent/references/cli-reference.md +158 -0
  375. package/built-in-skills/prismer-hermes-agent/references/configuration.md +96 -0
  376. package/built-in-skills/prismer-hermes-agent/references/contributor-guide.md +151 -0
  377. package/built-in-skills/prismer-hermes-agent/references/delegate-task-concurrency-diagnosis.md +47 -0
  378. package/built-in-skills/prismer-hermes-agent/references/desktop-plugins.md +215 -0
  379. package/built-in-skills/prismer-hermes-agent/references/native-mcp.md +351 -0
  380. package/built-in-skills/prismer-hermes-agent/references/petdex.md +82 -0
  381. package/built-in-skills/prismer-hermes-agent/references/portal-auth-for-third-party-apps.md +135 -0
  382. package/built-in-skills/prismer-hermes-agent/references/project-context-files.md +55 -0
  383. package/built-in-skills/prismer-hermes-agent/references/providers-and-models.md +81 -0
  384. package/built-in-skills/prismer-hermes-agent/references/security-privacy.md +73 -0
  385. package/built-in-skills/prismer-hermes-agent/references/slash-commands.md +119 -0
  386. package/built-in-skills/prismer-hermes-agent/references/themes.md +134 -0
  387. package/built-in-skills/prismer-hermes-agent/references/troubleshooting.md +87 -0
  388. package/built-in-skills/prismer-hermes-agent/references/tui-widgets.md +144 -0
  389. package/built-in-skills/prismer-hermes-agent/references/webhooks.md +219 -0
  390. package/built-in-skills/prismer-hermes-agent/references/windows-quirks.md +64 -0
  391. package/built-in-skills/prismer-hermes-agent/templates/clock.mjs +51 -0
  392. package/built-in-skills/prismer-hermes-agent/templates/plugin.js +97 -0
  393. package/built-in-skills/prismer-hermes-agent/templates/skin.yaml +49 -0
  394. package/built-in-skills/prismer-himalaya/LICENSE +21 -0
  395. package/built-in-skills/prismer-himalaya/NOTICE.md +11 -0
  396. package/built-in-skills/prismer-himalaya/SKILL.md +340 -0
  397. package/built-in-skills/prismer-himalaya/references/configuration.md +227 -0
  398. package/built-in-skills/prismer-himalaya/references/message-composition.md +206 -0
  399. package/built-in-skills/prismer-himalaya/scripts/compose_draft.py +48 -0
  400. package/built-in-skills/prismer-im-collab/SKILL.md +27 -7
  401. package/built-in-skills/prismer-imessage/LICENSE +21 -0
  402. package/built-in-skills/prismer-imessage/NOTICE.md +17 -0
  403. package/built-in-skills/prismer-imessage/SKILL.md +128 -0
  404. package/built-in-skills/prismer-imessage/scripts/readiness.py +47 -0
  405. package/built-in-skills/prismer-manim-video/LICENSE +21 -0
  406. package/built-in-skills/prismer-manim-video/LICENSE.hermes +21 -0
  407. package/built-in-skills/prismer-manim-video/PROVENANCE.md +7 -0
  408. package/built-in-skills/prismer-manim-video/README.md +23 -0
  409. package/built-in-skills/prismer-manim-video/SKILL.md +279 -0
  410. package/built-in-skills/prismer-manim-video/references/animation-design-thinking.md +161 -0
  411. package/built-in-skills/prismer-manim-video/references/animations.md +282 -0
  412. package/built-in-skills/prismer-manim-video/references/camera-and-3d.md +135 -0
  413. package/built-in-skills/prismer-manim-video/references/decorations.md +202 -0
  414. package/built-in-skills/prismer-manim-video/references/equations.md +216 -0
  415. package/built-in-skills/prismer-manim-video/references/graphs-and-data.md +163 -0
  416. package/built-in-skills/prismer-manim-video/references/mobjects.md +333 -0
  417. package/built-in-skills/prismer-manim-video/references/paper-explainer.md +255 -0
  418. package/built-in-skills/prismer-manim-video/references/production-quality.md +190 -0
  419. package/built-in-skills/prismer-manim-video/references/rendering.md +187 -0
  420. package/built-in-skills/prismer-manim-video/references/scene-planning.md +118 -0
  421. package/built-in-skills/prismer-manim-video/references/troubleshooting.md +135 -0
  422. package/built-in-skills/prismer-manim-video/references/updaters-and-trackers.md +260 -0
  423. package/built-in-skills/prismer-manim-video/references/visual-design.md +124 -0
  424. package/built-in-skills/prismer-manim-video/scripts/setup.sh +15 -0
  425. package/built-in-skills/prismer-maps/LICENSE +21 -0
  426. package/built-in-skills/prismer-maps/NOTICE.md +11 -0
  427. package/built-in-skills/prismer-maps/SKILL.md +240 -0
  428. package/built-in-skills/prismer-maps/scripts/maps_client.py +1324 -0
  429. package/built-in-skills/prismer-node-inspect-debugger/LICENSE +21 -0
  430. package/built-in-skills/prismer-node-inspect-debugger/NOTICE.md +17 -0
  431. package/built-in-skills/prismer-node-inspect-debugger/SKILL.md +347 -0
  432. package/built-in-skills/prismer-notion/LICENSE +21 -0
  433. package/built-in-skills/prismer-notion/NOTICE.md +11 -0
  434. package/built-in-skills/prismer-notion/SKILL.md +495 -0
  435. package/built-in-skills/prismer-notion/references/block-types.md +112 -0
  436. package/built-in-skills/prismer-obsidian/LICENSE +21 -0
  437. package/built-in-skills/prismer-obsidian/LICENSE.hermes +21 -0
  438. package/built-in-skills/prismer-obsidian/PROVENANCE.md +7 -0
  439. package/built-in-skills/prismer-obsidian/SKILL.md +80 -0
  440. package/built-in-skills/prismer-obsidian/provenance/category-DESCRIPTION.md +3 -0
  441. package/built-in-skills/prismer-obsidian/scripts/write_note.py +84 -0
  442. package/built-in-skills/prismer-p5js/LICENSE +21 -0
  443. package/built-in-skills/prismer-p5js/LICENSE.hermes +21 -0
  444. package/built-in-skills/prismer-p5js/PROVENANCE.md +7 -0
  445. package/built-in-skills/prismer-p5js/README.md +64 -0
  446. package/built-in-skills/prismer-p5js/SKILL.md +574 -0
  447. package/built-in-skills/prismer-p5js/references/animation.md +439 -0
  448. package/built-in-skills/prismer-p5js/references/color-systems.md +355 -0
  449. package/built-in-skills/prismer-p5js/references/core-api.md +410 -0
  450. package/built-in-skills/prismer-p5js/references/export-pipeline.md +534 -0
  451. package/built-in-skills/prismer-p5js/references/interaction.md +398 -0
  452. package/built-in-skills/prismer-p5js/references/shapes-and-geometry.md +300 -0
  453. package/built-in-skills/prismer-p5js/references/troubleshooting.md +532 -0
  454. package/built-in-skills/prismer-p5js/references/typography.md +302 -0
  455. package/built-in-skills/prismer-p5js/references/visual-effects.md +905 -0
  456. package/built-in-skills/prismer-p5js/references/webgl-and-3d.md +423 -0
  457. package/built-in-skills/prismer-p5js/scripts/export-frames.js +91 -0
  458. package/built-in-skills/prismer-p5js/scripts/render.sh +113 -0
  459. package/built-in-skills/prismer-p5js/scripts/serve.sh +25 -0
  460. package/built-in-skills/prismer-p5js/scripts/setup.sh +87 -0
  461. package/built-in-skills/prismer-p5js/templates/viewer.html +401 -0
  462. package/built-in-skills/prismer-popular-web-designs/LICENSE +21 -0
  463. package/built-in-skills/prismer-popular-web-designs/LICENSE.hermes +21 -0
  464. package/built-in-skills/prismer-popular-web-designs/LICENSE.origin +21 -0
  465. package/built-in-skills/prismer-popular-web-designs/PROVENANCE.md +7 -0
  466. package/built-in-skills/prismer-popular-web-designs/SKILL.md +218 -0
  467. package/built-in-skills/prismer-popular-web-designs/templates/airbnb.md +259 -0
  468. package/built-in-skills/prismer-popular-web-designs/templates/airtable.md +102 -0
  469. package/built-in-skills/prismer-popular-web-designs/templates/apple.md +326 -0
  470. package/built-in-skills/prismer-popular-web-designs/templates/bmw.md +193 -0
  471. package/built-in-skills/prismer-popular-web-designs/templates/cal.md +272 -0
  472. package/built-in-skills/prismer-popular-web-designs/templates/claude.md +325 -0
  473. package/built-in-skills/prismer-popular-web-designs/templates/clay.md +317 -0
  474. package/built-in-skills/prismer-popular-web-designs/templates/clickhouse.md +294 -0
  475. package/built-in-skills/prismer-popular-web-designs/templates/cohere.md +279 -0
  476. package/built-in-skills/prismer-popular-web-designs/templates/coinbase.md +142 -0
  477. package/built-in-skills/prismer-popular-web-designs/templates/composio.md +320 -0
  478. package/built-in-skills/prismer-popular-web-designs/templates/cursor.md +322 -0
  479. package/built-in-skills/prismer-popular-web-designs/templates/elevenlabs.md +278 -0
  480. package/built-in-skills/prismer-popular-web-designs/templates/expo.md +294 -0
  481. package/built-in-skills/prismer-popular-web-designs/templates/figma.md +233 -0
  482. package/built-in-skills/prismer-popular-web-designs/templates/framer.md +259 -0
  483. package/built-in-skills/prismer-popular-web-designs/templates/hashicorp.md +291 -0
  484. package/built-in-skills/prismer-popular-web-designs/templates/ibm.md +345 -0
  485. package/built-in-skills/prismer-popular-web-designs/templates/intercom.md +159 -0
  486. package/built-in-skills/prismer-popular-web-designs/templates/kraken.md +138 -0
  487. package/built-in-skills/prismer-popular-web-designs/templates/linear.app.md +380 -0
  488. package/built-in-skills/prismer-popular-web-designs/templates/lovable.md +311 -0
  489. package/built-in-skills/prismer-popular-web-designs/templates/minimax.md +270 -0
  490. package/built-in-skills/prismer-popular-web-designs/templates/mintlify.md +339 -0
  491. package/built-in-skills/prismer-popular-web-designs/templates/miro.md +121 -0
  492. package/built-in-skills/prismer-popular-web-designs/templates/mistral.ai.md +274 -0
  493. package/built-in-skills/prismer-popular-web-designs/templates/mongodb.md +279 -0
  494. package/built-in-skills/prismer-popular-web-designs/templates/notion.md +322 -0
  495. package/built-in-skills/prismer-popular-web-designs/templates/nvidia.md +306 -0
  496. package/built-in-skills/prismer-popular-web-designs/templates/ollama.md +280 -0
  497. package/built-in-skills/prismer-popular-web-designs/templates/opencode.ai.md +294 -0
  498. package/built-in-skills/prismer-popular-web-designs/templates/pinterest.md +243 -0
  499. package/built-in-skills/prismer-popular-web-designs/templates/posthog.md +269 -0
  500. package/built-in-skills/prismer-popular-web-designs/templates/raycast.md +281 -0
  501. package/built-in-skills/prismer-popular-web-designs/templates/replicate.md +274 -0
  502. package/built-in-skills/prismer-popular-web-designs/templates/resend.md +316 -0
  503. package/built-in-skills/prismer-popular-web-designs/templates/revolut.md +198 -0
  504. package/built-in-skills/prismer-popular-web-designs/templates/runwayml.md +257 -0
  505. package/built-in-skills/prismer-popular-web-designs/templates/sanity.md +370 -0
  506. package/built-in-skills/prismer-popular-web-designs/templates/sentry.md +275 -0
  507. package/built-in-skills/prismer-popular-web-designs/templates/spacex.md +207 -0
  508. package/built-in-skills/prismer-popular-web-designs/templates/spotify.md +259 -0
  509. package/built-in-skills/prismer-popular-web-designs/templates/stripe.md +335 -0
  510. package/built-in-skills/prismer-popular-web-designs/templates/supabase.md +268 -0
  511. package/built-in-skills/prismer-popular-web-designs/templates/superhuman.md +265 -0
  512. package/built-in-skills/prismer-popular-web-designs/templates/together.ai.md +276 -0
  513. package/built-in-skills/prismer-popular-web-designs/templates/uber.md +308 -0
  514. package/built-in-skills/prismer-popular-web-designs/templates/vercel.md +323 -0
  515. package/built-in-skills/prismer-popular-web-designs/templates/voltagent.md +336 -0
  516. package/built-in-skills/prismer-popular-web-designs/templates/warp.md +266 -0
  517. package/built-in-skills/prismer-popular-web-designs/templates/webflow.md +105 -0
  518. package/built-in-skills/prismer-popular-web-designs/templates/wise.md +186 -0
  519. package/built-in-skills/prismer-popular-web-designs/templates/x.ai.md +270 -0
  520. package/built-in-skills/prismer-popular-web-designs/templates/zapier.md +341 -0
  521. package/built-in-skills/prismer-product-price-monitor/LICENSE +21 -0
  522. package/built-in-skills/prismer-product-price-monitor/NOTICE.md +11 -0
  523. package/built-in-skills/prismer-product-price-monitor/SKILL.md +97 -0
  524. package/built-in-skills/prismer-product-price-monitor/scripts/watch_state.py +116 -0
  525. package/built-in-skills/prismer-python-debugpy/LICENSE +21 -0
  526. package/built-in-skills/prismer-python-debugpy/NOTICE.md +17 -0
  527. package/built-in-skills/prismer-python-debugpy/SKILL.md +377 -0
  528. package/built-in-skills/prismer-python-debugpy/scripts/dap_client.py +68 -0
  529. package/built-in-skills/prismer-songsee/LICENSE +21 -0
  530. package/built-in-skills/prismer-songsee/LICENSE.hermes +21 -0
  531. package/built-in-skills/prismer-songsee/PROVENANCE.md +7 -0
  532. package/built-in-skills/prismer-songsee/SKILL.md +91 -0
  533. package/built-in-skills/prismer-songwriting-and-ai-music/LICENSE +21 -0
  534. package/built-in-skills/prismer-songwriting-and-ai-music/LICENSE.hermes +21 -0
  535. package/built-in-skills/prismer-songwriting-and-ai-music/PROVENANCE.md +7 -0
  536. package/built-in-skills/prismer-songwriting-and-ai-music/SKILL.md +317 -0
  537. package/built-in-skills/prismer-spike/LICENSE +21 -0
  538. package/built-in-skills/prismer-spike/LICENSE.gsd +21 -0
  539. package/built-in-skills/prismer-spike/NOTICE.md +17 -0
  540. package/built-in-skills/prismer-spike/SKILL.md +212 -0
  541. package/built-in-skills/prismer-teams-meeting-pipeline/LICENSE +21 -0
  542. package/built-in-skills/prismer-teams-meeting-pipeline/NOTICE.md +11 -0
  543. package/built-in-skills/prismer-teams-meeting-pipeline/SKILL.md +156 -0
  544. package/built-in-skills/prismer-xurl/LICENSE +21 -0
  545. package/built-in-skills/prismer-xurl/LICENSE.hermes +21 -0
  546. package/built-in-skills/prismer-xurl/PROVENANCE.md +7 -0
  547. package/built-in-skills/prismer-xurl/SKILL.md +444 -0
  548. package/built-in-skills/prismer-xurl/provenance/category-DESCRIPTION.md +3 -0
  549. package/built-in-skills/prismer-youtube-content/LICENSE +21 -0
  550. package/built-in-skills/prismer-youtube-content/LICENSE.hermes +21 -0
  551. package/built-in-skills/prismer-youtube-content/PROVENANCE.md +7 -0
  552. package/built-in-skills/prismer-youtube-content/SKILL.md +86 -0
  553. package/built-in-skills/prismer-youtube-content/provenance/category-DESCRIPTION.md +3 -0
  554. package/built-in-skills/prismer-youtube-content/references/output-formats.md +56 -0
  555. package/built-in-skills/prismer-youtube-content/scripts/fetch_transcript.py +146 -0
  556. package/built-in-skills/proactivity/SKILL.md +84 -0
  557. package/built-in-skills/remotion/SKILL.md +431 -0
  558. package/built-in-skills/research-arxiv/LICENSE +21 -0
  559. package/built-in-skills/research-arxiv/NOTICE.md +4 -0
  560. package/built-in-skills/research-arxiv/SKILL.md +263 -0
  561. package/built-in-skills/research-arxiv/scripts/search_arxiv.py +111 -0
  562. package/built-in-skills/role-builder/SKILL.md +209 -0
  563. package/built-in-skills/role-builder/scripts/author-role.mjs +350 -0
  564. package/built-in-skills/role-builder/scripts/ingest-role.mjs +121 -0
  565. package/built-in-skills/role-builder/scripts/instantiate-and-run.mjs +304 -0
  566. package/built-in-skills/role-builder/scripts/operation-harness.mjs +269 -0
  567. package/built-in-skills/skill-creator/LICENSE.prismer +21 -0
  568. package/built-in-skills/skill-creator/NOTICE.md +18 -0
  569. package/built-in-skills/skill-creator/SKILL.md +182 -421
  570. package/built-in-skills/skill-creator/eval-viewer/generate_review.py +20 -31
  571. package/built-in-skills/skill-creator/eval-viewer/viewer.html +35 -23
  572. package/built-in-skills/skill-creator/references/external-library-import.md +110 -0
  573. package/built-in-skills/skill-creator/references/hermes-agent-skill-authoring/GUIDE.md +220 -0
  574. package/built-in-skills/skill-creator/references/hermes-agent-skill-authoring/LICENSE +21 -0
  575. package/built-in-skills/skill-creator/references/skill-authoring/GUIDE.md +134 -0
  576. package/built-in-skills/{skill-authoring/skill.json → skill-creator/references/skill-authoring/draft-contract.json} +3 -3
  577. package/built-in-skills/skill-creator/references/skill-builder/GUIDE.md +167 -0
  578. package/built-in-skills/skill-creator/references/skill-builder/scripts/ingest.mjs +93 -0
  579. package/built-in-skills/skill-creator/scripts/aggregate_benchmark.py +3 -2
  580. package/built-in-skills/skill-creator/scripts/import-library.mjs +498 -0
  581. package/built-in-skills/skill-creator/scripts/improve_description.py +14 -16
  582. package/built-in-skills/skill-creator/scripts/package_skill.py +16 -9
  583. package/built-in-skills/skill-creator/scripts/quick_validate.py +22 -5
  584. package/built-in-skills/skill-creator/scripts/run_eval.py +63 -144
  585. package/built-in-skills/skill-creator/scripts/run_loop.py +24 -16
  586. package/built-in-skills/slack-gif-creator/SKILL.md +20 -0
  587. package/built-in-skills/slack-gif-creator/core/gif_builder.py +13 -2
  588. package/built-in-skills/slack-gif-creator/core/validators.py +17 -7
  589. package/built-in-skills/tasks/SKILL.md +39 -24
  590. package/built-in-skills/tdd/LICENSE +21 -0
  591. package/built-in-skills/tdd/SKILL.md +117 -0
  592. package/built-in-skills/tdd/mocking.md +59 -0
  593. package/built-in-skills/tdd/refactoring.md +10 -0
  594. package/built-in-skills/tdd/references/test-driven-development/GUIDE.md +356 -0
  595. package/built-in-skills/tdd/references/test-driven-development/LICENSE +21 -0
  596. package/built-in-skills/tdd/references/test-driven-development/LICENSE.superpowers +21 -0
  597. package/built-in-skills/tdd/references/test-driven-development/NOTICE.md +37 -0
  598. package/built-in-skills/tdd/tests.md +61 -0
  599. package/built-in-skills/team/SKILL.md +3 -2
  600. package/built-in-skills/web-artifacts-builder/SKILL.md +4 -3
  601. package/built-in-skills/web-artifacts-builder/scripts/bundle-artifact.sh +9 -7
  602. package/built-in-skills/web-artifacts-builder/scripts/init-artifact.sh +28 -55
  603. package/built-in-skills/webapp-qa/LICENSE +21 -0
  604. package/built-in-skills/webapp-qa/NOTICE.md +37 -0
  605. package/built-in-skills/webapp-qa/SKILL.md +41 -0
  606. package/built-in-skills/webapp-qa/references/dogfood/GUIDE.md +161 -0
  607. package/built-in-skills/webapp-qa/references/issue-taxonomy.md +109 -0
  608. package/built-in-skills/webapp-qa/templates/dogfood-report-template.md +86 -0
  609. package/built-in-skills/webapp-testing/SKILL.md +10 -2
  610. package/built-in-skills/webapp-testing/examples/console_logging.py +10 -4
  611. package/built-in-skills/webapp-testing/examples/element_discovery.py +11 -4
  612. package/built-in-skills/webapp-testing/examples/static_html_automation.py +14 -9
  613. package/built-in-skills/webapp-testing/scripts/with_server.py +34 -10
  614. package/built-in-skills/wechat-pay/SKILL.md +59 -0
  615. package/built-in-skills/weekly-review/LICENSE +21 -0
  616. package/built-in-skills/weekly-review/NOTICE.md +21 -0
  617. package/built-in-skills/weekly-review/SKILL.md +49 -0
  618. package/components/eaas-pi-components.manifest.json +37 -0
  619. package/dist/cli.cjs +75799 -20210
  620. package/dist/cli.js +75900 -20248
  621. package/dist/index.cjs +76159 -20082
  622. package/dist/index.d.cts +4766 -766
  623. package/dist/index.d.ts +4766 -766
  624. package/dist/index.js +76040 -19960
  625. package/package.json +42 -6
  626. package/plugins/memory/prismer/__init__.py +1215 -0
  627. package/plugins/memory/prismer/plugin.yaml +8 -0
  628. package/plugins/memory/prismer/tool-schemas.generated.json +257 -0
  629. package/plugins/tools/prismer-recall/__init__.py +282 -0
  630. package/plugins/tools/prismer-recall/plugin.yaml +15 -0
  631. package/built-in-skills/memory-curation/SKILL.md +0 -135
  632. package/built-in-skills/skill-authoring/SKILL.md +0 -124
  633. package/built-in-skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
@@ -0,0 +1,900 @@
1
+ # Composition & Brightness Reference
2
+
3
+ The composable system is the core of visual complexity. It operates at three levels: pixel-level blend modes, multi-grid composition, and adaptive brightness management. This document covers all three, plus the masking/stencil system for spatial control.
4
+
5
+ > **See also:** architecture.md · effects.md · scenes.md · shaders.md · troubleshooting.md
6
+
7
+ ## Pixel-Level Blend Modes
8
+
9
+ ### The `blend_canvas()` Function
10
+
11
+ All blending operates on full pixel canvases (`uint8 H,W,3`). Internally converts to float32 [0,1] for precision, blends, lerps by opacity, converts back.
12
+
13
+ ```python
14
+ def blend_canvas(base, top, mode="normal", opacity=1.0):
15
+ af = base.astype(np.float32) / 255.0
16
+ bf = top.astype(np.float32) / 255.0
17
+ fn = BLEND_MODES.get(mode, BLEND_MODES["normal"])
18
+ result = fn(af, bf)
19
+ if opacity < 1.0:
20
+ result = af * (1 - opacity) + result * opacity
21
+ return np.clip(result * 255, 0, 255).astype(np.uint8)
22
+ ```
23
+
24
+ ### 20 Blend Modes
25
+
26
+ ```python
27
+ BLEND_MODES = {
28
+ # Basic arithmetic
29
+ "normal": lambda a, b: b,
30
+ "add": lambda a, b: np.clip(a + b, 0, 1),
31
+ "subtract": lambda a, b: np.clip(a - b, 0, 1),
32
+ "multiply": lambda a, b: a * b,
33
+ "screen": lambda a, b: 1 - (1 - a) * (1 - b),
34
+
35
+ # Contrast
36
+ "overlay": lambda a, b: np.where(a < 0.5, 2*a*b, 1 - 2*(1-a)*(1-b)),
37
+ "softlight": lambda a, b: (1 - 2*b)*a*a + 2*b*a,
38
+ "hardlight": lambda a, b: np.where(b < 0.5, 2*a*b, 1 - 2*(1-a)*(1-b)),
39
+
40
+ # Difference
41
+ "difference": lambda a, b: np.abs(a - b),
42
+ "exclusion": lambda a, b: a + b - 2*a*b,
43
+
44
+ # Dodge / burn
45
+ "colordodge": lambda a, b: np.clip(a / (1 - b + 1e-6), 0, 1),
46
+ "colorburn": lambda a, b: np.clip(1 - (1 - a) / (b + 1e-6), 0, 1),
47
+
48
+ # Light
49
+ "linearlight": lambda a, b: np.clip(a + 2*b - 1, 0, 1),
50
+ "vividlight": lambda a, b: np.where(b < 0.5,
51
+ np.clip(1 - (1-a)/(2*b + 1e-6), 0, 1),
52
+ np.clip(a / (2*(1-b) + 1e-6), 0, 1)),
53
+ "pin_light": lambda a, b: np.where(b < 0.5,
54
+ np.minimum(a, 2*b), np.maximum(a, 2*b - 1)),
55
+ "hard_mix": lambda a, b: np.where(a + b >= 1.0, 1.0, 0.0),
56
+
57
+ # Compare
58
+ "lighten": lambda a, b: np.maximum(a, b),
59
+ "darken": lambda a, b: np.minimum(a, b),
60
+
61
+ # Grain
62
+ "grain_extract": lambda a, b: np.clip(a - b + 0.5, 0, 1),
63
+ "grain_merge": lambda a, b: np.clip(a + b - 0.5, 0, 1),
64
+ }
65
+ ```
66
+
67
+ ### Blend Mode Selection Guide
68
+
69
+ **Modes that brighten** (safe for dark inputs):
70
+ - `screen` — always brightens. Two 50% gray layers screen to 75%. The go-to safe blend.
71
+ - `add` — simple addition, clips at white. Good for sparkles, glows, particle overlays.
72
+ - `colordodge` — extreme brightening at overlap zones. Can blow out. Use low opacity (0.3-0.5).
73
+ - `linearlight` — aggressive brightening. Similar to add but with offset.
74
+
75
+ **Modes that darken** (avoid with dark inputs):
76
+ - `multiply` — darkens everything. Only use when both layers are already bright.
77
+ - `overlay` — darkens when base < 0.5, brightens when base > 0.5. Crushes dark inputs: `2 * 0.12 * 0.12 = 0.03`. Use `screen` instead for dark material.
78
+ - `colorburn` — extreme darkening at overlap zones.
79
+
80
+ **Modes that create contrast**:
81
+ - `softlight` — gentle contrast. Good for subtle texture overlay.
82
+ - `hardlight` — strong contrast. Like overlay but keyed on the top layer.
83
+ - `vividlight` — very aggressive contrast. Use sparingly.
84
+
85
+ **Modes that create color effects**:
86
+ - `difference` — XOR-like patterns. Two identical layers difference to black; offset layers create wild colors. Great for psychedelic looks.
87
+ - `exclusion` — softer version of difference. Creates complementary color patterns.
88
+ - `hard_mix` — posterizes to pure black/white/saturated color at intersections.
89
+
90
+ **Modes for texture blending**:
91
+ - `grain_extract` / `grain_merge` — extract a texture from one layer, apply it to another.
92
+
93
+ ### Multi-Layer Chaining
94
+
95
+ ```python
96
+ # Pattern: render layers -> blend sequentially
97
+ canvas_a = _render_vf(r, "md", vf_plasma, hf_angle(0.0), PAL_DENSE, f, t, S)
98
+ canvas_b = _render_vf(r, "sm", vf_vortex, hf_time_cycle(0.1), PAL_RUNE, f, t, S)
99
+ canvas_c = _render_vf(r, "lg", vf_rings, hf_distance(), PAL_BLOCKS, f, t, S)
100
+
101
+ result = blend_canvas(canvas_a, canvas_b, "screen", 0.8)
102
+ result = blend_canvas(result, canvas_c, "difference", 0.6)
103
+ ```
104
+
105
+ Order matters: `screen(A, B)` is commutative, but `difference(screen(A,B), C)` differs from `difference(A, screen(B,C))`.
106
+
107
+ ### Linear-Light Blend Modes
108
+
109
+ Standard `blend_canvas()` operates in sRGB space — the raw byte values. This is fine for most uses, but sRGB is perceptually non-linear: blending in sRGB darkens midtones and shifts hues slightly. For physically accurate blending (matching how light actually combines), convert to linear light first.
110
+
111
+ Uses `srgb_to_linear()` / `linear_to_srgb()` from `architecture.md` § OKLAB Color System.
112
+
113
+ ```python
114
+ def blend_canvas_linear(base, top, mode="normal", opacity=1.0):
115
+ """Blend in linear light space for physically accurate results.
116
+
117
+ Identical API to blend_canvas(), but converts sRGB → linear before
118
+ blending and linear → sRGB after. More expensive (~2x) due to the
119
+ gamma conversions, but produces correct results for additive blending,
120
+ screen, and any mode where brightness matters.
121
+ """
122
+ af = srgb_to_linear(base.astype(np.float32) / 255.0)
123
+ bf = srgb_to_linear(top.astype(np.float32) / 255.0)
124
+ fn = BLEND_MODES.get(mode, BLEND_MODES["normal"])
125
+ result = fn(af, bf)
126
+ if opacity < 1.0:
127
+ result = af * (1 - opacity) + result * opacity
128
+ result = linear_to_srgb(np.clip(result, 0, 1))
129
+ return np.clip(result * 255, 0, 255).astype(np.uint8)
130
+ ```
131
+
132
+ **When to use `blend_canvas_linear()` vs `blend_canvas()`:**
133
+
134
+ | Scenario | Use | Why |
135
+ |----------|-----|-----|
136
+ | Screen-blending two bright layers | `linear` | sRGB screen over-brightens highlights |
137
+ | Add mode for glow/bloom effects | `linear` | Additive light follows linear physics |
138
+ | Blending text overlay at low opacity | `srgb` | Perceptual blending looks more natural for text |
139
+ | Multiply for shadow/darkening | `srgb` | Differences are minimal for darken ops |
140
+ | Color-critical work (matching reference) | `linear` | Avoids sRGB hue shifts in midtones |
141
+ | Performance-critical inner loop | `srgb` | ~2x faster, good enough for most ASCII art |
142
+
143
+ **Batch version** for compositing many layers (converts once, blends multiple, converts back):
144
+
145
+ ```python
146
+ def blend_many_linear(layers, modes, opacities):
147
+ """Blend a stack of layers in linear light space.
148
+
149
+ Args:
150
+ layers: list of uint8 (H,W,3) canvases
151
+ modes: list of blend mode strings (len = len(layers) - 1)
152
+ opacities: list of floats (len = len(layers) - 1)
153
+ Returns:
154
+ uint8 (H,W,3) canvas
155
+ """
156
+ # Convert all to linear at once
157
+ linear = [srgb_to_linear(l.astype(np.float32) / 255.0) for l in layers]
158
+ result = linear[0]
159
+ for i in range(1, len(linear)):
160
+ fn = BLEND_MODES.get(modes[i-1], BLEND_MODES["normal"])
161
+ blended = fn(result, linear[i])
162
+ op = opacities[i-1]
163
+ if op < 1.0:
164
+ blended = result * (1 - op) + blended * op
165
+ result = np.clip(blended, 0, 1)
166
+ result = linear_to_srgb(result)
167
+ return np.clip(result * 255, 0, 255).astype(np.uint8)
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Multi-Grid Composition
173
+
174
+ This is the core visual technique. Rendering the same conceptual scene at different grid densities (character sizes) creates natural texture interference, because characters at different scales overlap at different spatial frequencies.
175
+
176
+ ### Why It Works
177
+
178
+ - `sm` grid (10pt font): 320x83 characters. Fine detail, dense texture.
179
+ - `md` grid (16pt): 192x56 characters. Medium density.
180
+ - `lg` grid (20pt): 160x45 characters. Coarse, chunky characters.
181
+
182
+ When you render a plasma field on `sm` and a vortex on `lg`, then screen-blend them, the fine plasma texture shows through the gaps in the coarse vortex characters. The result has more visual complexity than either layer alone.
183
+
184
+ ### The `_render_vf()` Helper
185
+
186
+ This is the workhorse function. It takes a value field + hue field + palette + grid, renders to a complete pixel canvas:
187
+
188
+ ```python
189
+ def _render_vf(r, grid_key, val_fn, hue_fn, pal, f, t, S, sat=0.8, threshold=0.03):
190
+ """Render a value field + hue field to a pixel canvas via a named grid.
191
+
192
+ Args:
193
+ r: Renderer instance (has .get_grid())
194
+ grid_key: "xs", "sm", "md", "lg", "xl", "xxl"
195
+ val_fn: (g, f, t, S) -> float32 [0,1] array (rows, cols)
196
+ hue_fn: callable (g, f, t, S) -> float32 hue array, OR float scalar
197
+ pal: character palette string
198
+ f: feature dict
199
+ t: time in seconds
200
+ S: persistent state dict
201
+ sat: HSV saturation (0-1)
202
+ threshold: minimum value to render (below = space)
203
+
204
+ Returns:
205
+ uint8 array (VH, VW, 3) — full pixel canvas
206
+ """
207
+ g = r.get_grid(grid_key)
208
+ val = np.clip(val_fn(g, f, t, S), 0, 1)
209
+ mask = val > threshold
210
+ ch = val2char(val, mask, pal)
211
+
212
+ # Hue: either a callable or a fixed float
213
+ if callable(hue_fn):
214
+ h = hue_fn(g, f, t, S) % 1.0
215
+ else:
216
+ h = np.full((g.rows, g.cols), float(hue_fn), dtype=np.float32)
217
+
218
+ # CRITICAL: broadcast to full shape and copy (see Troubleshooting)
219
+ h = np.broadcast_to(h, (g.rows, g.cols)).copy()
220
+
221
+ R, G, B = hsv2rgb(h, np.full_like(val, sat), val)
222
+ co = mkc(R, G, B, g.rows, g.cols)
223
+ return g.render(ch, co)
224
+ ```
225
+
226
+ ### Grid Combination Strategies
227
+
228
+ | Combination | Effect | Good For |
229
+ |-------------|--------|----------|
230
+ | `sm` + `lg` | Maximum contrast between fine detail and chunky blocks | Bold, graphic looks |
231
+ | `sm` + `md` | Subtle texture layering, similar scales | Organic, flowing looks |
232
+ | `md` + `lg` + `xs` | Three-scale interference, maximum complexity | Psychedelic, dense |
233
+ | `sm` + `sm` (different effects) | Same scale, pattern interference only | Moire, interference |
234
+
235
+ ### Complete Multi-Grid Scene Example
236
+
237
+ ```python
238
+ def fx_psychedelic(r, f, t, S):
239
+ """Three-layer multi-grid scene with beat-reactive kaleidoscope."""
240
+ # Layer A: plasma on medium grid with rainbow hue
241
+ canvas_a = _render_vf(r, "md",
242
+ lambda g, f, t, S: vf_plasma(g, f, t, S) * 1.3,
243
+ hf_angle(0.0), PAL_DENSE, f, t, S, sat=0.8)
244
+
245
+ # Layer B: vortex on small grid with cycling hue
246
+ canvas_b = _render_vf(r, "sm",
247
+ lambda g, f, t, S: vf_vortex(g, f, t, S, twist=5.0) * 1.2,
248
+ hf_time_cycle(0.1), PAL_RUNE, f, t, S, sat=0.7)
249
+
250
+ # Layer C: rings on large grid with distance hue
251
+ canvas_c = _render_vf(r, "lg",
252
+ lambda g, f, t, S: vf_rings(g, f, t, S, n_base=8, spacing_base=3) * 1.4,
253
+ hf_distance(0.3, 0.02), PAL_BLOCKS, f, t, S, sat=0.9)
254
+
255
+ # Blend: A screened with B, then difference with C
256
+ result = blend_canvas(canvas_a, canvas_b, "screen", 0.8)
257
+ result = blend_canvas(result, canvas_c, "difference", 0.6)
258
+
259
+ # Beat-triggered kaleidoscope
260
+ if f.get("bdecay", 0) > 0.3:
261
+ result = sh_kaleidoscope(result.copy(), folds=6)
262
+
263
+ return result
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Adaptive Tone Mapping
269
+
270
+ ### The Brightness Problem
271
+
272
+ ASCII characters are small bright dots on a black background. Most pixels in any frame are background (black). This means:
273
+ - Mean frame brightness is inherently low (often 5-30 out of 255)
274
+ - Different effect combinations produce wildly different brightness levels
275
+ - A spiral scene might be 50 mean, while a fire scene is 9 mean
276
+ - Linear multipliers (e.g., `canvas * 2.0`) either leave dark scenes dark or blow out bright scenes
277
+
278
+ ### The `tonemap()` Function
279
+
280
+ Replaces linear brightness multipliers with adaptive per-frame normalization + gamma correction:
281
+
282
+ ```python
283
+ def tonemap(canvas, target_mean=90, gamma=0.75, black_point=2, white_point=253):
284
+ """Adaptive tone-mapping: normalizes + gamma-corrects so no frame is
285
+ fully dark or washed out.
286
+
287
+ 1. Compute 1st and 99.5th percentile on 4x subsample (16x fewer values,
288
+ negligible accuracy loss, major speedup at 1080p+)
289
+ 2. Stretch that range to [0, 1]
290
+ 3. Apply gamma curve (< 1 lifts shadows, > 1 darkens)
291
+ 4. Rescale to [black_point, white_point]
292
+ """
293
+ f = canvas.astype(np.float32)
294
+ sub = f[::4, ::4] # 4x subsample: ~390K values vs ~6.2M at 1080p
295
+ lo = np.percentile(sub, 1)
296
+ hi = np.percentile(sub, 99.5)
297
+ if hi - lo < 10:
298
+ hi = max(hi, lo + 10) # near-uniform frame fallback
299
+ f = np.clip((f - lo) / (hi - lo), 0.0, 1.0)
300
+ np.power(f, gamma, out=f) # in-place: avoids allocation
301
+ np.multiply(f, (white_point - black_point), out=f)
302
+ np.add(f, black_point, out=f)
303
+ if target_mean is not None:
304
+ f *= float(np.clip(target_mean, black_point, white_point)) / max(float(f.mean()), 1e-6)
305
+ return np.clip(f, 0, 255).astype(np.uint8)
306
+ ```
307
+
308
+ ### Why Gamma, Not Linear
309
+
310
+ Linear multiplier `* 2.0`:
311
+ ```
312
+ input 10 -> output 20 (still dark)
313
+ input 100 -> output 200 (ok)
314
+ input 200 -> output 255 (clipped, lost detail)
315
+ ```
316
+
317
+ Gamma 0.75 after normalization:
318
+ ```
319
+ input 0.04 -> output 0.08 (lifted from invisible to visible)
320
+ input 0.39 -> output 0.50 (moderate lift)
321
+ input 0.78 -> output 0.84 (gentle lift, no clipping)
322
+ ```
323
+
324
+ Gamma < 1 compresses the highlights and expands the shadows. This is exactly what we need: lift dark ASCII content into visibility without blowing out the bright parts.
325
+
326
+ ### Pipeline Ordering
327
+
328
+ The pipeline in `render_clip()` is:
329
+
330
+ ```
331
+ scene_fn(r, f, t, S) -> canvas
332
+ |
333
+ tonemap(canvas, gamma=scene_gamma)
334
+ |
335
+ FeedbackBuffer.apply(canvas, ...)
336
+ |
337
+ ShaderChain.apply(canvas, f=f, t=t)
338
+ |
339
+ ffmpeg pipe
340
+ ```
341
+
342
+ Tonemap runs BEFORE feedback and shaders. This means:
343
+ - Feedback operates on normalized data (consistent behavior regardless of scene brightness)
344
+ - Shaders like solarize, posterize, contrast operate on properly-ranged data
345
+ - The brightness shader in the chain is no longer needed (tonemap handles it)
346
+
347
+ ### Per-Scene Gamma Tuning
348
+
349
+ Default gamma is 0.75. Scenes that apply destructive post-processing need more aggressive lift because the destruction happens after tonemap:
350
+
351
+ | Scene Type | Recommended Gamma | Why |
352
+ |------------|-------------------|-----|
353
+ | Standard effects | 0.75 | Default, works for most scenes |
354
+ | Solarize post-process | 0.50-0.60 | Solarize inverts bright pixels, reducing overall brightness |
355
+ | Posterize post-process | 0.50-0.55 | Posterize quantizes, often crushing mid-values to black |
356
+ | Heavy difference blending | 0.60-0.70 | Difference mode creates many near-zero pixels |
357
+ | Already bright scenes | 0.85-1.0 | Don't over-boost scenes that are naturally bright |
358
+
359
+ Configure via the scene table:
360
+
361
+ ```python
362
+ SCENES = [
363
+ {"start": 9.17, "end": 11.25, "name": "fire", "gamma": 0.55,
364
+ "fx": fx_fire, "shaders": [("solarize", {"threshold": 200}), ...]},
365
+ {"start": 25.96, "end": 27.29, "name": "diamond", "gamma": 0.5,
366
+ "fx": fx_diamond, "shaders": [("bloom", {"thr": 90}), ...]},
367
+ ]
368
+ ```
369
+
370
+ ### Brightness Verification
371
+
372
+ After rendering, spot-check frame brightness:
373
+
374
+ ```python
375
+ # In test-frame mode
376
+ canvas = scene["fx"](r, feat, t, r.S)
377
+ canvas = tonemap(canvas, gamma=scene.get("gamma", 0.75))
378
+ chain = ShaderChain()
379
+ for sn, kw in scene.get("shaders", []):
380
+ chain.add(sn, **kw)
381
+ canvas = chain.apply(canvas, f=feat, t=t)
382
+ print(f"Mean brightness: {canvas.astype(float).mean():.1f}, max: {canvas.max()}")
383
+ ```
384
+
385
+ Target ranges after tonemap + shaders:
386
+ - Quiet/ambient scenes: mean 30-60
387
+ - Active scenes: mean 40-100
388
+ - Climax/peak scenes: mean 60-150
389
+ - If mean < 20: gamma is too high or a shader is destroying brightness
390
+ - If mean > 180: gamma is too low or add is stacking too much
391
+
392
+ ---
393
+
394
+ ## FeedbackBuffer Spatial Transforms
395
+
396
+ The feedback buffer stores the previous frame and blends it into the current frame with decay. Spatial transforms applied to the buffer before blending create the illusion of motion in the feedback trail.
397
+
398
+ ### Implementation
399
+
400
+ ```python
401
+ class FeedbackBuffer:
402
+ def __init__(self):
403
+ self.buf = None
404
+
405
+ def apply(self, canvas, decay=0.85, blend="screen", opacity=0.5,
406
+ transform=None, transform_amt=0.02, hue_shift=0.0):
407
+ if self.buf is None:
408
+ self.buf = canvas.astype(np.float32) / 255.0
409
+ return canvas
410
+
411
+ # Decay old buffer
412
+ self.buf *= decay
413
+
414
+ # Spatial transform
415
+ if transform:
416
+ self.buf = self._transform(self.buf, transform, transform_amt)
417
+
418
+ # Hue shift the feedback for rainbow trails
419
+ if hue_shift > 0:
420
+ self.buf = self._hue_shift(self.buf, hue_shift)
421
+
422
+ # Blend feedback into current frame
423
+ result = blend_canvas(canvas,
424
+ np.clip(self.buf * 255, 0, 255).astype(np.uint8),
425
+ blend, opacity)
426
+
427
+ # Update buffer with current frame
428
+ self.buf = result.astype(np.float32) / 255.0
429
+ return result
430
+
431
+ def _transform(self, buf, transform, amt):
432
+ h, w = buf.shape[:2]
433
+ if transform == "zoom":
434
+ # Zoom in: sample from slightly inside (creates expanding tunnel)
435
+ m = int(h * amt); n = int(w * amt)
436
+ if m > 0 and n > 0:
437
+ cropped = buf[m:-m or None, n:-n or None]
438
+ # Resize back to full (nearest-neighbor for speed)
439
+ buf = np.array(Image.fromarray(
440
+ np.clip(cropped * 255, 0, 255).astype(np.uint8)
441
+ ).resize((w, h), Image.NEAREST)).astype(np.float32) / 255.0
442
+ elif transform == "shrink":
443
+ # Zoom out: pad edges, shrink center
444
+ m = int(h * amt); n = int(w * amt)
445
+ small = np.array(Image.fromarray(
446
+ np.clip(buf * 255, 0, 255).astype(np.uint8)
447
+ ).resize((w - 2*n, h - 2*m), Image.NEAREST))
448
+ new = np.zeros((h, w, 3), dtype=np.uint8)
449
+ new[m:m+small.shape[0], n:n+small.shape[1]] = small
450
+ buf = new.astype(np.float32) / 255.0
451
+ elif transform == "rotate_cw":
452
+ # Small clockwise rotation via affine
453
+ angle = np.deg2rad(amt * 10) # convert degrees to radians
454
+ cy, cx = h / 2, w / 2
455
+ Y = np.arange(h, dtype=np.float32)[:, None]
456
+ X = np.arange(w, dtype=np.float32)[None, :]
457
+ cos_a, sin_a = np.cos(angle), np.sin(angle)
458
+ sx = (X - cx) * cos_a + (Y - cy) * sin_a + cx
459
+ sy = -(X - cx) * sin_a + (Y - cy) * cos_a + cy
460
+ sx = np.clip(sx.astype(int), 0, w - 1)
461
+ sy = np.clip(sy.astype(int), 0, h - 1)
462
+ buf = buf[sy, sx]
463
+ elif transform == "rotate_ccw":
464
+ angle = -amt * 10
465
+ cy, cx = h / 2, w / 2
466
+ Y = np.arange(h, dtype=np.float32)[:, None]
467
+ X = np.arange(w, dtype=np.float32)[None, :]
468
+ cos_a, sin_a = np.cos(angle), np.sin(angle)
469
+ sx = (X - cx) * cos_a + (Y - cy) * sin_a + cx
470
+ sy = -(X - cx) * sin_a + (Y - cy) * cos_a + cy
471
+ sx = np.clip(sx.astype(int), 0, w - 1)
472
+ sy = np.clip(sy.astype(int), 0, h - 1)
473
+ buf = buf[sy, sx]
474
+ elif transform == "shift_up":
475
+ pixels = max(1, int(h * amt))
476
+ buf = np.roll(buf, -pixels, axis=0)
477
+ buf[-pixels:] = 0 # black fill at bottom
478
+ elif transform == "shift_down":
479
+ pixels = max(1, int(h * amt))
480
+ buf = np.roll(buf, pixels, axis=0)
481
+ buf[:pixels] = 0
482
+ elif transform == "mirror_h":
483
+ buf = buf[:, ::-1]
484
+ return buf
485
+
486
+ def _hue_shift(self, buf, amount):
487
+ """Rotate hues of the feedback buffer. Operates on float32 [0,1]."""
488
+ rgb = np.clip(buf * 255, 0, 255).astype(np.uint8)
489
+ hsv = np.zeros_like(buf)
490
+ # Simple approximate RGB->HSV->shift->RGB
491
+ r, g, b = buf[:,:,0], buf[:,:,1], buf[:,:,2]
492
+ mx = np.maximum(np.maximum(r, g), b)
493
+ mn = np.minimum(np.minimum(r, g), b)
494
+ delta = mx - mn + 1e-10
495
+ # Hue
496
+ h = np.where(mx == r, ((g - b) / delta) % 6,
497
+ np.where(mx == g, (b - r) / delta + 2, (r - g) / delta + 4))
498
+ h = (h / 6 + amount) % 1.0
499
+ # Reconstruct with shifted hue (simplified)
500
+ s = delta / (mx + 1e-10)
501
+ v = mx
502
+ c = v * s; x = c * (1 - np.abs((h * 6) % 2 - 1)); m = v - c
503
+ ro = np.zeros_like(h); go = np.zeros_like(h); bo = np.zeros_like(h)
504
+ for lo, hi, rv, gv, bv in [(0,1,c,x,0),(1,2,x,c,0),(2,3,0,c,x),
505
+ (3,4,0,x,c),(4,5,x,0,c),(5,6,c,0,x)]:
506
+ mask = ((h*6) >= lo) & ((h*6) < hi)
507
+ ro[mask] = rv[mask] if not isinstance(rv, (int,float)) else rv
508
+ go[mask] = gv[mask] if not isinstance(gv, (int,float)) else gv
509
+ bo[mask] = bv[mask] if not isinstance(bv, (int,float)) else bv
510
+ return np.stack([ro+m, go+m, bo+m], axis=2)
511
+ ```
512
+
513
+ ### Feedback Presets
514
+
515
+ | Preset | Config | Visual Effect |
516
+ |--------|--------|---------------|
517
+ | Infinite zoom tunnel | `decay=0.8, blend="screen", transform="zoom", transform_amt=0.015` | Expanding ring patterns |
518
+ | Rainbow trails | `decay=0.7, blend="screen", transform="zoom", transform_amt=0.01, hue_shift=0.02` | Psychedelic color trails |
519
+ | Ghostly echo | `decay=0.9, blend="add", opacity=0.15, transform="shift_up", transform_amt=0.01` | Faint upward smearing |
520
+ | Kaleidoscopic recursion | `decay=0.75, blend="screen", transform="rotate_cw", transform_amt=0.005, hue_shift=0.01` | Rotating mandala feedback |
521
+ | Color evolution | `decay=0.8, blend="difference", opacity=0.4, hue_shift=0.03` | Frame-to-frame color XOR |
522
+ | Rising heat haze | `decay=0.5, blend="add", opacity=0.2, transform="shift_up", transform_amt=0.02` | Hot air shimmer |
523
+
524
+ ---
525
+
526
+ ## Masking / Stencil System
527
+
528
+ Masks are float32 arrays `(rows, cols)` or `(VH, VW)` in range [0, 1]. They control where effects are visible: 1.0 = fully visible, 0.0 = fully hidden. Use masks to create figure/ground relationships, focal points, and shaped reveals.
529
+
530
+ ### Shape Masks
531
+
532
+ ```python
533
+ def mask_circle(g, cx_frac=0.5, cy_frac=0.5, radius=0.3, feather=0.05):
534
+ """Circular mask centered at (cx_frac, cy_frac) in normalized coords.
535
+ feather: width of soft edge (0 = hard cutoff)."""
536
+ asp = g.cw / g.ch if hasattr(g, 'cw') else 1.0
537
+ dx = (g.cc / g.cols - cx_frac)
538
+ dy = (g.rr / g.rows - cy_frac) * asp
539
+ d = np.sqrt(dx**2 + dy**2)
540
+ if feather > 0:
541
+ return np.clip(1.0 - (d - radius) / feather, 0, 1)
542
+ return (d <= radius).astype(np.float32)
543
+
544
+ def mask_rect(g, x0=0.2, y0=0.2, x1=0.8, y1=0.8, feather=0.03):
545
+ """Rectangular mask. Coordinates in [0,1] normalized."""
546
+ dx = np.maximum(x0 - g.cc / g.cols, g.cc / g.cols - x1)
547
+ dy = np.maximum(y0 - g.rr / g.rows, g.rr / g.rows - y1)
548
+ d = np.maximum(dx, dy)
549
+ if feather > 0:
550
+ return np.clip(1.0 - d / feather, 0, 1)
551
+ return (d <= 0).astype(np.float32)
552
+
553
+ def mask_ring(g, cx_frac=0.5, cy_frac=0.5, inner_r=0.15, outer_r=0.35,
554
+ feather=0.03):
555
+ """Ring / annulus mask."""
556
+ inner = mask_circle(g, cx_frac, cy_frac, inner_r, feather)
557
+ outer = mask_circle(g, cx_frac, cy_frac, outer_r, feather)
558
+ return outer - inner
559
+
560
+ def mask_gradient_h(g, start=0.0, end=1.0):
561
+ """Left-to-right gradient mask."""
562
+ return np.clip((g.cc / g.cols - start) / (end - start + 1e-10), 0, 1).astype(np.float32)
563
+
564
+ def mask_gradient_v(g, start=0.0, end=1.0):
565
+ """Top-to-bottom gradient mask."""
566
+ return np.clip((g.rr / g.rows - start) / (end - start + 1e-10), 0, 1).astype(np.float32)
567
+
568
+ def mask_gradient_radial(g, cx_frac=0.5, cy_frac=0.5, inner=0.0, outer=0.5):
569
+ """Radial gradient mask — bright at center, dark at edges."""
570
+ d = np.sqrt((g.cc / g.cols - cx_frac)**2 + (g.rr / g.rows - cy_frac)**2)
571
+ return np.clip(1.0 - (d - inner) / (outer - inner + 1e-10), 0, 1)
572
+ ```
573
+
574
+ ### Value Field as Mask
575
+
576
+ Use any `vf_*` function's output as a spatial mask:
577
+
578
+ ```python
579
+ def mask_from_vf(vf_result, threshold=0.5, feather=0.1):
580
+ """Convert a value field to a mask by thresholding.
581
+ feather: smooth edge width around threshold."""
582
+ if feather > 0:
583
+ return np.clip((vf_result - threshold + feather) / (2 * feather), 0, 1)
584
+ return (vf_result > threshold).astype(np.float32)
585
+
586
+ def mask_select(mask, vf_a, vf_b):
587
+ """Spatial conditional: show vf_a where mask is 1, vf_b where mask is 0.
588
+ mask: float32 [0,1] array. Intermediate values blend."""
589
+ return vf_a * mask + vf_b * (1 - mask)
590
+ ```
591
+
592
+ ### Text Stencil
593
+
594
+ Render text to a mask. Effects are visible only through the letterforms:
595
+
596
+ ```python
597
+ def mask_text(grid, text, row_frac=0.5, font=None, font_size=None):
598
+ """Render text string as a float32 mask [0,1] at grid resolution.
599
+ Characters = 1.0, background = 0.0.
600
+
601
+ row_frac: vertical position as fraction of grid height.
602
+ font: PIL ImageFont (defaults to grid's font if None).
603
+ font_size: override font size for the mask text (for larger stencil text).
604
+ """
605
+ from PIL import Image, ImageDraw, ImageFont
606
+
607
+ f = font or grid.font
608
+ if font_size:
609
+ if not hasattr(f, 'path'):
610
+ raise ValueError('Font size override requires a file-backed font')
611
+ f = ImageFont.truetype(f.path, font_size)
612
+
613
+ # Render text to image at pixel resolution, then downsample to grid
614
+ img = Image.new("L", (grid.cols * grid.cw, grid.ch), 0)
615
+ draw = ImageDraw.Draw(img)
616
+ bbox = draw.textbbox((0, 0), text, font=f)
617
+ tw = bbox[2] - bbox[0]
618
+ x = (grid.cols * grid.cw - tw) // 2
619
+ draw.text((x, 0), text, fill=255, font=f)
620
+ row_mask = np.array(img, dtype=np.float32) / 255.0
621
+
622
+ # Place in full grid mask
623
+ mask = np.zeros((grid.rows, grid.cols), dtype=np.float32)
624
+ target_row = int(np.clip(grid.rows * row_frac, 0, grid.rows - 1))
625
+ # Downsample rendered text to grid cells
626
+ for c in range(grid.cols):
627
+ px = c * grid.cw
628
+ if px + grid.cw <= row_mask.shape[1]:
629
+ cell = row_mask[:, px:px + grid.cw]
630
+ if cell.mean() > 0.1:
631
+ mask[target_row, c] = cell.mean()
632
+ return mask
633
+
634
+ def mask_text_block(grid, lines, start_row_frac=0.3, font=None):
635
+ """Multi-line text stencil. Returns full grid mask."""
636
+ mask = np.zeros((grid.rows, grid.cols), dtype=np.float32)
637
+ for i, line in enumerate(lines):
638
+ row_frac = start_row_frac + i / grid.rows
639
+ line_mask = mask_text(grid, line, row_frac, font)
640
+ mask = np.maximum(mask, line_mask)
641
+ return mask
642
+ ```
643
+
644
+ ### Animated Masks
645
+
646
+ Masks that change over time for reveals, wipes, and morphing:
647
+
648
+ ```python
649
+ def mask_iris(g, t, t_start, t_end, cx_frac=0.5, cy_frac=0.5,
650
+ max_radius=0.7, ease_fn=None):
651
+ """Iris open/close: circle that grows from 0 to max_radius.
652
+ ease_fn: easing function (default: ease_in_out_cubic from effects.md)."""
653
+ if ease_fn is None:
654
+ ease_fn = lambda x: x * x * (3 - 2 * x) # smoothstep fallback
655
+ progress = np.clip((t - t_start) / (t_end - t_start), 0, 1)
656
+ radius = ease_fn(progress) * max_radius
657
+ return mask_circle(g, cx_frac, cy_frac, radius, feather=0.03)
658
+
659
+ def mask_wipe_h(g, t, t_start, t_end, direction="right"):
660
+ """Horizontal wipe reveal."""
661
+ progress = np.clip((t - t_start) / (t_end - t_start), 0, 1)
662
+ if direction == "left":
663
+ progress = 1 - progress
664
+ return mask_gradient_h(g, start=progress - 0.05, end=progress + 0.05)
665
+
666
+ def mask_wipe_v(g, t, t_start, t_end, direction="down"):
667
+ """Vertical wipe reveal."""
668
+ progress = np.clip((t - t_start) / (t_end - t_start), 0, 1)
669
+ if direction == "up":
670
+ progress = 1 - progress
671
+ return mask_gradient_v(g, start=progress - 0.05, end=progress + 0.05)
672
+
673
+ def mask_dissolve(g, t, t_start, t_end, seed=42):
674
+ """Random pixel dissolve — noise threshold sweeps from 0 to 1."""
675
+ progress = np.clip((t - t_start) / (t_end - t_start), 0, 1)
676
+ rng = np.random.RandomState(seed)
677
+ noise = rng.random((g.rows, g.cols)).astype(np.float32)
678
+ return (noise < progress).astype(np.float32)
679
+ ```
680
+
681
+ ### Mask Boolean Operations
682
+
683
+ ```python
684
+ def mask_union(a, b):
685
+ """OR — visible where either mask is active."""
686
+ return np.maximum(a, b)
687
+
688
+ def mask_intersect(a, b):
689
+ """AND — visible only where both masks are active."""
690
+ return np.minimum(a, b)
691
+
692
+ def mask_subtract(a, b):
693
+ """A minus B — visible where A is active but B is not."""
694
+ return np.clip(a - b, 0, 1)
695
+
696
+ def mask_invert(m):
697
+ """NOT — flip mask."""
698
+ return 1.0 - m
699
+ ```
700
+
701
+ ### Applying Masks to Canvases
702
+
703
+ ```python
704
+ def apply_mask_canvas(canvas, mask, bg_canvas=None):
705
+ """Apply a grid-resolution mask to a pixel canvas.
706
+ Expands mask from (rows, cols) to (VH, VW) via nearest-neighbor.
707
+
708
+ canvas: uint8 (VH, VW, 3)
709
+ mask: float32 (rows, cols) [0,1]
710
+ bg_canvas: what shows through where mask=0. None = black.
711
+ """
712
+ # Expand mask to pixel resolution
713
+ y_idx = np.minimum(np.arange(canvas.shape[0]) * mask.shape[0] // canvas.shape[0], mask.shape[0] - 1)
714
+ x_idx = np.minimum(np.arange(canvas.shape[1]) * mask.shape[1] // canvas.shape[1], mask.shape[1] - 1)
715
+ mask_px = mask[y_idx[:, None], x_idx[None, :]]
716
+
717
+ if bg_canvas is not None:
718
+ return np.clip(canvas * mask_px[:, :, None] +
719
+ bg_canvas * (1 - mask_px[:, :, None]), 0, 255).astype(np.uint8)
720
+ return np.clip(canvas * mask_px[:, :, None], 0, 255).astype(np.uint8)
721
+
722
+ def apply_mask_vf(vf_a, vf_b, mask):
723
+ """Apply mask at value-field level — blend two value fields spatially.
724
+ All arrays are (rows, cols) float32."""
725
+ return vf_a * mask + vf_b * (1 - mask)
726
+ ```
727
+
728
+ ---
729
+
730
+ ## PixelBlendStack
731
+
732
+ Higher-level wrapper for multi-layer compositing:
733
+
734
+ ```python
735
+ class PixelBlendStack:
736
+ def __init__(self):
737
+ self.layers = []
738
+
739
+ def add(self, canvas, mode="normal", opacity=1.0):
740
+ self.layers.append((canvas, mode, opacity))
741
+ return self
742
+
743
+ def composite(self):
744
+ if not self.layers:
745
+ return np.zeros((VH, VW, 3), dtype=np.uint8)
746
+ result = self.layers[0][0]
747
+ for canvas, mode, opacity in self.layers[1:]:
748
+ result = blend_canvas(result, canvas, mode, opacity)
749
+ return result
750
+ ```
751
+
752
+ ## Text Backdrop (Readability Mask)
753
+
754
+ When placing readable text over busy multi-grid ASCII backgrounds, the text will blend into the background and become illegible. **Always apply a dark backdrop behind text regions.**
755
+
756
+ The technique: compute the bounding box of all text glyphs, create a gaussian-blurred dark mask covering that area with padding, and multiply the background by `(1 - mask * darkness)` before rendering text on top.
757
+
758
+ ```python
759
+ from scipy.ndimage import gaussian_filter
760
+
761
+ def apply_text_backdrop(canvas, glyphs, padding=80, darkness=0.75):
762
+ """Darken the background behind text for readability.
763
+
764
+ Call AFTER rendering background, BEFORE rendering text.
765
+
766
+ Args:
767
+ canvas: (VH, VW, 3) uint8 background
768
+ glyphs: list of {"x": float, "y": float, ...} glyph positions
769
+ padding: pixel padding around text bounding box
770
+ darkness: 0.0 = no darkening, 1.0 = fully black
771
+ Returns:
772
+ darkened canvas (uint8)
773
+ """
774
+ if not glyphs:
775
+ return canvas
776
+ xs = [g['x'] for g in glyphs]
777
+ ys = [g['y'] for g in glyphs]
778
+ x0 = max(0, int(min(xs)) - padding)
779
+ y0 = max(0, int(min(ys)) - padding)
780
+ x1 = min(VW, int(max(xs)) + padding + 50) # extra for char width
781
+ y1 = min(VH, int(max(ys)) + padding + 60) # extra for char height
782
+
783
+ # Soft dark mask with gaussian blur for feathered edges
784
+ mask = np.zeros((VH, VW), dtype=np.float32)
785
+ mask[y0:y1, x0:x1] = 1.0
786
+ mask = gaussian_filter(mask, sigma=padding * 0.6)
787
+
788
+ factor = 1.0 - mask * darkness
789
+ return (canvas.astype(np.float32) * factor[:, :, np.newaxis]).astype(np.uint8)
790
+ ```
791
+
792
+ ### Usage in render pipeline
793
+
794
+ Insert between background rendering and text rendering:
795
+
796
+ ```python
797
+ # 1. Render background (multi-grid ASCII effects)
798
+ bg = render_background(cfg, t)
799
+
800
+ # 2. Darken behind text region
801
+ bg = apply_text_backdrop(bg, frame_glyphs, padding=80, darkness=0.75)
802
+
803
+ # 3. Render text on top (now readable against dark backdrop)
804
+ bg = text_renderer.render(bg, frame_glyphs, color=(255, 255, 255))
805
+ ```
806
+
807
+ Combine with **reverse vignette** (see shaders.md) for scenes where text is always centered — the reverse vignette provides a persistent center-dark zone, while the backdrop handles per-frame glyph positions.
808
+
809
+ ## External Layout Oracle Pattern
810
+
811
+ For text-heavy videos where text needs to dynamically reflow around obstacles (shapes, icons, other text), use an external layout engine to pre-compute glyph positions and feed them into the Python renderer via JSON.
812
+
813
+ ### Architecture
814
+
815
+ ```
816
+ Layout Engine (browser/Node.js) → layouts.json → Python ASCII Renderer
817
+ ↑ ↑
818
+ Computes per-frame Reads glyph positions,
819
+ glyph (x,y) positions renders as ASCII chars
820
+ with obstacle-aware reflow with full effect pipeline
821
+ ```
822
+
823
+ ### JSON interchange format
824
+
825
+ ```json
826
+ {
827
+ "meta": {
828
+ "canvas_width": 1080, "canvas_height": 1080,
829
+ "fps": 24, "total_frames": 1248,
830
+ "fonts": {
831
+ "body": {"charW": 12.04, "charH": 24, "fontSize": 20},
832
+ "hero": {"charW": 24.08, "charH": 48, "fontSize": 40}
833
+ }
834
+ },
835
+ "scenes": [
836
+ {
837
+ "id": "scene_name",
838
+ "start_frame": 0, "end_frame": 96,
839
+ "frames": {
840
+ "0": {
841
+ "glyphs": [
842
+ {"char": "H", "x": 287.1, "y": 400.0, "alpha": 1.0},
843
+ {"char": "e", "x": 311.2, "y": 400.0, "alpha": 1.0}
844
+ ],
845
+ "obstacles": [
846
+ {"type": "circle", "cx": 540, "cy": 540, "r": 80},
847
+ {"type": "rect", "x": 300, "y": 500, "w": 120, "h": 80}
848
+ ]
849
+ }
850
+ }
851
+ }
852
+ ]
853
+ }
854
+ ```
855
+
856
+ ### When to use
857
+
858
+ - Text that dynamically reflows around moving objects
859
+ - Per-glyph animation (reveal, scatter, physics)
860
+ - Variable typography that needs precise measurement
861
+ - Any case where Python's Pillow text layout is insufficient
862
+
863
+ ### When NOT to use
864
+
865
+ - Static centered text (just use PIL `draw.text()` directly)
866
+ - Text that only fades in/out without spatial animation
867
+ - Simple typewriter effects (handle in Python with a character counter)
868
+
869
+ ### Running the oracle
870
+
871
+ Use Playwright to run the layout engine in a headless browser:
872
+
873
+ ```javascript
874
+ // extract.mjs
875
+ import { chromium } from 'playwright';
876
+ import { writeFileSync } from 'node:fs';
877
+ import { resolve } from 'node:path';
878
+ import { pathToFileURL } from 'node:url';
879
+ const oraclePath = resolve(process.argv[2] || 'oracle.html');
880
+ const browser = await chromium.launch({ headless: true });
881
+ const page = await browser.newPage();
882
+ await page.goto(pathToFileURL(oraclePath).href);
883
+ await page.waitForFunction(() => window.__ORACLE_DONE__ === true, null, { timeout: 60000 });
884
+ const result = await page.evaluate(() => window.__ORACLE_RESULT__);
885
+ writeFileSync('layouts.json', JSON.stringify(result));
886
+ await browser.close();
887
+ ```
888
+
889
+ ### Consuming in Python
890
+
891
+ ```python
892
+ # In the renderer, map pixel positions to the canvas:
893
+ for glyph in frame_data['glyphs']:
894
+ char, px, py = glyph['char'], glyph['x'], glyph['y']
895
+ alpha = glyph.get('alpha', 1.0)
896
+ # Render using PIL draw.text() at exact pixel position
897
+ draw.text((px, py), char, fill=(int(255*alpha),)*3, font=font)
898
+ ```
899
+
900
+ Obstacles from the JSON can also be rendered as glowing ASCII shapes (circles, rectangles) to visualize the reflow zones.