stream_weaver 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. checksums.yaml +7 -0
  2. data/.beads/.gitignore +77 -0
  3. data/.beads/README.md +81 -0
  4. data/.beads/config.yaml +54 -0
  5. data/.beads/metadata.json +7 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +91 -0
  8. data/CHANGELOG.md +294 -0
  9. data/CLAUDE.md +67 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +565 -0
  12. data/Rakefile +13 -0
  13. data/assets/streamweaver-hero.jpg +0 -0
  14. data/bench/baselines/ledger.rb +58 -0
  15. data/bench/baselines/warroom.rb +51 -0
  16. data/bench/fixtures/ledger.rb +76 -0
  17. data/bench/fixtures/warroom.rb +60 -0
  18. data/bench/results/6daf8b8.md +102 -0
  19. data/bench/results/c8d749b.md +102 -0
  20. data/bench/results/eb06ab9.md +102 -0
  21. data/bench/run.rb +151 -0
  22. data/bench/support.rb +84 -0
  23. data/copy_this/dto_run_sample_run_7631.html +220 -0
  24. data/docs/SERVICE_MODE.md +302 -0
  25. data/docs/architecture/how_streamweaver_works.md +772 -0
  26. data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
  27. data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
  28. data/docs/canvas-ipc-session-summary.md +127 -0
  29. data/docs/canvas-panel-workflow.md +172 -0
  30. data/docs/canvas-read.md +147 -0
  31. data/docs/canvas-roadmap.md +275 -0
  32. data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
  33. data/docs/claude-code-companion-skill-spec.md +567 -0
  34. data/docs/components_reference.md +1223 -0
  35. data/docs/crud-patterns.md +265 -0
  36. data/docs/endpoints.md +122 -0
  37. data/docs/for_llms.md +1 -0
  38. data/docs/form-patterns.md +165 -0
  39. data/docs/frontend-only.md +169 -0
  40. data/docs/html-artifact-audit.md +238 -0
  41. data/docs/ideas/2025-01-01-as-a-service.md +203 -0
  42. data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
  43. data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
  44. data/docs/ideas/dhh-review-future-refactors.md +142 -0
  45. data/docs/opal-jamstack.md +47 -0
  46. data/docs/opal-npm-direction.md +184 -0
  47. data/docs/opal-spike-findings.md +194 -0
  48. data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
  49. data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
  50. data/docs/plans/2026-01-02-charts-design.md +288 -0
  51. data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
  52. data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
  53. data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
  54. data/docs/plans/canvas-claude-project-design.md +238 -0
  55. data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
  56. data/docs/plans/org-doc-preview-surfaces.md +293 -0
  57. data/docs/plans/shadcn-polish-plan.md +417 -0
  58. data/docs/porting-artifacts.md +252 -0
  59. data/docs/reference/agent-skills-comparison.md +369 -0
  60. data/docs/reference/travel-state-prd.artifact.html +780 -0
  61. data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
  62. data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
  63. data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
  64. data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
  65. data/docs/research/frontend-only-matrix.md +278 -0
  66. data/docs/research/streamweaver-way-spike-findings.md +529 -0
  67. data/docs/resource-dsl.md +342 -0
  68. data/docs/routing.md +302 -0
  69. data/docs/ruby-ui-comparison.md +319 -0
  70. data/docs/shared-dsl-fragments.md +356 -0
  71. data/docs/streamweaver-for-ai-agents.md +401 -0
  72. data/docs/streamweaver-frontend-vision.md +123 -0
  73. data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
  74. data/docs/streamweaver_canvas/save_example.rb +9 -0
  75. data/docs/templates.md +213 -0
  76. data/docs/testing.md +155 -0
  77. data/docs/theming-hooks.md +281 -0
  78. data/docs/tutorials/the-streamweaver-way.md +882 -0
  79. data/docs/university/capability-inventory.md +137 -0
  80. data/docs/university/dependency-survey.md +114 -0
  81. data/docs/university/design-spec.md +217 -0
  82. data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
  83. data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
  84. data/docs/university/mockups/course-list-dark.png +0 -0
  85. data/docs/university/mockups/course-list-light.png +0 -0
  86. data/docs/university/mockups/course_canvas_mockup.rb +741 -0
  87. data/docs/university/mockups/step-screen-dark.png +0 -0
  88. data/docs/university/mockups/step-screen-light.png +0 -0
  89. data/docs/university/roadmap.md +72 -0
  90. data/docs/university/send-to-coworker.md +77 -0
  91. data/docs/university/worker-session-mining.md +109 -0
  92. data/docs/visual/browser-rendering-spike.md +173 -0
  93. data/docs/visual/builder-visual-plan-analysis.md +378 -0
  94. data/docs/visual/sw-plan-format-exploration.md +511 -0
  95. data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
  96. data/docs/visual-skills/PROGRESS.md +60 -0
  97. data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
  98. data/docs/visual-skills/analysis/components.md +317 -0
  99. data/docs/visual-skills/analysis/overlap.md +360 -0
  100. data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
  101. data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
  102. data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
  103. data/docs/visual-skills/analysis/visual-explainer.md +964 -0
  104. data/docs/visual-skills/blog/blog-series-outline.md +39 -0
  105. data/docs/visual-skills/blog/token-efficiency.md +73 -0
  106. data/docs/visual-skills/design/architecture.md +1725 -0
  107. data/docs/visual-skills/design/codex-review.md +340 -0
  108. data/docs/visual-skills/design/dhh-review.md +209 -0
  109. data/docs/visual-skills/design/evolution.md +100 -0
  110. data/docs/visual-skills/design/gemini-review.md +55 -0
  111. data/docs/visual-skills/design/review-synthesis.md +149 -0
  112. data/docs/visual-skills/implementation/STATE.md +22 -0
  113. data/docs/visual-skills/implementation/plan.md +347 -0
  114. data/docs/visual-skills/implementation/spike-findings.md +237 -0
  115. data/docs/visual-skills/implementation/tasks.md +702 -0
  116. data/docs/visual-skills/lessons-learned/process.md +145 -0
  117. data/examples/README.md +96 -0
  118. data/examples/advanced/all_components.rb +177 -0
  119. data/examples/advanced/examples_browser.rb +394 -0
  120. data/examples/advanced/teachables_browser.rb +261 -0
  121. data/examples/advanced/theme_tweaker.rb +366 -0
  122. data/examples/advanced/tutorial.rb +1528 -0
  123. data/examples/agentic/agentic_form.rb +30 -0
  124. data/examples/agentic/agentic_form_autoclose.rb +30 -0
  125. data/examples/agentic/cultivation_tracker.rb +69 -0
  126. data/examples/basic/hello_world.rb +22 -0
  127. data/examples/basic/opal_tabs_table.rb +44 -0
  128. data/examples/basic/todo_list.rb +39 -0
  129. data/examples/button_loading_test.rb +36 -0
  130. data/examples/canvas/mermaid_canvas_demo.sh +151 -0
  131. data/examples/charts/bar_chart_demo.rb +57 -0
  132. data/examples/charts/line_chart_demo.rb +73 -0
  133. data/examples/charts/pie_area_demo.rb +87 -0
  134. data/examples/charts/stacked_bar_chart_demo.rb +69 -0
  135. data/examples/claude_code/README.md +85 -0
  136. data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
  137. data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
  138. data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
  139. data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
  140. data/examples/claude_code/codebreaker/README.md +213 -0
  141. data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
  142. data/examples/claude_code/tutorial/README.md +102 -0
  143. data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
  144. data/examples/claude_code/verification_flow/README.md +126 -0
  145. data/examples/components/annotated_code_demo.rb +54 -0
  146. data/examples/components/callout_demo.rb +33 -0
  147. data/examples/components/checkbox_group_demo.rb +46 -0
  148. data/examples/components/design_review.css +468 -0
  149. data/examples/components/design_review_demo.rb +44 -0
  150. data/examples/components/design_review_dsl.rb +300 -0
  151. data/examples/components/diff_block_demo.rb +112 -0
  152. data/examples/components/events_demo.rb +184 -0
  153. data/examples/components/form_demo.rb +62 -0
  154. data/examples/components/lesson_demo.rb +53 -0
  155. data/examples/components/markdown_demo.rb +110 -0
  156. data/examples/components/mermaid_demo.rb +166 -0
  157. data/examples/components/pareto_set.rb +64 -0
  158. data/examples/components/prd_demo.rb +21 -0
  159. data/examples/components/prd_dsl.rb +385 -0
  160. data/examples/components/quiz_demo.rb +47 -0
  161. data/examples/components/run_viewer_demo.rb +100 -0
  162. data/examples/components/score_and_collapsible_demo.rb +82 -0
  163. data/examples/components/select_stale_value_smoke_test.rb +39 -0
  164. data/examples/components/table_demo.rb +247 -0
  165. data/examples/components/timer_with_state_demo.rb +58 -0
  166. data/examples/components/todo_due_dates.rb +63 -0
  167. data/examples/components/uat_gaps_demo.rb +112 -0
  168. data/examples/dashboard/feed_simulator.rb +60 -0
  169. data/examples/dashboard/live_dashboard.rb +80 -0
  170. data/examples/dashboard_components.rb +108 -0
  171. data/examples/deferred_fragments_demo.rb +62 -0
  172. data/examples/generate_more_spike/README.md +63 -0
  173. data/examples/generate_more_spike/app.rb +441 -0
  174. data/examples/git_health.sh +401 -0
  175. data/examples/layout/layout_components_demo.rb +174 -0
  176. data/examples/layout/modal_demo.rb +215 -0
  177. data/examples/layout/navigation_demo.rb +227 -0
  178. data/examples/layout/route_tabs_demo.rb +52 -0
  179. data/examples/layout/routing_demo.rb +52 -0
  180. data/examples/layout/scroll_box_demo.rb +154 -0
  181. data/examples/lazy_fragments_demo.rb +151 -0
  182. data/examples/my_todos/README.md +29 -0
  183. data/examples/my_todos/my_todos.rb +312 -0
  184. data/examples/my_todos/store.rb +86 -0
  185. data/examples/opal/reactive_demo.rb +62 -0
  186. data/examples/opal/scenarios/s1_counter.rb +15 -0
  187. data/examples/opal/scenarios/s2_search_filter.rb +17 -0
  188. data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
  189. data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
  190. data/examples/opal/scenarios/s5_watch.rb +39 -0
  191. data/examples/opal/scenarios/s6_on_start.rb +20 -0
  192. data/examples/opal/scenarios/s7_wizard.rb +33 -0
  193. data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
  194. data/examples/opal/scenarios/s9_dashboard.rb +27 -0
  195. data/examples/operations_dashboard_demo.rb +167 -0
  196. data/examples/panel_demo.sh +447 -0
  197. data/examples/parity/assets/tyrion_slice.css +86 -0
  198. data/examples/parity/rivet_people_slice.rb +182 -0
  199. data/examples/parity/tyrion_components.css +777 -0
  200. data/examples/parity/tyrion_warroom_components.rb +414 -0
  201. data/examples/parity/tyrion_warroom_slice.rb +549 -0
  202. data/examples/puma_dev/README.md +124 -0
  203. data/examples/puma_dev/config.ru +34 -0
  204. data/examples/puma_dev/standalone_app.rb +34 -0
  205. data/examples/scaffolding/blog.rb +49 -0
  206. data/examples/scaffolding/utf_lite.rb +105 -0
  207. data/examples/styling/feedback_demo.rb +229 -0
  208. data/examples/styling/style_showcase.rb +172 -0
  209. data/examples/styling/theme_demo.rb +508 -0
  210. data/examples/timer_health_checker.rb +111 -0
  211. data/examples/timer_showcase.rb +131 -0
  212. data/examples/tutorials/render_markdown.rb +112 -0
  213. data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
  214. data/examples/tutorials/tutorial_content.rb +1082 -0
  215. data/examples/visual_skills/design_deck_demo.rb +314 -0
  216. data/examples/visual_skills/explainer_demo.rb +499 -0
  217. data/exe/streamweaver +9 -0
  218. data/gsd/ROADMAP-1.0.md +167 -0
  219. data/gsd/STATE.md +13 -0
  220. data/gsd/research/market-positioning-research.md +139 -0
  221. data/gsd/research/production-patterns-research.md +270 -0
  222. data/gsd/research/repo-audit-1.0.md +274 -0
  223. data/lib/stream_weaver/action_token.rb +58 -0
  224. data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
  225. data/lib/stream_weaver/adapter/base.rb +591 -0
  226. data/lib/stream_weaver/adapter/opal.rb +334 -0
  227. data/lib/stream_weaver/adapter/static.rb +1118 -0
  228. data/lib/stream_weaver/admin.rb +176 -0
  229. data/lib/stream_weaver/app.rb +1821 -0
  230. data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
  231. data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
  232. data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
  233. data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
  234. data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
  235. data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
  236. data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
  237. data/lib/stream_weaver/canvas/bridge.rb +239 -0
  238. data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
  239. data/lib/stream_weaver/canvas/client.rb +298 -0
  240. data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
  241. data/lib/stream_weaver/canvas/doc_store.rb +252 -0
  242. data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
  243. data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
  244. data/lib/stream_weaver/canvas/gist_store.rb +135 -0
  245. data/lib/stream_weaver/canvas/helpers.rb +109 -0
  246. data/lib/stream_weaver/canvas/history.rb +90 -0
  247. data/lib/stream_weaver/canvas/protocol.rb +86 -0
  248. data/lib/stream_weaver/canvas/reader.rb +853 -0
  249. data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
  250. data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
  251. data/lib/stream_weaver/canvas/session.rb +132 -0
  252. data/lib/stream_weaver/cli.rb +3235 -0
  253. data/lib/stream_weaver/component_assets.rb +70 -0
  254. data/lib/stream_weaver/component_registry.rb +67 -0
  255. data/lib/stream_weaver/component_renderer.rb +48 -0
  256. data/lib/stream_weaver/components/annotated_code.rb +53 -0
  257. data/lib/stream_weaver/components/api_endpoint.rb +42 -0
  258. data/lib/stream_weaver/components/callout.rb +59 -0
  259. data/lib/stream_weaver/components/chart.rb +84 -0
  260. data/lib/stream_weaver/components/code_block.rb +77 -0
  261. data/lib/stream_weaver/components/comparison.rb +39 -0
  262. data/lib/stream_weaver/components/decision.rb +38 -0
  263. data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
  264. data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
  265. data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
  266. data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
  267. data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
  268. data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
  269. data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
  270. data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
  271. data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
  272. data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
  273. data/lib/stream_weaver/components/diff_block.rb +125 -0
  274. data/lib/stream_weaver/components/doc_header.rb +57 -0
  275. data/lib/stream_weaver/components/image_block.rb +70 -0
  276. data/lib/stream_weaver/components/implementation_map.rb +33 -0
  277. data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
  278. data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
  279. data/lib/stream_weaver/components/mermaid.rb +79 -0
  280. data/lib/stream_weaver/components/pipeline.rb +63 -0
  281. data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
  282. data/lib/stream_weaver/components/slide_container.rb +141 -0
  283. data/lib/stream_weaver/components/timeline_event.rb +58 -0
  284. data/lib/stream_weaver/components/wireframe.rb +29 -0
  285. data/lib/stream_weaver/components/wireframe_block.rb +34 -0
  286. data/lib/stream_weaver/components.rb +2711 -0
  287. data/lib/stream_weaver/css.rb +230 -0
  288. data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
  289. data/lib/stream_weaver/display_dsl.rb +1004 -0
  290. data/lib/stream_weaver/export/html_exporter.rb +478 -0
  291. data/lib/stream_weaver/feed.rb +34 -0
  292. data/lib/stream_weaver/feed_builder.rb +31 -0
  293. data/lib/stream_weaver/fonts.rb +33 -0
  294. data/lib/stream_weaver/interaction_runner.rb +487 -0
  295. data/lib/stream_weaver/iterm.rb +460 -0
  296. data/lib/stream_weaver/layout_registry.rb +92 -0
  297. data/lib/stream_weaver/opal/bridge.rb +52 -0
  298. data/lib/stream_weaver/opal/builder.rb +193 -0
  299. data/lib/stream_weaver/opal/env.rb +29 -0
  300. data/lib/stream_weaver/opal/reactive_state.rb +75 -0
  301. data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
  302. data/lib/stream_weaver/opal/renderer.rb +77 -0
  303. data/lib/stream_weaver/opal/runtime.rb +250 -0
  304. data/lib/stream_weaver/opal/shell.rb +145 -0
  305. data/lib/stream_weaver/opal/string_bridge.rb +50 -0
  306. data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
  307. data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
  308. data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
  309. data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
  310. data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
  311. data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
  312. data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
  313. data/lib/stream_weaver/opal_entry.rb +136 -0
  314. data/lib/stream_weaver/org/inline.rb +95 -0
  315. data/lib/stream_weaver/org/reader.rb +561 -0
  316. data/lib/stream_weaver/org/recording_context.rb +86 -0
  317. data/lib/stream_weaver/org/source_splitter.rb +63 -0
  318. data/lib/stream_weaver/org/writer.rb +300 -0
  319. data/lib/stream_weaver/page_shell.rb +530 -0
  320. data/lib/stream_weaver/portfile.rb +79 -0
  321. data/lib/stream_weaver/pushable.rb +42 -0
  322. data/lib/stream_weaver/resource/default_views.rb +111 -0
  323. data/lib/stream_weaver/resource/field_input.rb +25 -0
  324. data/lib/stream_weaver/resource/state_keys.rb +16 -0
  325. data/lib/stream_weaver/resource/store.rb +18 -0
  326. data/lib/stream_weaver/resource.rb +104 -0
  327. data/lib/stream_weaver/server.rb +1471 -0
  328. data/lib/stream_weaver/service.rb +1240 -0
  329. data/lib/stream_weaver/service_client.rb +104 -0
  330. data/lib/stream_weaver/session_store.rb +186 -0
  331. data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
  332. data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
  333. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
  334. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
  335. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
  336. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
  337. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
  338. data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
  339. data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
  340. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
  341. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
  342. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
  343. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
  344. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
  345. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
  346. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
  347. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
  348. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
  349. data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
  350. data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
  351. data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
  352. data/lib/stream_weaver/streamer.rb +63 -0
  353. data/lib/stream_weaver/templates/choices.rb +149 -0
  354. data/lib/stream_weaver/templates/code.rb +157 -0
  355. data/lib/stream_weaver/templates/confirm.rb +110 -0
  356. data/lib/stream_weaver/templates/diff.rb +195 -0
  357. data/lib/stream_weaver/templates/info.rb +142 -0
  358. data/lib/stream_weaver/templates/table.rb +169 -0
  359. data/lib/stream_weaver/templates/wizard.rb +271 -0
  360. data/lib/stream_weaver/theme/auto_mode.rb +116 -0
  361. data/lib/stream_weaver/theme/presets.rb +555 -0
  362. data/lib/stream_weaver/theme.rb +639 -0
  363. data/lib/stream_weaver/university/canvas.rb +924 -0
  364. data/lib/stream_weaver/university/course.rb +699 -0
  365. data/lib/stream_weaver/university/demos/counter.rb +33 -0
  366. data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
  367. data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
  368. data/lib/stream_weaver/university/demos.rb +50 -0
  369. data/lib/stream_weaver/university/listener.rb +467 -0
  370. data/lib/stream_weaver/university/progress.rb +201 -0
  371. data/lib/stream_weaver/university/runner.rb +138 -0
  372. data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
  373. data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
  374. data/lib/stream_weaver/utils.rb +31 -0
  375. data/lib/stream_weaver/version.rb +5 -0
  376. data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
  377. data/lib/stream_weaver/views.rb +3940 -0
  378. data/lib/stream_weaver.rb +122 -0
  379. data/llms.txt +1269 -0
  380. data/sig/stream_weaver.rbs +4 -0
  381. metadata +598 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,294 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.0] - 2026-09-04
11
+
12
+ First published RubyGems release.
13
+
14
+ ### Added
15
+ - **`streamweaver get-started` — three steps to a guided course** - `gem install stream_weaver; streamweaver install; streamweaver get-started` opens StreamWeaver University: an interactive 5-step course driven from a live canvas controller that launches and directs an agent (Claude Code or Codex) in a worker session. Premier experience on macOS + iTerm2 (auto-arranged windows, one-click step runs, automatic hand-back); degraded browser-tab path everywhere else. Course demos ship in the gem (`streamweaver university-demo <name>`); progress persists across reboots; `streamweaver university-reset` starts over.
16
+ - **`streamweaver install`** - friendlier alias of `setup`: installs the bundled agent skills to both the Claude Code and cross-tool (`.agents/`) skill roots.
17
+ - **`streamweaver canvas-snapshot` / `canvas-restore` / `canvas-restart`** - snapshot every live canvas session (theme + layout + content), restore them, or restart the bridge onto newly installed code without losing sessions.
18
+ - **`streamweaver canvas-raise` / `focus-me`** - bring a canvas pane or the calling iTerm session to the front from the CLI; used by the course choreography, handy anywhere.
19
+ - **Charts render on the live canvas bridge** - `chart type:` initialization now survives poll-driven content swaps (previously charts only rendered on a first page load).
20
+ - **Canvas auto-scroll** - a growing doc follows the bottom when the viewer is already there (and never yanks a reader who scrolled up); full-page reshapes land at the top.
21
+ - **Platform support matrix + canvas-read guide** - README states what is tested (macOS + iTerm2), what degrades, and what is future; `docs/canvas-read.md` documents the document-shelf workflow, with a real-world case study.
22
+
23
+
24
+ - **Tutorial: "Build My Todos: the StreamWeaver Way"** - The narrated twin of the `streamweaver-way` skill. Where the skill *prescribes* — here are the six laws, here are the four recipes, copy them — the tutorial *narrates*: it rebuilds inline editing, scoped live search, lazy hover cards, and Russian-doll infinite scroll in the order you'd hit them building the app yourself, and every step names three things: the law it obeys, the gotcha it avoids, and the measured number where one exists (deferring a 1.5s region: 0.018s shell vs a 1.508s fragment fetch; hover cards: a 0.085s shell vs 9.151s eager; search: an 800-byte scoped morph vs a 2,123-byte whole-body swap; inline edit: an 806-byte save vs a 108,792-byte page; infinite scroll: O(1) per page vs O(n) per click). The infinite-scroll section teaches Turbo's real shape — current page inline, only the *next* page lazy — and uses the empty-shell bug a clean-room agent hit as its teaching moment: wrapping page N itself in the lazy fragment serves HTML with zero rows in it and only *looks* correct, because page 1's placeholder starts in the viewport, so the reader must `curl` the page rather than trust the screen. Ships in two forms from one outline (`examples/tutorials/tutorial_content.rb`): a `:doc`-theme StreamWeaver app at `examples/tutorials/streamweaver_way_tutorial.rb` and a markdown twin at `docs/tutorials/the-streamweaver-way.md`, regenerated by `ruby examples/tutorials/render_markdown.rb`. `spec/tutorial_twins_spec.rb` fails if the checked-in markdown drifts from the outline, so the twins can't quietly disagree. Every code sample is an extract from `examples/my_todos/`, verified by assembling them into a running four-feature app. Linked from `llms.txt`.
25
+ - **`streamweaver-way` skill — the conventions layer, taught** - A third bundled agent skill (alongside `streamweaver-visual-companion` and `streamweaver-doc-builder`), installed by the same `streamweaver setup` / `streamweaver install-skill` command to both Claude Code's path and the `.agents/skills/` cross-tool alias. It codifies "The StreamWeaver Way" with progressive disclosure: one screen of laws up front — key by what is unique per *position* not by what the content is *about*; deferred over hand-rolled timers; lazy means visible; dev loud, prod self-heals; state your intent in DSL verbs (zero custom JavaScript is the default, not an aspiration); trilaws as design filters — then the four benchmark Turbo Frames recipes (inline editing via `form_for` + `fragment`, scoped live search, lazy hover cards, Russian-doll infinite scroll), each complete and runnable with the two or three gotchas that actually bite, then pointers to the long tail. The gotchas are the ones the parity spike paid for: `text_field ..., updates:` is silently accepted and silently ignored (an input can target only its own enclosing fragment or `#app-container`, so a field placed outside the results fragment swaps the whole container), a lazy fragment inside a tab goes *inside* a `tab` block and never beside one, a named-action button inside a deferred fragment doesn't fire, and hiding a hover card with `visibility: hidden` instead of `display: none` makes every card fetch eagerly. The infinite-scroll recipe teaches Turbo's real shape — **the page the reader is on renders inline, and only the *next* page is lazy** — and names the wrong-but-tempting alternative: wrapping page N itself in the lazy fragment serves HTML with no rows in it at all, and only looks correct because page 1's placeholder starts in the viewport, so the IntersectionObserver fires immediately. `llms.txt` gains a matching "The StreamWeaver Way" section near the top linking each law to its detailed section. Worked example set: `examples/my_todos/`.
26
+ - **Deferred fragments — `fragment :name, defer: true`** - The Turbo `turbo_frame_tag ..., src:` equivalent: the deferred block does not run during the page render at all, so a slow region can't hold the shell hostage. The page ships immediately with a placeholder, and the fragment fetches its own content right after — with no custom JavaScript in app code, just the one option on the verb you already use. Measured on a page with one `sleep 1.5` fragment: initial GET 0.018s (was 1.5s), fragment endpoint 1.508s. `placeholder:` takes a String (rendered as text), a Proc (evaluated as DSL), or nothing (a small spinner). The auto-fetch is an `hx-trigger="load"` POST to the same signed `_sw_fragment` endpoint every in-fragment interaction already uses, so there's no new route and no new authorization path. Nesting works and chains — a deferred fragment inside another loads after its parent — though each fetch re-runs its ancestors' blocks, so nest for staging, not to split one expensive block. Two things to know: the block is skipped on the shell render, so keep action/timer registration outside it; and a *named*-action button (`action: :foo`) inside a deferred fragment does not fire (its token is minted outside the session's action manifest) — use a block button (`button('X') { |s| ... }`), which is unaffected. Static exports run every deferred block inline, since nothing will fetch them later. See `llms.txt` and `examples/deferred_fragments_demo.rb`.
27
+ - **Lazy fragments — `fragment :name, lazy: true`** - Turbo's `loading="lazy"` for deferred fragments: the fetch waits until the fragment is *visible*, whatever made it visible — scrolled into the viewport, or a CSS rule flipping an ancestor out of `display: none`. Two guarantees follow. A CSS-hidden fragment **never** fetches while it stays hidden, which is what makes a zero-JavaScript hover card cost nothing on page load: `display: none` until `:hover`, and revealing it is what fires the fetch. And it fetches **exactly once** — the content lands in the DOM and stays, so hiding and re-showing the region doesn't refetch. Nesting gives you infinite scroll for free, in Turbo's own shape: the page the reader is on renders inline and its block ends by declaring only the *next* page as a lazy fragment, so page N+1 doesn't exist until page N has been scrolled past — no scroll handler, no page counter in state, and the served HTML still carries real rows. (Wrapping page N itself in the lazy fragment is the tempting shape and it's wrong: the block is skipped on the shell render, so nothing but a placeholder goes out.) `lazy: true` implies `defer: true`, so you only write one option. Under the hood the placeholder wrapper carries `hx-trigger="intersect once"` (htmx's `IntersectionObserver`-backed trigger, the same browser API Turbo uses) rather than `hx-trigger="load"`; htmx's other visibility trigger, `revealed`, reads `getBoundingClientRect()` and so counts a hidden element as revealed, which would fetch every hidden fragment on load. A full-container swap re-arms a lazy fragment the way it re-arms a deferred one, but lazily — off-screen when the re-render lands means it waits. See `llms.txt`, `examples/lazy_fragments_demo.rb`, and `docs/research/2026-08-22-lazy-fragments-trigger-decision.md` (which also sketches how route tabs adopt this as the deprecated `tabs ..., lazy: true` mode's successor).
28
+ - **Route tabs — `tabs :view, url: true`** - A tab group's active index lives in a query param (`?view=2`), so a tab is bookmarkable, shareable, and back/forward-aware. Switching is pure client-side History API (`pushState`/`popstate`) — zero requests — and multiple groups compose, one param per key (`/?view=2&panel=1`). On a full GET the URL is authoritative: the param wins when present and valid, and an absent or invalid param means tab 0, *never* the session's value, so the same URL always renders the same tabs. Invalid values degrade rather than error (`?view=999`, `?view=abc`, `?view[]=` all return 200 on tab 0). Two consequences worth knowing: read `state[:view]` *below* the declaration (above it the key still holds the raw pre-authority value), and a `url: true` group can't be server-side preset — `state[:view] ||= 1` above the declaration is overwritten by URL authority. Raises at build time on reserved keys (`app_id`, `splat`, `captures`, `button_id`), duplicate `url:` keys, or `url: true` with `lazy: true`. Ignored on canvas pages (plain client tabs plus one logged warning per render pass), which have no app URL to carry the tab. See `docs/components_reference.md#route-tabs-url-true` and `examples/layout/route_tabs_demo.rb`.
29
+ - **`canvas-read`: live file browser** - A "📁 Browse files…" link in the reader's nav rail opens a live filesystem browser inside the already-running reader — breadcrumbs, folder navigation, and shortcuts to "This repo" and "StreamWeaver (global)" — so you can open a doc saved somewhere else without restarting `canvas-read` with a new path. No index or registered locations: browsing computes fresh on every request, so it finds anything, including docs saved before this shipped. Deliberately read-only (no move/copy/delete — a plain `mv` still does that). Guarded by a new cross-origin request check (Host + `Sec-Fetch-Site`) on every `canvas-read` route, since the file browser's `/open` route evaluates the `.rb` file it's handed and that's now reachable from any tab in your browser, not just a CLI argument you typed yourself.
30
+ - **Mermaid diagrams: expand to full screen** - Every mermaid diagram, regardless of `zoom:`, now gets an expand button that opens it in a full-viewport modal with no width constraint. The in-place `zoom: true` pan/zoom controls help but don't fix the actual problem — a wide diagram's fixed-px labels shrink proportionally to fit whatever doc column width it renders in, no matter how the layout is tuned. Expand re-hosts the already-rendered SVG at natural size with real scroll (scroll/drag to pan, Ctrl+scroll to zoom further, Escape/backdrop-click/close-button to exit). Pure client-side, no new dependency — works identically in the live canvas, `canvas-read`, and any exported doc (including `--offline`).
31
+ - **`--offline` HTML export: mermaid diagrams render in viewers that block every external script** - `streamweaver export <file.rb> --offline` (also `?offline=1` on the reader's `GET /export`, and a second "⬇ Export HTML (offline)" nav link shown only on docs with a mermaid diagram) fetches mermaid's own library at export time and inlines it as a plain `<script>`, instead of referencing its CDN. A plain inline script is covered by a CSP's `unsafe-inline`, so the diagram renders even in a viewer (SharePoint's built-in HTML preview is the motivating case) whose CSP doesn't allowlist any external host at all — the gap the Alpine/htmx fix below couldn't close on its own, since it still referenced mermaid's CDN URL. Doesn't cover `elk: true` diagrams (ELK has no equivalent inlinable build) or any other CDN asset (Chart.js, Prism); needs network access once, at export time, not at view time.
32
+ - **Export a canvas doc to standalone HTML** - "⬇ Export HTML" in `canvas-read`'s nav rail (every file, not just history snapshots) downloads the doc you're viewing as a single self-contained HTML file — same theme/layout, same look, no server required to view it. Same capability from the CLI: `streamweaver export <file.rb> [-o out.html] [--inline-images] [--offline]`. `--inline-images` embeds local images as base64 data URIs so the file is fully portable.
33
+ - **`use_theme`/`use_layout` DSL directives, plus `canvas-read --theme=/--layout=`** - A canvas doc's theme now travels with it: `use_theme :doc` / `use_layout :wide` declared in the DSL (or automatically prepended when you use the "Save as doc" button on a themed canvas session) makes the doc render with that theme wherever it's later opened — `canvas-read` no longer has no way to know a saved file was meant to be `:doc` themed. `canvas-read --theme=doc --layout=wide` sets a fallback for files that declare neither. Precedence: DSL directive > CLI flag > `:default`/`:fluid`.
34
+ - **Human-readable service-mode URLs** - `streamweaver run <file.rb>` now serves apps at slug URLs like `/apps/sales-dashboard` (derived from the app's declared name, falling back to the filename) instead of opaque hashes. The hex `/apps/:app_id` URL still resolves as a canonical fallback. Slugs that collide across different files get a numeric suffix (`-2`, `-3`, ...); re-loading the same file reuses its existing slug.
35
+ - **`endpoint` DSL — the "never rewrite in Sinatra" escape hatch** - Register a real HTTP route (webhook receiver, JSON API, file download) directly from the app DSL: `endpoint(:get, "/api/status") { |req| { ok: true } }`. Supports `:get`/`:post`/`:put`/`:patch`/`:delete`; the block receives the raw `Rack::Request` and its return value maps to a response (`Hash` -> JSON 200, `String` -> HTML 200, `[status, headers, body]` -> passed through verbatim). Endpoints bypass StreamWeaver's state/session/CSRF machinery entirely and always lose to a colliding internal route (`/update`, `/action/*`, `/submit`, `/event/*`, `/form/*`, `/theme/*`, `/sw/*`), with a warning at registration time if that happens. Works in both standalone (`run!`) and multi-app service mode (scoped under `/apps/:app_id/...`). See `docs/endpoints.md`.
36
+ - **`canvas-read` renders `.org` docs, not just `.rb`** - StreamWeaver-flavored org files (marked with `#+STREAMWEAVER_DSL:`) now open in the reader everywhere `.rb` docs do — file lists, the browser, `GET /open`, and rendering — via a server-side detect-then-convert path (`Org::Reader.to_dsl`), the same conversion the browser extension's sandbox has done client-side since it shipped.
37
+ - **`canvas-read` multi-repo doc discovery with repo filter** - The reader now shows every docs root on the machine, grouped by repo and filterable, instead of only the repo it was launched in. Discovery unions two sources: a one-level scan of `$STREAMWEAVER_DOCS_SCAN_ROOTS` (default `~/work`) for `*/docs/streamweaver_canvas`, and an append-only registry at `~/.streamweaver/docs_roots.log` written automatically by "Save as doc" and by `canvas-read <explicit path>` — visiting a doc once backfills its repo, so there is no registration command. Deleted or moved repos drop off on read.
38
+ - **iTerm split-pane install hint** - When `streamweaver panel` falls back to the system browser because the optional `iterm2_ruby` gem isn't installed (but you ARE in iTerm2 on macOS), the CLI now prints a one-line `gem install iterm2_ruby` tip so the split-pane perk is discoverable.
39
+ - **`bin/smoke` — executable UAT smoke test** - Freezes a manual UAT battery into a repeatable script: boots a fixture app in both standalone (`run!`) and multi-app service (`streamweaver serve`) mode on ephemeral ports and drives it over real HTTP, checking the `endpoint` DSL, the reserved-path boot warning, slug/hex `/apps/:id` resolution, endpoint dispatch scoping, and slug collision/reuse. Prints one check/x check line per assertion plus a final summary; exits nonzero on any failure. Wired into CI as the `smoke` job. See `docs/testing.md`.
40
+
41
+ - **`strict_ids` as a global setting, and it degrades in production** - `StreamWeaver.strict_ids = true` (or `SW_STRICT_IDS=1`) opts every app into strict interactive-id checking without threading `strict_ids:` through each `App.new`; an explicit `strict_ids:` still wins. A strict violation now raises in development and test but only warns when `RACK_ENV=production` — a live page shouldn't 500 over an id the framework already auto-disambiguated. Ids stay unique either way. Documented in `llms.txt` under "Interactive IDs and keying".
42
+ - **Feed/canvas-pushed buttons get the same id guarantees as app buttons** - `DisplayDSL#button` (the `FeedBuilder` path behind `feed`, `streamer`, and `canvas-push`) previously had no auto-disambiguation, no `key:`, and no scalar-key validation, so same-label buttons pushed in a loop shared one id. It now shares the App's identity helpers: `key:` for content-stable ids, verbatim `id:`, occurrence-based auto-disambiguation, and `ArgumentError` on a non-scalar key.
43
+
44
+ ### Changed
45
+ - **`button id:` is now a verbatim id override, and outranks `key:`** - Previously `id:` was an alias for `key:` (hashed into the derived id, and *lost* to `key:` when both were passed). It now sets the emitted id directly — `button "Delete", id: "row-7"` renders `id="btn_delete_row-7"` — which is what you want when SSE pushes, CSS, or tests target the element by selector. Precedence is `id:` > `key:` > auto-derivation from label + block source location. Existing `id:` callers keep working (dispatch has always used the emitted id); only the *shape* of that id changes, from an MD5 hash to your literal value.
46
+ - **`tabs lazy: true` is deprecated** - The POST-morph lazy mode now warns once per process, naming route tabs (`url: true`) as the direction and a future lazy route-tab mode as its replacement. Lazy tabs fetch the newly-active panel with an `hx-post` morph that canvas has no route for, so inactive lazy panels on a canvas page can never receive content (`stream_weaver-pkh`). The warning is the only change — the mode behaves exactly as before, and a compat spec locks that (active-only panel evaluation, the placeholder comment for inactive panels, and the full `hx-post`/`hx-include`/`hx-vals`/`hx-target`/`hx-swap` attribute set).
47
+ - **Built-in tutorial (`streamweaver tutorial`) revamped for the last ~6 months of features** - Refreshed the Themes and Layout/Cards lessons (`:doc` theme, `:sketch` preset, `theme_switcher`/`theme_toggle` auto dark mode, `card_header` `badge:`/`meta:`) and added six new lessons: The Four Modes (standalone/agentic/service/canvas-panel orientation), Navigation (`navbar`/`nav_item`/`link_to` + `route_by`/`route_with`), Resource DSL, Endpoints (the tutorial now registers and self-demonstrates a real `GET /tutorial/api/hello` endpoint), Service Mode, and Canvas/Panel. Also fixed an invalid-Ruby one-liner in the Resource DSL quick-start example (`docs/resource-dsl.md`).
48
+
49
+ ### Fixed
50
+ - **`route_with`: a `/now`-style dedicated view left open in one tab no longer poisons every sibling tab's clicks** - Session state is one hash per browser (one cookie), not per tab, but `route_with`'s special-view flags were only ever reset by `parser` on a real GET — every POST (`/update`, `/action/:button_id`, `/event/:key`, `/form/:form_name`, and any future POST route) only read them via `builder`. So a tab that navigated to a dedicated view (e.g. `/now`) kept that flag true in the shared session forever, and any OTHER tab's next click inherited it and got pushed there too — even a tab that never touched that view. Fixed by re-running `parser` against the requesting tab's own on-screen URL (htmx already sends this as `HX-Current-URL` on every request) before dispatching any POST, wired as a `before` filter (`server.rb`) / path-scoped `before '/apps/:app_id/*'` filter (`service.rb`, mount-prefix aware so a stale URL for a different mounted app is never misapplied) rather than a per-route call, so a future POST route can't be added without the fix. A raw non-htmx POST has no such header and is left exactly as before. Found live in a downstream app's dashboard from real multi-tab usage. See `docs/routing.md` "Pitfall 3 — one session, many tabs".
51
+ - **A design deck no longer confirms a choice the server never got** - Clicking a deck option checked the card — `.sw-deck-option--selected` plus `aria-checked="true"`, and the checkmark taken off whichever sibling really was selected — and *then* fired a `POST /deck/select` whose response it never looked at. Only `server.rb` mounts the `/deck/*` routes and the deck's JS posts to them by absolute path, so on the canvas bridge, in `canvas-read`, under `streamweaver serve`, and in an exported file that call 404s or never lands at all — and the deck sat there showing a decision nobody recorded. Selection, notes, final notes, Submit, generate-more, and the model selector's per-model click are now all gated on the response — the card is marked only once the server has the choice, with `aria-busy` covering the round trip so the click is still acknowledged — and any deck call that fails is reported to the console naming the route and the status instead of being swallowed. Where nothing serves those routes the deck now renders read-only rather than offering the dead call at all: cards carry `aria-disabled` and `title="Read-only: deck selections need the standalone deck server"`, drop out of the tab order, and the note/generate/submit controls are disabled. That covers the bridge and the reader (known from the render mode) plus exports, service-mode apps, and canvas live-push (each of which builds its own adapter and now declares `deck_server: false`, since an export and the standalone server are otherwise indistinguishable at render time). Standalone deck markup is unchanged, pinned by golden-string specs; the emitted JS is exercised for real under node.
52
+ - **`ComponentRenderer` can render fragments** - `Components::Fragment` asks the view it renders into to track fragment scope, which only the app views implemented — so any app using `fragment` raised `NoMethodError` when rendered through `ComponentRenderer` (static HTML export, `Streamer`, `Feed`). It now tracks scope the same way the app views do.
53
+ - **The bare `defer { ... }` verb raises instead of silently dropping its block** - It was never implemented (an empty method sitting alongside real verbs like `every` and `watch`), so content written inside it simply never appeared, with no error. It now raises and names the real verb, `fragment(:name, defer: true)`.
54
+ - **`canvas-read` no longer renders dead interactive controls** - Buttons and radio groups in a doc opened in the reader used to render as fully live canvas controls, but the reader deliberately omits the canvas bridge's scripts — so clicking a button greyed it out and then threw `ReferenceError: sendEvent is not defined`, and a radio moved its selection for nothing. The reader now renders those controls disabled with a `title="Interactive on live canvas only"` tooltip: honest about being a preview instead of pretending to work. Live canvas rendering (where the bridge *does* define `sendEvent`) is byte-for-byte unchanged.
55
+ - **Tab indices are clamped everywhere** - An out-of-range active tab index (a stale session value, or a group that shrank between renders) now renders tab 0 instead of matching no panel and blanking the group. Applies to plain tabs, not just route tabs.
56
+ - **Standalone param sync no longer leaks route-owned params into state** - Standalone mode strips `app_id`, `splat`, `captures`, and `button_id` from the request params it syncs into state, matching what service mode already did. Previously a route's own plumbing could land in a state key of the same name.
57
+ - **HTML export no longer needs htmx or an unconditional Alpine.js, and mermaid renders without either** - A static export never talks to a server, so `htmx`/`idiomorph` are dropped from every export entirely. Alpine.js now loads only when the rendered doc actually contains an `x-data` directive (checked against the rendered markup itself, not a hardcoded list of component classes, so it can't silently drift as new Alpine-using components are added). Mermaid no longer depends on Alpine at all: `sw-mermaid-zoom.js` self-initializes on `DOMContentLoaded`/`htmx:afterSwap` instead of relying on an `x-init` directive, so a mermaid-only export loads no Alpine and a mermaid diagram never again silently fails to render just because Alpine's external `<script>` didn't load in some CSP-locked-down viewer (e.g. SharePoint's built-in HTML preview). Note: mermaid's own CDN-hosted library is still an external load, so a CSP that blocks *all* external hosts (not just Alpine's) needs the `--offline` diagram-inlining flag above to actually render there.
58
+ - **HTML export no longer silently discards every component** - `HtmlExporter` unconditionally rebuilt the app before rendering, which re-runs the block a standalone `App.new { ... }` was built from — but a canvas doc/history snapshot is `instance_eval`'d from a DSL string into a bare `App` with no block, so the rebuild re-evaluated nothing and wiped every component, exporting an empty page with none of your content. Only rebuilds when there's a block to rebuild from.
59
+ - **`canvas-read` now renders docs the way canvas actually does** - The reader was missing the framework's theme CSS entirely (no `master_theme_css`, no cascade-layer pin) and nested its content wrapper inside an extra `<div>`, so every selector depending on `#app-container` being a direct `<body>` child was dead — `sidebar_toc` rendered as a full-width block instead of a sticky rail, and a doc's declared theme never applied. `canvas-read` now emits the same framework CSS as canvas, keeps `#app-container` a direct body child, and its own file-browser sidebar is a separate collapsible rail (collapses below ~1100px viewport width) that doesn't interfere with a doc's own `sidebar_toc`.
60
+ - **Canvas `sidebar_toc` nav stays sticky again** - Two cascade-layer/specificity bugs caused the TOC sidebar to scroll away instead of staying pinned (desktop) and bounce to the top on every scroll-spy update (mobile). The framework's base CSS is now layer-wrapped alongside `master_theme_css`/`visual_skills_css` (previously it was left unlayered and silently outranked them), and the sidebar's `overflow: visible` override now out-specifies the `#app-container` rule it needs to beat and applies below 1000px too, not just on desktop.
61
+ - **`date_field` now works inside `form`/`scope` blocks** - Previously `date_field` ignored `form_context`/`scope_name` and always emitted a flat top-level `name`/`x-model`, so a date picker inside a `form` or `scope` block silently landed its value outside the intended nested state instead of `state[scope][key]`. It now branches the same way `text_field` does, and also gained `on_change`/`on_blur`/`debounce` callback support and a `render_date_field` stub on `Adapter::Base`/the Opal adapter. `resource`/`form_for` `:date` fields now render a real date picker instead of a plain text field. See `docs/components_reference.md#dates`.
62
+ - **`streamweaver serve` auto-selects a free port** - When 4567 (or the default) is busy, `serve` now increments to the next free port like standalone mode instead of crashing with EADDRINUSE; an explicit `--port` is still honored strictly
63
+ - **`streamweaver panel` browser fallback works beyond macOS** - The fallback now uses the cross-platform `open_browser` helper (`open`/`xdg-open`/`start`) instead of macOS-only `open`
64
+ - **Canvas history ignores empty `STREAMWEAVER_HISTORY_ROOT`** - An empty-string env var no longer redirects history writes to filesystem-root-relative paths
65
+ - **Canvas history writes are atomic** - `History.record` claims snapshot paths with `File::EXCL`, so concurrent writers in the same second get distinct files instead of clobbering each other
66
+ - **Loading a `.run!` app file no longer kills service mode** - `Service.load_app` evaluates app files with a `service_loading` flag set, and `run!` is now a warn-and-no-op while it's active. Previously a file ending in `end.run!` (the documented standalone pattern) started a second server inside the service process and took the whole service down on exit. Standalone `ruby app.rb` behavior is unchanged.
67
+
68
+ ### Fixed
69
+ - Plain `gem install` no longer crashes on load: the Opal compiler is now an optional, lazily-required dependency (`gem install opal` only if you use `opal-build`).
70
+ - Org export tolerates interactive components (forms save cleanly as `.org`, controls omitted with a marker; the `.rb` sibling keeps full fidelity).
71
+
72
+ ## [0.2.0] - 2026-07-05
73
+
74
+ ### Changed
75
+ - **`iterm2_ruby` is now an optional enhancement, not a runtime dependency** - `gem install stream_weaver` no longer requires it; iTerm2 split-pane browser panes activate when the (now published) `iterm2_ruby` gem is installed (`gem install iterm2_ruby`), otherwise panel/canvas commands fall back to opening the system browser
76
+
77
+ ### Added
78
+ - **Canvas theme support** - Canvas/panel sessions can now use the `:doc` theme (and any registered theme): `streamweaver panel my-session --theme=doc`. Canvas sessions default to `:default` as before; the canvas page now reuses the same theme CSS (including dark-mode variants) as full-page rendering, so the mermaid dark-mode fix also applies in canvas mode.
79
+ - **`CardHeader` `badge:` / `meta:` options** - `card_header "Title", badge: "C1", meta: "right-aligned text"` renders a mono badge before the title and right-aligned meta text after it, for compact labeled card headers.
80
+ - **Puma-dev support** - Run StreamWeaver apps with memorable URLs like `http://myapp.test`:
81
+ - Detects `PORT` environment variable (set by Puma-dev and PaaS platforms)
82
+ - Skips auto-browser opening when `PORT` is set for on-demand access
83
+ - Provides `config.ru` example in `examples/puma_dev/`
84
+ - See [examples/puma_dev/README.md](examples/puma_dev/README.md) for setup guide
85
+
86
+ ### Fixed
87
+ - **Mermaid diagrams stayed light in dark mode** - Diagrams didn't re-render when the page switched to dark mode, and modern CSS color functions (`oklch()`, `color-mix()`) in theme tokens crashed Mermaid's color parser. Diagrams now re-render on theme change, and colors are resolved through a canvas probe so Mermaid always receives plain `rgb()`.
88
+ - **`theme_toggle` `mode:` was a dead parameter** - `theme_toggle mode: :light` (or `:dark`) had no effect on first-page-load behavior; the page always defaulted to following OS `prefers-color-scheme` regardless of `mode:`. `AutoMode.inline_script`/`.alpine_data` now accept the component's `mode:` as the localStorage-fallback default, so `mode: :light`/`:dark` actually forces that theme until the user toggles.
89
+ - **Puma thread pool exhaustion** - SSE streaming apps could hang when opening multiple browser tabs; increased default Puma thread pool from 5 to 16 to accommodate long-lived SSE connections
90
+ - **Canvas-push error feedback** - DSL errors now reported to stderr with exit code 1, enabling Claude to see and fix syntax errors
91
+ - **Tutorial checkbox syntax** - Fixed incorrect `checkbox :key, label: "text"` to correct `checkbox :key, "text"` in learn.md examples
92
+ - **iTerm panel stability** - AppleScript keystrokes could hang or type into wrong window; now opens URL in external browser instead
93
+
94
+ ### Added
95
+ - **`navbar` / `nav_item` / `link_to` components** - Cross-app navigation bar DSL. `navbar` renders a horizontal nav bar; `nav_item` renders as a bold non-clickable span when `active: true` or a link otherwise; `link_to` renders an inline anchor element.
96
+ ```ruby
97
+ navbar do
98
+ nav_item "Dashboard", active: true
99
+ nav_item "Settings", href: "/settings"
100
+ end
101
+ link_to "Docs", href: "https://example.com"
102
+ ```
103
+ - **`--reset` flag** - Clear corrupted session state on any app: `ruby my_app.rb --reset`. One-shot clear on first page load, then normal operation resumes
104
+ - **`SW_DEBUG` env var** - Request-level debug logging for troubleshooting: `SW_DEBUG=1 ruby my_app.rb`. Logs request method, path, cookie/state sizes, and SSE connection counts
105
+ - **StatusDot `label:` option** - Display text labels below status dots:
106
+ ```ruby
107
+ status_dot status: :green, pulse: true, label: "billing.rb"
108
+ ```
109
+ - **Canvas Mode CSS** - Full component styling in canvas/panel mode:
110
+ - Progress bars with animated stripes
111
+ - Spinners with rotation animation
112
+ - Status dots with pulse animation and labels
113
+ - Badges, alerts, activity items
114
+ - Tables with striped rows and sortable headers
115
+ - Collapsible sections, hstack/vstack spacing
116
+ - **Canvas Syntax Highlighting** - Code blocks in canvas mode now have syntax highlighting via highlight.js (github theme)
117
+ - **Canvas Charts** - Chart.js support in canvas mode for bar_chart, line_chart, etc.
118
+ - **`git_health.sh` example** - Dynamic git repository analyzer:
119
+ - Scans real git history, generates custom health dashboard
120
+ - Charts showing commit patterns by day of week
121
+ - Author contribution tables with sorting
122
+ - Contextual recommendations based on findings
123
+ - Different results each run based on actual repo state
124
+ - **Enhanced `panel_demo.sh`** - 4-step workflow showcasing canvas capabilities:
125
+ - Step 1: Issue selection (cards, badges, checkboxes, radio groups)
126
+ - Step 2: Diff preview (side-by-side columns, alerts)
127
+ - Step 3: Progress animation (spinner, progress bar, status dots, activity log)
128
+ - Step 4: Results summary (badges, alerts, sortable table, collapsibles)
129
+ - **Canvas Mode** - IPC system for external apps to push rich UI:
130
+ - Persistent browser canvas for agentic CLIs to display interactive UI
131
+ - WebSocket + HTTP bridge for bidirectional communication
132
+ - `streamweaver panel SESSION` - Open canvas in iTerm2 split pane (side-by-side with terminal)
133
+ - `streamweaver canvas-push SESSION` - Push DSL content to canvas
134
+ - `streamweaver canvas-wait SESSION` - Wait for button click, return JSON (ignores radio/checkbox)
135
+ - `streamweaver setup` - Configure Claude Code with bash permissions and panel skill
136
+ - High-level Ruby helpers: `Canvas.pick(session, options)`, `Canvas.confirm(session, message)`
137
+ - See [docs/canvas-roadmap.md](docs/canvas-roadmap.md) for full documentation
138
+ - **Button `id:` option** - Disambiguate buttons in loops to prevent callback collisions:
139
+ ```ruby
140
+ items.each { |i| button "Select", id: i[:name] do |s| ... end }
141
+ ```
142
+ - **Templates** - Pre-built UI patterns for common interactions:
143
+ - `wizard` - Multi-step forms with branching (`next: {branch_on: "field_name"}`)
144
+ - `choices` - Quick selection from options, returns `{choice: "Selected"}`
145
+ - `confirm` - Yes/No decisions, returns `{confirmed: true/false}`
146
+ - `info` - Display messages with action buttons, returns `{action: "Clicked"}`
147
+ - `table` - Data display with optional row selection
148
+ - `code` - Code display with syntax highlighting and line numbers
149
+ - `diff` - Unified diff display with add/remove highlighting
150
+ - Usage: `streamweaver template <name> <session> '<json-config>'`
151
+ - See [docs/templates.md](docs/templates.md) for full documentation
152
+ - **Dashboard Components** - Operations dashboard UI elements (best with `theme: :dark`):
153
+ - `status_dot` - Colored status indicators with optional pulse animation
154
+ - `badge` - Pill-shaped badges with variant colors (`:default`, `:danger`, `:warning`, `:success`)
155
+ - `stat_display` - Large value + label statistics (e.g., "42 TASKS")
156
+ - `type_tag` - Activity type badges (`:research`, `:task`, `:decision`, `:planning`)
157
+ - `pulse_indicator` - Animated system status indicators
158
+ - `priority_item` - Priority-colored items (`:critical`, `:high`, `:medium`, `:low`)
159
+ - `activity_item` - Time-stamped activity feed entries
160
+ - `app_shell` - Dashboard layout with main area and collapsible sidebar
161
+ - `expandable_card` - Cards that expand/collapse to show details
162
+ - **Table Component Enhancements**:
163
+ - `markdown: true` option for clickable links in table cells
164
+ - Smart header inference from array of hashes (no need to specify headers)
165
+ - Column DSL with formatters: `column :balance, format: :currency, align: :right`
166
+ - Built-in formatters: `:currency`, `:date`, `:number`, `:percent`, `:boolean`
167
+ - Interactive features: `sortable: true`, `sticky_header: true`
168
+ - Styling options: `striped: true`, `bordered: true`, `hoverable: true`, `compact: true`
169
+ - **Dark Theme** - Full dark mode with deep backgrounds, glow effects, and dashboard styling
170
+ - **`default:` option** for `text_field`, `text_area`, and `code_editor` to set initial values
171
+ - **Service Mode** - Single server renders multiple apps without per-app process management:
172
+ - `streamweaver <file.rb>` - Run app (auto-starts service if needed)
173
+ - `streamweaver list` - List all loaded apps with timing info
174
+ - `streamweaver remove <id>` - Remove a specific app
175
+ - `streamweaver clear` - Remove all apps
176
+ - `streamweaver admin` - Open admin dashboard
177
+ - `streamweaver status` - Show service status
178
+ - `streamweaver stop` - Stop background service
179
+ - Named sessions via `--name` flag for easier identification
180
+ - **Admin Dashboard** - StreamWeaver app managing other StreamWeaver apps (meta!)
181
+ - Shows service stats (apps loaded, PID, port)
182
+ - Lists all apps with timing (loaded/idle duration)
183
+ - Open/Remove buttons for each app
184
+ - Clear All Apps action
185
+ - **Multi-app routing** - Each app gets unique URL (`/apps/:app_id`)
186
+ - **URL prefix support** in adapter for service mode routing
187
+ - **Multi-theme system** with three built-in themes:
188
+ - `:default` - Warm Industrial (Source Sans 3, 17px, generous spacing)
189
+ - `:dashboard` - Data Dense (15px, tighter spacing, minimal accents)
190
+ - `:document` - Reading Mode (Crimson Pro serif, 19px, paper background)
191
+ - **Custom theme registration** via `StreamWeaver.register_theme`
192
+ - **Runtime theme switching** via `theme_switcher` component
193
+ - **Theme Tweaker app** (`examples/theme_tweaker.rb`) - Visual theme editor with live preview and export
194
+ - **`submit: false` option** for form components to disable HTMX auto-submit:
195
+ - `text_field :key, submit: false`
196
+ - `checkbox :key, "Label", submit: false`
197
+ - `select :key, choices, submit: false`
198
+ - `button "Label", submit: false` (display-only button)
199
+ - **Adapter pattern** for rendering - `StreamWeaver::Adapter::AlpineJS` handles all framework-specific rendering
200
+ - **New components**:
201
+ - **Charts** via Chart.js (CDN-loaded only when charts present):
202
+ - `BarChart` / `bar_chart` / `hbar_chart` - Bar charts (vertical/horizontal)
203
+ - `LineChart` / `line_chart` - Line charts with fill, smooth, points options
204
+ - `PieChart` / `pie_chart` / `doughnut_chart` - Pie and doughnut charts
205
+ - `StackedBarChart` / `stacked_bar_chart` - Multi-series stacked/grouped bars
206
+ - `sparkline` - Compact inline trends (no axes/labels)
207
+ - `area_chart` - Line chart with fill (shorthand)
208
+ - Multiple data input modes: inline hash, file+path, explicit labels/values, state-bound
209
+ - File loading with dot-path extraction (e.g., `"entries.-1.phases"`)
210
+ - `Markdown` / `md` - Full GitHub Flavored Markdown via Kramdown
211
+ - `Header` with `header1`-`header6` helpers for semantic headers
212
+ - `TextArea` for multi-line text input
213
+ - `RadioGroup` for single-choice radio buttons
214
+ - `Card` for styled content containers with `card_header`, `card_body`, `card_footer`
215
+ - `Collapsible` for expandable/collapsible sections
216
+ - `Columns` and `Column` for multi-column layouts with custom widths
217
+ - `Form` with `submit`/`cancel` for deferred submission forms (client-side only until submit)
218
+ - `ScoreTable` for color-coded metrics display
219
+ - `LessonText`, `Term`, `Phrase` for educational content with glossary tooltips
220
+ - `CheckboxGroup` with `item` for batch selection with select all/none
221
+ - `StatusBadge` for visual match indicators (🟢 Strong / 🟡 Maybe / 🔴 Skip)
222
+ - `TagButtons` for quick-select tag groups (single-select with destructive style option)
223
+ - `ExternalLinkButton` for buttons that open URLs in new tabs (with optional form submit)
224
+ - **Layout components**:
225
+ - `VStack` for vertical stacking with spacing and dividers
226
+ - `HStack` for horizontal stacking with alignment and justify options
227
+ - `Grid` for responsive grid layouts with breakpoint columns (`[1, 2, 3]`)
228
+ - **Navigation components**:
229
+ - `Tabs` with `tab` for tabbed navigation (variants: `:line`, `:enclosed`, `:soft-rounded`)
230
+ - `Breadcrumbs` with `crumb` for hierarchical navigation trails
231
+ - `Dropdown` with `trigger`, `menu`, `menu_item`, `menu_divider` for action menus
232
+ - **Modal dialogs**:
233
+ - `Modal` with `modal_footer` for overlay dialogs (sizes: `:sm`, `:md`, `:lg`, `:xl`)
234
+ - State-driven open/close via `state[:modal_key_open]`
235
+ - **Feedback components**:
236
+ - `Alert` for static feedback messages (variants: `:info`, `:success`, `:warning`, `:error`)
237
+ - `ToastContainer` with `show_toast`/`clear_toasts` for stacked notifications
238
+ - `ProgressBar` for visual progress indicators with variants and animation
239
+ - `Spinner` for loading indicators with sizes and labels
240
+ - **Event callbacks**:
241
+ - `on_change` callback for text_field, checkbox, select
242
+ - `on_blur` callback for text_field, text_area
243
+ - `debounce:` option for text input callbacks
244
+ - **Custom component modules** via `components:` option on `app`
245
+ - **Layout modes** via `layout:` parameter: `:default` (900px), `:wide` (1100px), `:full` (1400px), `:fluid` (100%)
246
+ - `default:` option for `select` component to set initial value
247
+ - `auto_close_window:` option for `run_once!` to close browser after submit
248
+ - Automatic "Submit to Agent" button in agentic mode
249
+ - CSS custom properties (CSS variables) for theme customization
250
+ - Comprehensive inline CSS with modern styling
251
+
252
+ ### Changed
253
+ - `Text` component now renders literal text only (no markdown parsing)
254
+ - Refactored component rendering to use adapter pattern for future extensibility
255
+ - **New "Warm Industrial" theme**: Source Sans 3 font, terracotta primary color (#c2410c), 17px base font with 1.7 line-height
256
+
257
+ ### Fixed
258
+ - **Canvas panel iTerm2 integration** - Panel now opens in split pane beside terminal
259
+ - **Canvas bridge port conflicts** - Auto-finds available port instead of hardcoded 4568
260
+ - **Canvas bridge race condition** - Waits for HTTP server health before returning URL
261
+ - **Canvas bridge reuse** - Verifies HTTP health on existing bridge before reusing
262
+ - **iTerm2 URL navigation** - Adds Escape keys to dismiss autocomplete before typing URL
263
+ - **Canvas-wait event filtering** - Now filters for 'action' events by default (button clicks only), ignoring checkbox/radio changes
264
+ - **Canvas 'Submitted' feedback** - Only shows for button clicks, not radio/checkbox changes
265
+ - **Canvas card styling** - Cards now render with proper borders, backgrounds, and the terracotta left accent
266
+ - **Canvas checkbox rendering** - Checkboxes wrapped in proper div with aligned label, inline markdown parsed
267
+ - Checkbox state properly handles unchecked values
268
+ - Agentic mode correctly outputs JSON to STDOUT after form submission
269
+ - Select `default:` now properly initializes Alpine.js state
270
+ - **Table markdown cells** - Use correct Phlex raw/safe pattern for markdown rendering in table cells
271
+ - **Table data: keyword** - Support `data:` keyword argument in table DSL method for explicit data passing
272
+ - **Tutorial session overflow** (2026-01-02) - Session cookie was exceeding 4KB limit due to `*_edited_code` keys; now filtered from session storage
273
+ - **Tutorial button ID mismatch** (2026-01-02) - Button IDs now use stable hash from `block.source_location` instead of render-order counter, preventing action failures when conditional content changes component tree
274
+ - **Tutorial Reset button** (2026-01-02) - Reset always renders with block for stable ID; uses flag pattern to survive session filtering
275
+
276
+ ## [0.1.0] - 2025-11-08
277
+
278
+ ### Added
279
+ - Initial release of StreamWeaver gem
280
+ - Core DSL for building interactive UIs with `app` helper method
281
+ - MVP components: TextField, Button, Text, Div, Checkbox, Select
282
+ - Sinatra-based web server with automatic port detection
283
+ - Phlex-based HTML rendering with inline CSS
284
+ - HTMX + Alpine.js frontend reactivity
285
+ - Session-based state management
286
+ - Single-file execution with `run!` method
287
+ - Browser auto-opening (cross-platform: macOS/Linux/Windows)
288
+ - **Agentic mode** with `run_once!` method for AI agent workflows
289
+ - Documentation and examples
290
+ - RSpec test suite
291
+
292
+ [Unreleased]: https://github.com/fkchang/stream_weaver/compare/v0.2.0...HEAD
293
+ [0.2.0]: https://github.com/fkchang/stream_weaver/compare/v0.1.0...v0.2.0
294
+ [0.1.0]: https://github.com/fkchang/stream_weaver/releases/tag/v0.1.0
data/CLAUDE.md ADDED
@@ -0,0 +1,67 @@
1
+ # Project Instructions for AI Agents
2
+
3
+ This file provides instructions and context for AI coding agents working on this project.
4
+
5
+ ## Work tracking authority
6
+
7
+ Tyrion is authoritative for all new StreamWeaver work. Run `tyrion prime` and
8
+ `tyrion status` at session start, and use the Tyrion skills for shaping, importing,
9
+ claiming, implementing, gating, checkpointing, and completing stories.
10
+
11
+ Beads remains readable only as a legacy backlog. Existing Beads IDs may be searched,
12
+ updated, and closed, but never use `bd create`, `bd ready`, `bd update --claim`, or
13
+ `bv` to create or select new work. Re-home still-relevant legacy work in Tyrion and
14
+ record the Beads ID in the Tyrion note/evidence trail.
15
+
16
+
17
+ ## Build & Test
18
+
19
+ _Add your build and test commands here_
20
+
21
+ ```bash
22
+ # Example:
23
+ # npm install
24
+ # npm test
25
+ ```
26
+
27
+ ## Architecture Overview
28
+
29
+ _Add a brief overview of your project architecture_
30
+
31
+ ## Visual Output
32
+
33
+ Whenever about to show something visually — a UI mockup, diagram, dashboard, layout/design comparison, or long-form doc — use the `streamweaver-visual-companion` skill. Do NOT use the `Artifact` tool, and do NOT write a local HTML file and open/screenshot it via Chrome browser tools (claude-in-chrome, superpowers-chrome, playwright) for this purpose. StreamWeaver's canvas-push is 5-7x cheaper in tokens than the chrome route and avoids GEA session conflicts. The skill's own "Fall back to Artifact only when..." section covers the rare exceptions (StreamWeaver unavailable, or a claude.ai-hosted link that must persist/reach someone outside this repo).
34
+
35
+ ## Conventions & Patterns
36
+
37
+ ### Git Hygiene Policy
38
+
39
+ This repo is destined for **open source release**. Every commit is a candidate for public history — git history cannot be sanitized after the fact without rewriting it.
40
+
41
+ **Staging rules:**
42
+
43
+ - NEVER use `git add -A`, `git add .`, or `git add -u`. Stage explicit paths only, and only files you created or intentionally changed for the task at hand.
44
+ - Every staged file must be StreamWeaver-related. Personal tooling state (`.registry/`, `.serena/`, `.beads/`), build artifacts (`dist/`, `*.gem`), and session/agent scratch content stay out (most are gitignored — if you see one in `git status`, add it to `.gitignore` rather than committing it).
45
+ - Do not commit content containing personal info: home directory paths (`/Users/...`), personal emails, employer references, real names of testers/colleagues, travel/location details. Use placeholders or relative paths.
46
+
47
+ **Enforcement:**
48
+
49
+ - `bin/check_git_hygiene` runs automatically from `.githooks/pre-commit`. It BLOCKS on home paths, personal email, personal-system references, secret-shaped strings, and staged files over 500KB; it WARNS on employer/tester-name references (full pattern list lives in the script itself, which is exempt from its own scan).
50
+ - False positive? Bypass once with `SW_HYGIENE_SKIP=1 git commit ...` — never disable the hook itself.
51
+
52
+ **Before the public flip (tracked in epic stream_weaver-b9g):**
53
+
54
+ - Full-history scan for the block patterns (e.g. `gitleaks` or `git log -S`), docs/ cleanup (stream_weaver-wh2), and repo hygiene sweep (stream_weaver-kj0).
55
+ - Decide the public committer identity — history currently uses a work email.
56
+
57
+ <!-- BEGIN TYRION-MANAGED-BLOCK v1 sha256:bc9a54f4421ec81e87306bdadc00e7524d992aaa63e04ed655f8670b5cd1bc0f -->
58
+ ## Tyrion
59
+
60
+ This repo is tracked by Tyrion, a resumability ledger for coding agents.
61
+
62
+ Rules:
63
+ - claim before code (tyrion claim-next)
64
+ - evidence via tyrion note/check, not ad hoc
65
+
66
+ Run `tyrion prime` for the live session briefing — active epic/story, next action, unmet criteria.
67
+ <!-- END TYRION-MANAGED-BLOCK -->
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Forrest Chang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.