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.
- checksums.yaml +7 -0
- data/.beads/.gitignore +77 -0
- data/.beads/README.md +81 -0
- data/.beads/config.yaml +54 -0
- data/.beads/metadata.json +7 -0
- data/.rspec +3 -0
- data/AGENTS.md +91 -0
- data/CHANGELOG.md +294 -0
- data/CLAUDE.md +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +565 -0
- data/Rakefile +13 -0
- data/assets/streamweaver-hero.jpg +0 -0
- data/bench/baselines/ledger.rb +58 -0
- data/bench/baselines/warroom.rb +51 -0
- data/bench/fixtures/ledger.rb +76 -0
- data/bench/fixtures/warroom.rb +60 -0
- data/bench/results/6daf8b8.md +102 -0
- data/bench/results/c8d749b.md +102 -0
- data/bench/results/eb06ab9.md +102 -0
- data/bench/run.rb +151 -0
- data/bench/support.rb +84 -0
- data/copy_this/dto_run_sample_run_7631.html +220 -0
- data/docs/SERVICE_MODE.md +302 -0
- data/docs/architecture/how_streamweaver_works.md +772 -0
- data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
- data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
- data/docs/canvas-ipc-session-summary.md +127 -0
- data/docs/canvas-panel-workflow.md +172 -0
- data/docs/canvas-read.md +147 -0
- data/docs/canvas-roadmap.md +275 -0
- data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
- data/docs/claude-code-companion-skill-spec.md +567 -0
- data/docs/components_reference.md +1223 -0
- data/docs/crud-patterns.md +265 -0
- data/docs/endpoints.md +122 -0
- data/docs/for_llms.md +1 -0
- data/docs/form-patterns.md +165 -0
- data/docs/frontend-only.md +169 -0
- data/docs/html-artifact-audit.md +238 -0
- data/docs/ideas/2025-01-01-as-a-service.md +203 -0
- data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
- data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
- data/docs/ideas/dhh-review-future-refactors.md +142 -0
- data/docs/opal-jamstack.md +47 -0
- data/docs/opal-npm-direction.md +184 -0
- data/docs/opal-spike-findings.md +194 -0
- data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
- data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
- data/docs/plans/2026-01-02-charts-design.md +288 -0
- data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
- data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
- data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
- data/docs/plans/canvas-claude-project-design.md +238 -0
- data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
- data/docs/plans/org-doc-preview-surfaces.md +293 -0
- data/docs/plans/shadcn-polish-plan.md +417 -0
- data/docs/porting-artifacts.md +252 -0
- data/docs/reference/agent-skills-comparison.md +369 -0
- data/docs/reference/travel-state-prd.artifact.html +780 -0
- data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
- data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
- data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
- data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
- data/docs/research/frontend-only-matrix.md +278 -0
- data/docs/research/streamweaver-way-spike-findings.md +529 -0
- data/docs/resource-dsl.md +342 -0
- data/docs/routing.md +302 -0
- data/docs/ruby-ui-comparison.md +319 -0
- data/docs/shared-dsl-fragments.md +356 -0
- data/docs/streamweaver-for-ai-agents.md +401 -0
- data/docs/streamweaver-frontend-vision.md +123 -0
- data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
- data/docs/streamweaver_canvas/save_example.rb +9 -0
- data/docs/templates.md +213 -0
- data/docs/testing.md +155 -0
- data/docs/theming-hooks.md +281 -0
- data/docs/tutorials/the-streamweaver-way.md +882 -0
- data/docs/university/capability-inventory.md +137 -0
- data/docs/university/dependency-survey.md +114 -0
- data/docs/university/design-spec.md +217 -0
- data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
- data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
- data/docs/university/mockups/course-list-dark.png +0 -0
- data/docs/university/mockups/course-list-light.png +0 -0
- data/docs/university/mockups/course_canvas_mockup.rb +741 -0
- data/docs/university/mockups/step-screen-dark.png +0 -0
- data/docs/university/mockups/step-screen-light.png +0 -0
- data/docs/university/roadmap.md +72 -0
- data/docs/university/send-to-coworker.md +77 -0
- data/docs/university/worker-session-mining.md +109 -0
- data/docs/visual/browser-rendering-spike.md +173 -0
- data/docs/visual/builder-visual-plan-analysis.md +378 -0
- data/docs/visual/sw-plan-format-exploration.md +511 -0
- data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
- data/docs/visual-skills/PROGRESS.md +60 -0
- data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
- data/docs/visual-skills/analysis/components.md +317 -0
- data/docs/visual-skills/analysis/overlap.md +360 -0
- data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
- data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
- data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
- data/docs/visual-skills/analysis/visual-explainer.md +964 -0
- data/docs/visual-skills/blog/blog-series-outline.md +39 -0
- data/docs/visual-skills/blog/token-efficiency.md +73 -0
- data/docs/visual-skills/design/architecture.md +1725 -0
- data/docs/visual-skills/design/codex-review.md +340 -0
- data/docs/visual-skills/design/dhh-review.md +209 -0
- data/docs/visual-skills/design/evolution.md +100 -0
- data/docs/visual-skills/design/gemini-review.md +55 -0
- data/docs/visual-skills/design/review-synthesis.md +149 -0
- data/docs/visual-skills/implementation/STATE.md +22 -0
- data/docs/visual-skills/implementation/plan.md +347 -0
- data/docs/visual-skills/implementation/spike-findings.md +237 -0
- data/docs/visual-skills/implementation/tasks.md +702 -0
- data/docs/visual-skills/lessons-learned/process.md +145 -0
- data/examples/README.md +96 -0
- data/examples/advanced/all_components.rb +177 -0
- data/examples/advanced/examples_browser.rb +394 -0
- data/examples/advanced/teachables_browser.rb +261 -0
- data/examples/advanced/theme_tweaker.rb +366 -0
- data/examples/advanced/tutorial.rb +1528 -0
- data/examples/agentic/agentic_form.rb +30 -0
- data/examples/agentic/agentic_form_autoclose.rb +30 -0
- data/examples/agentic/cultivation_tracker.rb +69 -0
- data/examples/basic/hello_world.rb +22 -0
- data/examples/basic/opal_tabs_table.rb +44 -0
- data/examples/basic/todo_list.rb +39 -0
- data/examples/button_loading_test.rb +36 -0
- data/examples/canvas/mermaid_canvas_demo.sh +151 -0
- data/examples/charts/bar_chart_demo.rb +57 -0
- data/examples/charts/line_chart_demo.rb +73 -0
- data/examples/charts/pie_area_demo.rb +87 -0
- data/examples/charts/stacked_bar_chart_demo.rb +69 -0
- data/examples/claude_code/README.md +85 -0
- data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
- data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
- data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
- data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
- data/examples/claude_code/codebreaker/README.md +213 -0
- data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
- data/examples/claude_code/tutorial/README.md +102 -0
- data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
- data/examples/claude_code/verification_flow/README.md +126 -0
- data/examples/components/annotated_code_demo.rb +54 -0
- data/examples/components/callout_demo.rb +33 -0
- data/examples/components/checkbox_group_demo.rb +46 -0
- data/examples/components/design_review.css +468 -0
- data/examples/components/design_review_demo.rb +44 -0
- data/examples/components/design_review_dsl.rb +300 -0
- data/examples/components/diff_block_demo.rb +112 -0
- data/examples/components/events_demo.rb +184 -0
- data/examples/components/form_demo.rb +62 -0
- data/examples/components/lesson_demo.rb +53 -0
- data/examples/components/markdown_demo.rb +110 -0
- data/examples/components/mermaid_demo.rb +166 -0
- data/examples/components/pareto_set.rb +64 -0
- data/examples/components/prd_demo.rb +21 -0
- data/examples/components/prd_dsl.rb +385 -0
- data/examples/components/quiz_demo.rb +47 -0
- data/examples/components/run_viewer_demo.rb +100 -0
- data/examples/components/score_and_collapsible_demo.rb +82 -0
- data/examples/components/select_stale_value_smoke_test.rb +39 -0
- data/examples/components/table_demo.rb +247 -0
- data/examples/components/timer_with_state_demo.rb +58 -0
- data/examples/components/todo_due_dates.rb +63 -0
- data/examples/components/uat_gaps_demo.rb +112 -0
- data/examples/dashboard/feed_simulator.rb +60 -0
- data/examples/dashboard/live_dashboard.rb +80 -0
- data/examples/dashboard_components.rb +108 -0
- data/examples/deferred_fragments_demo.rb +62 -0
- data/examples/generate_more_spike/README.md +63 -0
- data/examples/generate_more_spike/app.rb +441 -0
- data/examples/git_health.sh +401 -0
- data/examples/layout/layout_components_demo.rb +174 -0
- data/examples/layout/modal_demo.rb +215 -0
- data/examples/layout/navigation_demo.rb +227 -0
- data/examples/layout/route_tabs_demo.rb +52 -0
- data/examples/layout/routing_demo.rb +52 -0
- data/examples/layout/scroll_box_demo.rb +154 -0
- data/examples/lazy_fragments_demo.rb +151 -0
- data/examples/my_todos/README.md +29 -0
- data/examples/my_todos/my_todos.rb +312 -0
- data/examples/my_todos/store.rb +86 -0
- data/examples/opal/reactive_demo.rb +62 -0
- data/examples/opal/scenarios/s1_counter.rb +15 -0
- data/examples/opal/scenarios/s2_search_filter.rb +17 -0
- data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
- data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
- data/examples/opal/scenarios/s5_watch.rb +39 -0
- data/examples/opal/scenarios/s6_on_start.rb +20 -0
- data/examples/opal/scenarios/s7_wizard.rb +33 -0
- data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
- data/examples/opal/scenarios/s9_dashboard.rb +27 -0
- data/examples/operations_dashboard_demo.rb +167 -0
- data/examples/panel_demo.sh +447 -0
- data/examples/parity/assets/tyrion_slice.css +86 -0
- data/examples/parity/rivet_people_slice.rb +182 -0
- data/examples/parity/tyrion_components.css +777 -0
- data/examples/parity/tyrion_warroom_components.rb +414 -0
- data/examples/parity/tyrion_warroom_slice.rb +549 -0
- data/examples/puma_dev/README.md +124 -0
- data/examples/puma_dev/config.ru +34 -0
- data/examples/puma_dev/standalone_app.rb +34 -0
- data/examples/scaffolding/blog.rb +49 -0
- data/examples/scaffolding/utf_lite.rb +105 -0
- data/examples/styling/feedback_demo.rb +229 -0
- data/examples/styling/style_showcase.rb +172 -0
- data/examples/styling/theme_demo.rb +508 -0
- data/examples/timer_health_checker.rb +111 -0
- data/examples/timer_showcase.rb +131 -0
- data/examples/tutorials/render_markdown.rb +112 -0
- data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
- data/examples/tutorials/tutorial_content.rb +1082 -0
- data/examples/visual_skills/design_deck_demo.rb +314 -0
- data/examples/visual_skills/explainer_demo.rb +499 -0
- data/exe/streamweaver +9 -0
- data/gsd/ROADMAP-1.0.md +167 -0
- data/gsd/STATE.md +13 -0
- data/gsd/research/market-positioning-research.md +139 -0
- data/gsd/research/production-patterns-research.md +270 -0
- data/gsd/research/repo-audit-1.0.md +274 -0
- data/lib/stream_weaver/action_token.rb +58 -0
- data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
- data/lib/stream_weaver/adapter/base.rb +591 -0
- data/lib/stream_weaver/adapter/opal.rb +334 -0
- data/lib/stream_weaver/adapter/static.rb +1118 -0
- data/lib/stream_weaver/admin.rb +176 -0
- data/lib/stream_weaver/app.rb +1821 -0
- data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
- data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
- data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
- data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
- data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
- data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
- data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
- data/lib/stream_weaver/canvas/bridge.rb +239 -0
- data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
- data/lib/stream_weaver/canvas/client.rb +298 -0
- data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
- data/lib/stream_weaver/canvas/doc_store.rb +252 -0
- data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
- data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
- data/lib/stream_weaver/canvas/gist_store.rb +135 -0
- data/lib/stream_weaver/canvas/helpers.rb +109 -0
- data/lib/stream_weaver/canvas/history.rb +90 -0
- data/lib/stream_weaver/canvas/protocol.rb +86 -0
- data/lib/stream_weaver/canvas/reader.rb +853 -0
- data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
- data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
- data/lib/stream_weaver/canvas/session.rb +132 -0
- data/lib/stream_weaver/cli.rb +3235 -0
- data/lib/stream_weaver/component_assets.rb +70 -0
- data/lib/stream_weaver/component_registry.rb +67 -0
- data/lib/stream_weaver/component_renderer.rb +48 -0
- data/lib/stream_weaver/components/annotated_code.rb +53 -0
- data/lib/stream_weaver/components/api_endpoint.rb +42 -0
- data/lib/stream_weaver/components/callout.rb +59 -0
- data/lib/stream_weaver/components/chart.rb +84 -0
- data/lib/stream_weaver/components/code_block.rb +77 -0
- data/lib/stream_weaver/components/comparison.rb +39 -0
- data/lib/stream_weaver/components/decision.rb +38 -0
- data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
- data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
- data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
- data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
- data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
- data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
- data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
- data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
- data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
- data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
- data/lib/stream_weaver/components/diff_block.rb +125 -0
- data/lib/stream_weaver/components/doc_header.rb +57 -0
- data/lib/stream_weaver/components/image_block.rb +70 -0
- data/lib/stream_weaver/components/implementation_map.rb +33 -0
- data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
- data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
- data/lib/stream_weaver/components/mermaid.rb +79 -0
- data/lib/stream_weaver/components/pipeline.rb +63 -0
- data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
- data/lib/stream_weaver/components/slide_container.rb +141 -0
- data/lib/stream_weaver/components/timeline_event.rb +58 -0
- data/lib/stream_weaver/components/wireframe.rb +29 -0
- data/lib/stream_weaver/components/wireframe_block.rb +34 -0
- data/lib/stream_weaver/components.rb +2711 -0
- data/lib/stream_weaver/css.rb +230 -0
- data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
- data/lib/stream_weaver/display_dsl.rb +1004 -0
- data/lib/stream_weaver/export/html_exporter.rb +478 -0
- data/lib/stream_weaver/feed.rb +34 -0
- data/lib/stream_weaver/feed_builder.rb +31 -0
- data/lib/stream_weaver/fonts.rb +33 -0
- data/lib/stream_weaver/interaction_runner.rb +487 -0
- data/lib/stream_weaver/iterm.rb +460 -0
- data/lib/stream_weaver/layout_registry.rb +92 -0
- data/lib/stream_weaver/opal/bridge.rb +52 -0
- data/lib/stream_weaver/opal/builder.rb +193 -0
- data/lib/stream_weaver/opal/env.rb +29 -0
- data/lib/stream_weaver/opal/reactive_state.rb +75 -0
- data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
- data/lib/stream_weaver/opal/renderer.rb +77 -0
- data/lib/stream_weaver/opal/runtime.rb +250 -0
- data/lib/stream_weaver/opal/shell.rb +145 -0
- data/lib/stream_weaver/opal/string_bridge.rb +50 -0
- data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
- data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
- data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
- data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
- data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
- data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
- data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
- data/lib/stream_weaver/opal_entry.rb +136 -0
- data/lib/stream_weaver/org/inline.rb +95 -0
- data/lib/stream_weaver/org/reader.rb +561 -0
- data/lib/stream_weaver/org/recording_context.rb +86 -0
- data/lib/stream_weaver/org/source_splitter.rb +63 -0
- data/lib/stream_weaver/org/writer.rb +300 -0
- data/lib/stream_weaver/page_shell.rb +530 -0
- data/lib/stream_weaver/portfile.rb +79 -0
- data/lib/stream_weaver/pushable.rb +42 -0
- data/lib/stream_weaver/resource/default_views.rb +111 -0
- data/lib/stream_weaver/resource/field_input.rb +25 -0
- data/lib/stream_weaver/resource/state_keys.rb +16 -0
- data/lib/stream_weaver/resource/store.rb +18 -0
- data/lib/stream_weaver/resource.rb +104 -0
- data/lib/stream_weaver/server.rb +1471 -0
- data/lib/stream_weaver/service.rb +1240 -0
- data/lib/stream_weaver/service_client.rb +104 -0
- data/lib/stream_weaver/session_store.rb +186 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
- data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
- data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
- data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
- data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
- data/lib/stream_weaver/streamer.rb +63 -0
- data/lib/stream_weaver/templates/choices.rb +149 -0
- data/lib/stream_weaver/templates/code.rb +157 -0
- data/lib/stream_weaver/templates/confirm.rb +110 -0
- data/lib/stream_weaver/templates/diff.rb +195 -0
- data/lib/stream_weaver/templates/info.rb +142 -0
- data/lib/stream_weaver/templates/table.rb +169 -0
- data/lib/stream_weaver/templates/wizard.rb +271 -0
- data/lib/stream_weaver/theme/auto_mode.rb +116 -0
- data/lib/stream_weaver/theme/presets.rb +555 -0
- data/lib/stream_weaver/theme.rb +639 -0
- data/lib/stream_weaver/university/canvas.rb +924 -0
- data/lib/stream_weaver/university/course.rb +699 -0
- data/lib/stream_weaver/university/demos/counter.rb +33 -0
- data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
- data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
- data/lib/stream_weaver/university/demos.rb +50 -0
- data/lib/stream_weaver/university/listener.rb +467 -0
- data/lib/stream_weaver/university/progress.rb +201 -0
- data/lib/stream_weaver/university/runner.rb +138 -0
- data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
- data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
- data/lib/stream_weaver/utils.rb +31 -0
- data/lib/stream_weaver/version.rb +5 -0
- data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
- data/lib/stream_weaver/views.rb +3940 -0
- data/lib/stream_weaver.rb +122 -0
- data/llms.txt +1269 -0
- data/sig/stream_weaver.rbs +4 -0
- metadata +598 -0
data/bench/support.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "rack/test"
|
|
7
|
+
require "tmpdir"
|
|
8
|
+
|
|
9
|
+
ENV["RACK_ENV"] ||= "test"
|
|
10
|
+
ENV["SW_SESSION_DIR"] ||= File.join(Dir.tmpdir, "stream-weaver-bench-sessions")
|
|
11
|
+
FileUtils.mkdir_p(ENV.fetch("SW_SESSION_DIR"))
|
|
12
|
+
require "stream_weaver"
|
|
13
|
+
|
|
14
|
+
module StreamWeaverBench
|
|
15
|
+
PEOPLE = Array.new(50) do |index|
|
|
16
|
+
id = index + 1
|
|
17
|
+
{ id: id, name: format("Person %02d", id), email: format("person%02d@example.test", id), touched: "2026-07-01" }
|
|
18
|
+
end.freeze
|
|
19
|
+
STORIES = Array.new(12) do |index|
|
|
20
|
+
id = index + 1
|
|
21
|
+
{ id: id, title: format("Story %02d", id), column: %w[Ready Active Done][index % 3], notes: ["Seed note #{id}"] }
|
|
22
|
+
end.freeze
|
|
23
|
+
|
|
24
|
+
class MemoryStore
|
|
25
|
+
def initialize(records)
|
|
26
|
+
@records = StreamWeaverBench.deep_copy(records)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def all = @records
|
|
30
|
+
def find(id) = @records.find { |record| record[:id] == id }
|
|
31
|
+
def create(attributes) = @records << attributes.dup
|
|
32
|
+
|
|
33
|
+
def update(id, attributes)
|
|
34
|
+
find(id)&.merge!(attributes)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def destroy(id)
|
|
38
|
+
@records.reject! { |record| record[:id] == id }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Metrics
|
|
43
|
+
attr_accessor :rebuilds, :callbacks
|
|
44
|
+
|
|
45
|
+
def initialize = reset!
|
|
46
|
+
def reset! = (@rebuilds = @callbacks = 0)
|
|
47
|
+
def callback! = (@callbacks += 1)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module Instrumented
|
|
51
|
+
attr_accessor :bench_metrics
|
|
52
|
+
|
|
53
|
+
def rebuild_with_state(...)
|
|
54
|
+
bench_metrics.rebuilds += 1 if bench_metrics
|
|
55
|
+
super
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.instrument(app, metrics)
|
|
60
|
+
app.singleton_class.prepend(Instrumented)
|
|
61
|
+
app.bench_metrics = metrics
|
|
62
|
+
app
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.deep_copy(value) = Marshal.load(Marshal.dump(value))
|
|
66
|
+
|
|
67
|
+
module Html
|
|
68
|
+
module_function
|
|
69
|
+
|
|
70
|
+
def action_path(body, label)
|
|
71
|
+
tags = body.scan(/<button\b[^>]*>.*?<\/button>/m)
|
|
72
|
+
tag = tags.find { |candidate| candidate.gsub(/<[^>]+>/, "").include?(label) }
|
|
73
|
+
raise "button not found: #{label}" unless tag
|
|
74
|
+
tag[%r{hx-post="([^"]+)"}, 1] || raise("action path missing for #{label}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def fragment_token(body)
|
|
78
|
+
encoded = body[/name="_sw_fragment" value="([^"]+)"/, 1]
|
|
79
|
+
encoded && CGI.unescapeHTML(encoded)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def node_count(body) = body.scan(/<(?!\/|!|\?)[a-z][^>]*>/i).length
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Run Viewer — abc-123</title>
|
|
6
|
+
<style>
|
|
7
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
8
|
+
body { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 13px; background: #1a1a2e; color: #e0e0e0; padding: 16px; }
|
|
9
|
+
header { margin-bottom: 16px; }
|
|
10
|
+
h1 { font-size: 18px; color: #fff; margin-bottom: 8px; }
|
|
11
|
+
.meta { color: #888; margin-bottom: 4px; }
|
|
12
|
+
.meta span { margin-right: 16px; }
|
|
13
|
+
.timeline { display: flex; flex-direction: column; gap: 2px; }
|
|
14
|
+
.event { padding: 6px 10px; border-radius: 4px; cursor: pointer; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; border-left: 3px solid transparent; }
|
|
15
|
+
.event:hover { background: #2a2a4a; }
|
|
16
|
+
.idx { color: #555; width: 24px; text-align: right; flex-shrink: 0; }
|
|
17
|
+
.ts { color: #666; flex-shrink: 0; }
|
|
18
|
+
.type-badge { display: inline-block; width: 100px; padding: 1px 6px; border-radius: 3px; text-align: center; font-size: 11px; flex-shrink: 0; }
|
|
19
|
+
.label { color: #ccc; }
|
|
20
|
+
.detail { width: 100%; padding: 10px 0 6px 36px; }
|
|
21
|
+
.field { margin-bottom: 4px; }
|
|
22
|
+
.field strong { color: #aaa; }
|
|
23
|
+
.field pre { margin: 4px 0 4px 12px; padding: 8px; background: #0d0d1a; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; }
|
|
24
|
+
.phase { border-left-color: #00bcd4; }
|
|
25
|
+
.phase .type-badge { background: #0e3a3f; color: #00bcd4; }
|
|
26
|
+
.snapshot { border-left-color: #ffc107; }
|
|
27
|
+
.snapshot .type-badge { background: #3f3500; color: #ffc107; }
|
|
28
|
+
.intervention { border-left-color: #ce93d8; }
|
|
29
|
+
.intervention .type-badge { background: #3a1a44; color: #ce93d8; }
|
|
30
|
+
.timeout { border-left-color: #ef5350; }
|
|
31
|
+
.timeout .type-badge { background: #3f0e0e; color: #ef5350; }
|
|
32
|
+
.guard { border-left-color: #ef5350; }
|
|
33
|
+
.guard .type-badge { background: #3f0e0e; color: #ef5350; }
|
|
34
|
+
.final { border-left-color: #66bb6a; }
|
|
35
|
+
.final .type-badge { background: #0e3f12; color: #66bb6a; }
|
|
36
|
+
</style>
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<header>
|
|
40
|
+
<h1>Run Viewer</h1>
|
|
41
|
+
<div class="meta">
|
|
42
|
+
<span>run_id: abc-123</span>
|
|
43
|
+
<span>target: codex</span>
|
|
44
|
+
<span>duration: 60.0s</span>
|
|
45
|
+
<span>events: 11</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="meta">
|
|
48
|
+
<span>interventions: 2</span>
|
|
49
|
+
<span>timeouts: 0</span>
|
|
50
|
+
<span>snapshots: 2</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="meta"><span>source: demo/sample_run.jsonl</span></div>
|
|
53
|
+
</header>
|
|
54
|
+
<main><div class="timeline"><div class="event phase" onclick="toggle(this)">
|
|
55
|
+
<span class="idx">0</span>
|
|
56
|
+
<span class="type-badge">phase</span>
|
|
57
|
+
<span class="ts">10:00:00</span>
|
|
58
|
+
<span class="label">launch</span>
|
|
59
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
60
|
+
<div class="field"><strong>phase:</strong> launch</div>
|
|
61
|
+
<div class="field"><strong>target:</strong> codex</div>
|
|
62
|
+
<div class="field"><strong>baseline_files:</strong> lib/app.rb</div>
|
|
63
|
+
<div class="field"><strong>step_id:</strong> launch</div>
|
|
64
|
+
<div class="field"><strong>target_socket_name:</strong> dto-codex</div>
|
|
65
|
+
<div class="field"><strong>target_session_name:</strong> run-1</div></div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="event phase" onclick="toggle(this)">
|
|
69
|
+
<span class="idx">1</span>
|
|
70
|
+
<span class="type-badge">phase</span>
|
|
71
|
+
<span class="ts">10:00:05</span>
|
|
72
|
+
<span class="label">settle step=1</span>
|
|
73
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
74
|
+
<div class="field"><strong>phase:</strong> settle</div>
|
|
75
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
76
|
+
<div class="field"><strong>step_id:</strong> step-1</div></div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="event phase" onclick="toggle(this)">
|
|
80
|
+
<span class="idx">2</span>
|
|
81
|
+
<span class="type-badge">phase</span>
|
|
82
|
+
<span class="ts">10:00:15</span>
|
|
83
|
+
<span class="label">observe step=1</span>
|
|
84
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
85
|
+
<div class="field"><strong>phase:</strong> observe</div>
|
|
86
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
87
|
+
<div class="field"><strong>step_id:</strong> step-1</div></div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="event snapshot" onclick="toggle(this)">
|
|
91
|
+
<span class="idx">3</span>
|
|
92
|
+
<span class="type-badge">snapshot</span>
|
|
93
|
+
<span class="ts">10:00:16</span>
|
|
94
|
+
<span class="label">state=working verification_ok=false step=1</span>
|
|
95
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
96
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
97
|
+
<div class="field"><strong>step_id:</strong> step-1</div>
|
|
98
|
+
<div class="field"><strong>state:</strong> working</div>
|
|
99
|
+
<div class="field"><strong>waiting_for_input:</strong> false</div>
|
|
100
|
+
<div class="field"><strong>busy:</strong> true</div>
|
|
101
|
+
<div class="field"><strong>likely_complete:</strong> false</div>
|
|
102
|
+
<div class="field"><strong>signals_found:</strong> []</div>
|
|
103
|
+
<div class="field"><strong>signals_missing:</strong> test passes</div>
|
|
104
|
+
<div class="field"><strong>verification_ok:</strong> false</div>
|
|
105
|
+
<div class="field"><strong>changed_files:</strong> lib/app.rb</div>
|
|
106
|
+
<div class="field"><strong>last_assistant_lines:</strong> Writing the initial implementation...</div></div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="event phase" onclick="toggle(this)">
|
|
110
|
+
<span class="idx">4</span>
|
|
111
|
+
<span class="type-badge">phase</span>
|
|
112
|
+
<span class="ts">10:00:17</span>
|
|
113
|
+
<span class="label">decide step=1</span>
|
|
114
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
115
|
+
<div class="field"><strong>phase:</strong> decide</div>
|
|
116
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
117
|
+
<div class="field"><strong>step_id:</strong> step-1</div>
|
|
118
|
+
<div class="field"><strong>driver:</strong> ClaudeDriverBackend</div></div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="event intervention" onclick="toggle(this)">
|
|
122
|
+
<span class="idx">5</span>
|
|
123
|
+
<span class="type-badge">intervention</span>
|
|
124
|
+
<span class="ts">10:00:22</span>
|
|
125
|
+
<span class="label">action=question terminal=false step=1</span>
|
|
126
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
127
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
128
|
+
<div class="field"><strong>step_id:</strong> step-1</div>
|
|
129
|
+
<div class="field"><strong>action:</strong> question</div>
|
|
130
|
+
<div class="field"><strong>intent:</strong> Clarify scope</div>
|
|
131
|
+
<div class="field"><strong>why:</strong> Worker jumped into coding without asking about interface shape</div>
|
|
132
|
+
<div class="field"><strong>next_step:</strong> List the product choices before writing code</div>
|
|
133
|
+
<div class="field"><strong>stop_if:</strong> Worker starts asking clarifying questions</div>
|
|
134
|
+
<div class="field"><strong>rendered_message:</strong> <pre>INTENT: Clarify scope
|
|
135
|
+
WHY: You started coding without clarifying the interface shape.
|
|
136
|
+
NEXT: List the product choices first.
|
|
137
|
+
STOP IF: You start asking clarifying questions.</pre></div>
|
|
138
|
+
<div class="field"><strong>terminal:</strong> false</div></div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div class="event phase" onclick="toggle(this)">
|
|
142
|
+
<span class="idx">6</span>
|
|
143
|
+
<span class="type-badge">phase</span>
|
|
144
|
+
<span class="ts">10:00:23</span>
|
|
145
|
+
<span class="label">send step=1</span>
|
|
146
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
147
|
+
<div class="field"><strong>phase:</strong> send</div>
|
|
148
|
+
<div class="field"><strong>step:</strong> 1</div>
|
|
149
|
+
<div class="field"><strong>step_id:</strong> step-1</div></div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div class="event phase" onclick="toggle(this)">
|
|
153
|
+
<span class="idx">7</span>
|
|
154
|
+
<span class="type-badge">phase</span>
|
|
155
|
+
<span class="ts">10:00:35</span>
|
|
156
|
+
<span class="label">settle step=2</span>
|
|
157
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
158
|
+
<div class="field"><strong>phase:</strong> settle</div>
|
|
159
|
+
<div class="field"><strong>step:</strong> 2</div>
|
|
160
|
+
<div class="field"><strong>step_id:</strong> step-2</div></div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div class="event snapshot" onclick="toggle(this)">
|
|
164
|
+
<span class="idx">8</span>
|
|
165
|
+
<span class="type-badge">snapshot</span>
|
|
166
|
+
<span class="ts">10:00:45</span>
|
|
167
|
+
<span class="label">state=waiting verification_ok=true step=2</span>
|
|
168
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
169
|
+
<div class="field"><strong>step:</strong> 2</div>
|
|
170
|
+
<div class="field"><strong>step_id:</strong> step-2</div>
|
|
171
|
+
<div class="field"><strong>state:</strong> waiting</div>
|
|
172
|
+
<div class="field"><strong>waiting_for_input:</strong> true</div>
|
|
173
|
+
<div class="field"><strong>busy:</strong> false</div>
|
|
174
|
+
<div class="field"><strong>likely_complete:</strong> false</div>
|
|
175
|
+
<div class="field"><strong>signals_found:</strong> test passes</div>
|
|
176
|
+
<div class="field"><strong>signals_missing:</strong> []</div>
|
|
177
|
+
<div class="field"><strong>verification_ok:</strong> true</div>
|
|
178
|
+
<div class="field"><strong>changed_files:</strong> lib/app.rb, test/test_app.rb</div>
|
|
179
|
+
<div class="field"><strong>last_assistant_lines:</strong> All tests pass. Here is the summary...</div></div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="event intervention" onclick="toggle(this)">
|
|
183
|
+
<span class="idx">9</span>
|
|
184
|
+
<span class="type-badge">intervention</span>
|
|
185
|
+
<span class="ts">10:00:50</span>
|
|
186
|
+
<span class="label">action=verify terminal=true step=2</span>
|
|
187
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
188
|
+
<div class="field"><strong>step:</strong> 2</div>
|
|
189
|
+
<div class="field"><strong>step_id:</strong> step-2</div>
|
|
190
|
+
<div class="field"><strong>action:</strong> verify</div>
|
|
191
|
+
<div class="field"><strong>intent:</strong> Confirm completion</div>
|
|
192
|
+
<div class="field"><strong>why:</strong> Signals met and verification passes</div>
|
|
193
|
+
<div class="field"><strong>next_step:</strong> Summarize what changed</div>
|
|
194
|
+
<div class="field"><strong>stop_if:</strong> nil</div>
|
|
195
|
+
<div class="field"><strong>rendered_message:</strong> <pre>INTENT: Confirm completion
|
|
196
|
+
WHY: All signals met.
|
|
197
|
+
NEXT: Summarize.
|
|
198
|
+
STOP IF: n/a</pre></div>
|
|
199
|
+
<div class="field"><strong>terminal:</strong> true</div></div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="event final" onclick="toggle(this)">
|
|
203
|
+
<span class="idx">10</span>
|
|
204
|
+
<span class="type-badge">final</span>
|
|
205
|
+
<span class="ts">10:01:00</span>
|
|
206
|
+
<span class="label">interventions=2</span>
|
|
207
|
+
<div class="detail" style="display:none"><div class="field"><strong>run_id:</strong> abc-123</div>
|
|
208
|
+
<div class="field"><strong>interventions:</strong> 2</div>
|
|
209
|
+
<div class="field"><strong>final_transcript_tail:</strong> Done. All tests pass.</div>
|
|
210
|
+
<div class="field"><strong>step_id:</strong> final</div></div>
|
|
211
|
+
</div>
|
|
212
|
+
</div></main>
|
|
213
|
+
<script>
|
|
214
|
+
function toggle(el) {
|
|
215
|
+
var d = el.querySelector('.detail');
|
|
216
|
+
if (d) d.style.display = d.style.display === 'none' ? 'block' : 'none';
|
|
217
|
+
}
|
|
218
|
+
</script>
|
|
219
|
+
</body>
|
|
220
|
+
</html>
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# StreamWeaver Service Mode
|
|
2
|
+
|
|
3
|
+
StreamWeaver operates in two modes: **standalone scripts** and **service mode**. This document explains when to use each and how the service architecture works.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install stream_weaver
|
|
9
|
+
|
|
10
|
+
# Interactive tutorial
|
|
11
|
+
streamweaver tutorial
|
|
12
|
+
|
|
13
|
+
# Browse all examples
|
|
14
|
+
streamweaver showcase
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Execution Modes
|
|
18
|
+
|
|
19
|
+
### 1. Standalone Scripts (`ruby app.rb`)
|
|
20
|
+
|
|
21
|
+
For simple, one-off scripts where you want a quick UI:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# greeting.rb
|
|
25
|
+
require 'stream_weaver'
|
|
26
|
+
|
|
27
|
+
app "Greeting" do
|
|
28
|
+
text_field :name, placeholder: "Your name"
|
|
29
|
+
|
|
30
|
+
if state[:name].to_s.strip != ""
|
|
31
|
+
text "Hello, #{state[:name]}!"
|
|
32
|
+
end
|
|
33
|
+
end.run!
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run with: `ruby greeting.rb`
|
|
37
|
+
|
|
38
|
+
**Use when:**
|
|
39
|
+
- Quick utilities and scripts
|
|
40
|
+
- Single-purpose tools
|
|
41
|
+
- Learning StreamWeaver
|
|
42
|
+
- Prototyping
|
|
43
|
+
|
|
44
|
+
**Characteristics:**
|
|
45
|
+
- Each script runs its own Sinatra server
|
|
46
|
+
- Auto-detects free port (4567, 4568, etc.)
|
|
47
|
+
- Auto-opens browser
|
|
48
|
+
- Self-contained - no dependencies on service
|
|
49
|
+
|
|
50
|
+
### 2. Service Mode (`streamweaver <file>`)
|
|
51
|
+
|
|
52
|
+
A single background service renders multiple apps, each at a unique URL:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Start service (auto-starts if not running)
|
|
56
|
+
streamweaver examples/basic/hello_world.rb
|
|
57
|
+
|
|
58
|
+
# Run another app (same service, new URL)
|
|
59
|
+
streamweaver examples/basic/todo_list.rb
|
|
60
|
+
|
|
61
|
+
# List all loaded apps
|
|
62
|
+
streamweaver list
|
|
63
|
+
|
|
64
|
+
# Admin dashboard
|
|
65
|
+
streamweaver admin
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Use when:**
|
|
69
|
+
- Running multiple apps side-by-side
|
|
70
|
+
- Tutorial and examples browser (loads playground apps)
|
|
71
|
+
- Comparing different implementations
|
|
72
|
+
- Long-running development sessions
|
|
73
|
+
|
|
74
|
+
**Characteristics:**
|
|
75
|
+
- Single server process on port 4567
|
|
76
|
+
- Apps at human-readable `/apps/:slug` URLs (e.g. `/apps/sales-dashboard`), derived from the app's declared name
|
|
77
|
+
- Canonical `/apps/:app_id` hex URL always still resolves as a fallback
|
|
78
|
+
- Human-readable aliases like `/tutorial/philosophy`
|
|
79
|
+
- Hot-reload: change file, re-run command
|
|
80
|
+
- Persistent until `streamweaver stop`
|
|
81
|
+
|
|
82
|
+
## CLI Commands
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---------|-------------|
|
|
86
|
+
| `streamweaver <file.rb>` | Run app (auto-starts service) |
|
|
87
|
+
| `streamweaver run --name "My App" <file>` | Run with custom name |
|
|
88
|
+
| `streamweaver list` | List all loaded apps |
|
|
89
|
+
| `streamweaver remove <app_id>` | Remove specific app |
|
|
90
|
+
| `streamweaver clear` | Remove all apps |
|
|
91
|
+
| `streamweaver admin` | Open admin dashboard |
|
|
92
|
+
| `streamweaver showcase` | Open examples browser |
|
|
93
|
+
| `streamweaver tutorial` | Open interactive tutorial |
|
|
94
|
+
| `streamweaver status` | Show service status |
|
|
95
|
+
| `streamweaver stop` | Stop background service |
|
|
96
|
+
| `streamweaver llm` | Output LLM documentation |
|
|
97
|
+
|
|
98
|
+
## Service Architecture
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
102
|
+
│ StreamWeaver Service │
|
|
103
|
+
│ (port 4567) │
|
|
104
|
+
├─────────────────────────────────────────────────────────────┤
|
|
105
|
+
│ /apps/hello-world (a1b2c3d4) → hello_world.rb │
|
|
106
|
+
│ /apps/todo-list (e5f6g7h8) → todo_list.rb │
|
|
107
|
+
│ /apps/form-demo (i9j0k1l2) → form_demo.rb │
|
|
108
|
+
│ │
|
|
109
|
+
│ Aliases: │
|
|
110
|
+
│ /tutorial/philosophy → /apps/m3n4o5p6 │
|
|
111
|
+
│ /examples/hello_world → /apps/q7r8s9t0 │
|
|
112
|
+
├─────────────────────────────────────────────────────────────┤
|
|
113
|
+
│ Admin Dashboard: /admin │
|
|
114
|
+
└─────────────────────────────────────────────────────────────┘
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### How Apps Are Loaded
|
|
118
|
+
|
|
119
|
+
1. **CLI calls `/load-app`** with file path and optional name/source
|
|
120
|
+
2. **Service evaluates** the Ruby file in isolation
|
|
121
|
+
3. **App gets a unique hex ID** (8-char hash) and a human-readable slug derived
|
|
122
|
+
from its declared name (falling back to the filename); colliding slugs
|
|
123
|
+
from different files get a numeric suffix (`-2`, `-3`, ...); re-loading the
|
|
124
|
+
same file reuses its existing slug
|
|
125
|
+
4. **Browser opens** to `/apps/:slug` (or `/apps/:app_id` / an aliased URL)
|
|
126
|
+
|
|
127
|
+
### Aliased URLs
|
|
128
|
+
|
|
129
|
+
Apps can have human-readable URLs:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# Internal: loads app with source="tutorial", name="tutorial/philosophy"
|
|
133
|
+
load_app_via_service(temp_file, source: "tutorial", name: "tutorial/philosophy")
|
|
134
|
+
|
|
135
|
+
# Results in alias: /tutorial/philosophy → /apps/a1b2c3d4
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Tutorial Integration
|
|
139
|
+
|
|
140
|
+
The interactive tutorial (`streamweaver tutorial`) demonstrates service integration:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
┌────────────────────────────────────────────────────────────┐
|
|
144
|
+
│ Tutorial App (standalone on port 4567) │
|
|
145
|
+
├────────────────────────────────────────────────────────────┤
|
|
146
|
+
│ ┌──────────────────┬─────────────────────────────────────┐│
|
|
147
|
+
│ │ Navigation │ Code Editor ││
|
|
148
|
+
│ │ - Philosophy │ ┌─────────────────────────────────┐││
|
|
149
|
+
│ │ - Hello World │ │ header1 "Welcome!" │││
|
|
150
|
+
│ │ - Getting Input │ │ text_field :name │││
|
|
151
|
+
│ │ - ... │ │ ... │││
|
|
152
|
+
│ │ │ └─────────────────────────────────┘││
|
|
153
|
+
│ │ │ [Check] [Reset] [Run] [Open] [Stop]││
|
|
154
|
+
│ └──────────────────┴─────────────────────────────────────┘│
|
|
155
|
+
└────────────────────────────────────────────────────────────┘
|
|
156
|
+
│
|
|
157
|
+
│ Click "Run" → Loads code into service
|
|
158
|
+
▼
|
|
159
|
+
┌────────────────────────────────────────────────────────────┐
|
|
160
|
+
│ StreamWeaver Service (port 4567) │
|
|
161
|
+
├────────────────────────────────────────────────────────────┤
|
|
162
|
+
│ /tutorial/philosophy → User's edited code running │
|
|
163
|
+
│ /tutorial/hello_world → Another lesson's code │
|
|
164
|
+
└────────────────────────────────────────────────────────────┘
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Flow:**
|
|
168
|
+
1. User edits code in tutorial's CodeMirror editor
|
|
169
|
+
2. Clicks "Run" → Tutorial writes temp file, calls service `/load-app`
|
|
170
|
+
3. Service loads app, returns URL
|
|
171
|
+
4. Tutorial opens browser to `/tutorial/philosophy`
|
|
172
|
+
5. User sees their code running live
|
|
173
|
+
6. Click "Stop" to unload from service
|
|
174
|
+
|
|
175
|
+
## Examples Browser Integration
|
|
176
|
+
|
|
177
|
+
The examples browser (`streamweaver showcase`) works similarly:
|
|
178
|
+
|
|
179
|
+
- Displays categorized examples with syntax-highlighted previews
|
|
180
|
+
- "Run" loads example into service
|
|
181
|
+
- "Stop" unloads from service
|
|
182
|
+
- Multiple examples can run simultaneously
|
|
183
|
+
|
|
184
|
+
## Use Cases
|
|
185
|
+
|
|
186
|
+
### Quick One-Off Script
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Just run it directly
|
|
190
|
+
ruby my_tool.rb
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Development Session
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Start working on an app
|
|
197
|
+
streamweaver my_app.rb
|
|
198
|
+
|
|
199
|
+
# Edit code, re-run to hot-reload
|
|
200
|
+
streamweaver my_app.rb
|
|
201
|
+
|
|
202
|
+
# Check what's loaded
|
|
203
|
+
streamweaver list
|
|
204
|
+
|
|
205
|
+
# Clean up when done
|
|
206
|
+
streamweaver stop
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Learning StreamWeaver
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Interactive tutorial
|
|
213
|
+
streamweaver tutorial
|
|
214
|
+
|
|
215
|
+
# Browse and run examples
|
|
216
|
+
streamweaver showcase
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Comparing Implementations
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Load multiple versions
|
|
223
|
+
streamweaver v1/dashboard.rb
|
|
224
|
+
streamweaver v2/dashboard.rb
|
|
225
|
+
|
|
226
|
+
# Open both in browser tabs
|
|
227
|
+
streamweaver list # Shows URLs for both
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Session State
|
|
231
|
+
|
|
232
|
+
**Important:** Each app has isolated session state.
|
|
233
|
+
|
|
234
|
+
- Standalone scripts: Session in browser cookie per port
|
|
235
|
+
- Service mode: Session per app_id (apps don't share state)
|
|
236
|
+
- Tutorial/Examples: Playground apps are independent of the host app
|
|
237
|
+
|
|
238
|
+
## Agentic Mode with Service
|
|
239
|
+
|
|
240
|
+
For AI agent workflows, standalone mode is typically simpler:
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
# Agent runs this, waits for response
|
|
244
|
+
result = app "Quick Survey" do
|
|
245
|
+
text_field :feedback
|
|
246
|
+
select :rating, ["1", "2", "3", "4", "5"]
|
|
247
|
+
end.run_once!
|
|
248
|
+
|
|
249
|
+
puts result.inspect
|
|
250
|
+
# => { "feedback" => "Great!", "rating" => "5" }
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Service mode can be used for persistent agent UIs that need to run alongside other apps.
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
### Service won't start
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Check if already running
|
|
261
|
+
streamweaver status
|
|
262
|
+
|
|
263
|
+
# Stop and restart
|
|
264
|
+
streamweaver stop
|
|
265
|
+
streamweaver serve # Foreground mode for debugging
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### App not updating after code change
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Re-run to hot-reload
|
|
272
|
+
streamweaver my_app.rb
|
|
273
|
+
|
|
274
|
+
# Or remove and re-add
|
|
275
|
+
streamweaver remove <app_id>
|
|
276
|
+
streamweaver my_app.rb
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Port conflict
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Service uses 4575 by default
|
|
283
|
+
# Standalone scripts auto-detect free ports starting at 4567
|
|
284
|
+
# If 4575 is in use, stop other service or change port:
|
|
285
|
+
streamweaver serve -p 4576
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Session state persists unexpectedly
|
|
289
|
+
|
|
290
|
+
Browser cookies persist across server restarts. To reset:
|
|
291
|
+
1. Clear browser cookies for localhost
|
|
292
|
+
2. Use incognito mode
|
|
293
|
+
3. Add reset button: `button "Reset" do |s| s.clear end`
|
|
294
|
+
|
|
295
|
+
## Future: Claude Code Integration
|
|
296
|
+
|
|
297
|
+
*Documentation for Claude Code integration patterns coming soon.*
|
|
298
|
+
|
|
299
|
+
Key areas:
|
|
300
|
+
- Using `run_once!` for form collection
|
|
301
|
+
- Building persistent agent UIs via service mode
|
|
302
|
+
- Patterns for multi-step agent workflows
|