@proflandrigan/shards 1.1.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.
- package/README.md +475 -0
- package/package.json +37 -0
- package/src/agents/academic.md +276 -0
- package/src/agents/ai-engineer.md +377 -0
- package/src/agents/analytics-engineer.md +364 -0
- package/src/agents/applied-ml-scientist.md +410 -0
- package/src/agents/backend-engineer.md +255 -0
- package/src/agents/bi-engineer.md +333 -0
- package/src/agents/data-analyst.md +343 -0
- package/src/agents/data-engineer.md +260 -0
- package/src/agents/data-modeller.md +386 -0
- package/src/agents/data-scientist.md +366 -0
- package/src/agents/deep-learning-engineer.md +389 -0
- package/src/agents/ml-engineer.md +424 -0
- package/src/agents/mlops-engineer.md +339 -0
- package/src/agents/researcher.md +187 -0
- package/src/agents/specific_instructions/academic/critical_review.md +263 -0
- package/src/agents/specific_instructions/academic/report.md +113 -0
- package/src/agents/specific_instructions/ai_engineer/advise.md +162 -0
- package/src/agents/specific_instructions/ai_engineer/bi_engineer_handoff.md +86 -0
- package/src/agents/specific_instructions/ai_engineer/experiment.md +471 -0
- package/src/agents/specific_instructions/ai_engineer/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/ai_engineer/phases/index.md +45 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-1.md +55 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-2.md +86 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-3.md +96 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-4.md +138 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-5.md +157 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-6.md +196 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-7.md +313 -0
- package/src/agents/specific_instructions/ai_engineer/phases.md +1011 -0
- package/src/agents/specific_instructions/ai_engineer/prompt_lab.md +161 -0
- package/src/agents/specific_instructions/ai_engineer/prompt_lab_ui_mode.md +28 -0
- package/src/agents/specific_instructions/ai_engineer/research.md +393 -0
- package/src/agents/specific_instructions/ai_engineer/research_ui_mode.md +66 -0
- package/src/agents/specific_instructions/ai_engineer/review.md +159 -0
- package/src/agents/specific_instructions/ai_engineer/validation_checklist.md +182 -0
- package/src/agents/specific_instructions/analytics_engineer/advise.md +155 -0
- package/src/agents/specific_instructions/analytics_engineer/bi_engineer_handoff.md +91 -0
- package/src/agents/specific_instructions/analytics_engineer/data_analyst_handoff.md +84 -0
- package/src/agents/specific_instructions/analytics_engineer/deep_phases.md +818 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/index.md +24 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-1.md +77 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-2.md +106 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-3.md +93 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-4.md +79 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-5.md +61 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-6.md +45 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-7.md +235 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-8.md +221 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-1.md +47 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-2.md +78 -0
- package/src/agents/specific_instructions/analytics_engineer/quick_phases.md +112 -0
- package/src/agents/specific_instructions/analytics_engineer/review.md +167 -0
- package/src/agents/specific_instructions/analytics_engineer/service_mode.md +369 -0
- package/src/agents/specific_instructions/analytics_engineer/ui_mode.md +45 -0
- package/src/agents/specific_instructions/analytics_engineer/update.md +162 -0
- package/src/agents/specific_instructions/analytics_engineer/validation_checklist.md +121 -0
- package/src/agents/specific_instructions/applied_ml_scientist/advise.md +143 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/index.md +21 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-1.md +51 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-2.md +66 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-3.md +113 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-4.md +104 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-5.md +156 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases.md +428 -0
- package/src/agents/specific_instructions/applied_ml_scientist/research.md +379 -0
- package/src/agents/specific_instructions/applied_ml_scientist/review.md +142 -0
- package/src/agents/specific_instructions/applied_ml_scientist/validation_checklist.md +136 -0
- package/src/agents/specific_instructions/backend_engineer/clean.md +149 -0
- package/src/agents/specific_instructions/backend_engineer/review.md +91 -0
- package/src/agents/specific_instructions/backend_engineer/review_checklist.md +54 -0
- package/src/agents/specific_instructions/backend_engineer/service_mode.md +67 -0
- package/src/agents/specific_instructions/bi_engineer/advise.md +137 -0
- package/src/agents/specific_instructions/bi_engineer/data_analyst_handoff.md +77 -0
- package/src/agents/specific_instructions/bi_engineer/incoming_handoff.md +45 -0
- package/src/agents/specific_instructions/bi_engineer/phases/index.md +20 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-1.md +164 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-2.md +92 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-3.md +121 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-4.md +106 -0
- package/src/agents/specific_instructions/bi_engineer/phases.md +451 -0
- package/src/agents/specific_instructions/bi_engineer/review.md +166 -0
- package/src/agents/specific_instructions/bi_engineer/update.md +147 -0
- package/src/agents/specific_instructions/bi_engineer/validation_checklist.md +124 -0
- package/src/agents/specific_instructions/data_analyst/advise.md +138 -0
- package/src/agents/specific_instructions/data_analyst/explain.md +221 -0
- package/src/agents/specific_instructions/data_analyst/incoming_handoff.md +40 -0
- package/src/agents/specific_instructions/data_analyst/phases/index.md +20 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-1.md +159 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-2.md +112 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-3.md +265 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-4.md +100 -0
- package/src/agents/specific_instructions/data_analyst/phases.md +501 -0
- package/src/agents/specific_instructions/data_analyst/review.md +138 -0
- package/src/agents/specific_instructions/data_analyst/ui_mode.md +26 -0
- package/src/agents/specific_instructions/data_analyst/update.md +144 -0
- package/src/agents/specific_instructions/data_analyst/validation_checklist.md +95 -0
- package/src/agents/specific_instructions/data_engineer/advise.md +137 -0
- package/src/agents/specific_instructions/data_engineer/phases.md +466 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/index.md +23 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-1.md +49 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-2.md +93 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-3.md +55 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-4.md +48 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-5.md +40 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-6.md +102 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-7.md +87 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/phase-1.md +45 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/phase-2.md +54 -0
- package/src/agents/specific_instructions/data_engineer/review.md +135 -0
- package/src/agents/specific_instructions/data_engineer/validation_checklist.md +136 -0
- package/src/agents/specific_instructions/data_modeller/advise.md +137 -0
- package/src/agents/specific_instructions/data_modeller/phases.md +581 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/index.md +23 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-1.md +52 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-2.md +113 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-3.md +47 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-4.md +51 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-5.md +45 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-6.md +105 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-7.md +136 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/phase-1.md +47 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/phase-2.md +65 -0
- package/src/agents/specific_instructions/data_modeller/review.md +141 -0
- package/src/agents/specific_instructions/data_modeller/service_mode.md +218 -0
- package/src/agents/specific_instructions/data_modeller/validation_checklist.md +125 -0
- package/src/agents/specific_instructions/data_scientist/advise.md +158 -0
- package/src/agents/specific_instructions/data_scientist/bi_engineer_handoff.md +63 -0
- package/src/agents/specific_instructions/data_scientist/experiment.md +482 -0
- package/src/agents/specific_instructions/data_scientist/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/data_scientist/explain.md +247 -0
- package/src/agents/specific_instructions/data_scientist/greenfield_data.md +35 -0
- package/src/agents/specific_instructions/data_scientist/ml_engineer_handoff.md +52 -0
- package/src/agents/specific_instructions/data_scientist/notebook_walkthrough.md +76 -0
- package/src/agents/specific_instructions/data_scientist/phases/index.md +24 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-1.md +45 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-2.md +67 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-3.md +89 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-4.md +143 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-5.md +71 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-6.md +239 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-7.md +207 -0
- package/src/agents/specific_instructions/data_scientist/phases.md +651 -0
- package/src/agents/specific_instructions/data_scientist/research.md +345 -0
- package/src/agents/specific_instructions/data_scientist/research_ui_mode.md +52 -0
- package/src/agents/specific_instructions/data_scientist/review.md +136 -0
- package/src/agents/specific_instructions/data_scientist/service_mode.md +247 -0
- package/src/agents/specific_instructions/data_scientist/validation_checklist.md +183 -0
- package/src/agents/specific_instructions/deep_learning_engineer/advise.md +145 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/index.md +21 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-1.md +74 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-2.md +98 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-3.md +76 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-4.md +128 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-5.md +292 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases.md +567 -0
- package/src/agents/specific_instructions/deep_learning_engineer/research.md +389 -0
- package/src/agents/specific_instructions/deep_learning_engineer/review.md +155 -0
- package/src/agents/specific_instructions/deep_learning_engineer/validation_checklist.md +147 -0
- package/src/agents/specific_instructions/ml_engineer/advise.md +174 -0
- package/src/agents/specific_instructions/ml_engineer/bi_engineer_handoff.md +71 -0
- package/src/agents/specific_instructions/ml_engineer/experiment.md +474 -0
- package/src/agents/specific_instructions/ml_engineer/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/ml_engineer/notebook_walkthrough.md +75 -0
- package/src/agents/specific_instructions/ml_engineer/phases/index.md +25 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-1.md +49 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-2.md +75 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-3.md +124 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-4.md +279 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-5.md +160 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-6-5.md +170 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-6.md +295 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-7.md +337 -0
- package/src/agents/specific_instructions/ml_engineer/phases.md +1068 -0
- package/src/agents/specific_instructions/ml_engineer/research.md +437 -0
- package/src/agents/specific_instructions/ml_engineer/research_ui_mode.md +71 -0
- package/src/agents/specific_instructions/ml_engineer/review.md +187 -0
- package/src/agents/specific_instructions/ml_engineer/service_mode.md +273 -0
- package/src/agents/specific_instructions/ml_engineer/validation_checklist.md +185 -0
- package/src/agents/specific_instructions/mlops_engineer/advise.md +139 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/index.md +23 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-1.md +52 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-2.md +86 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-3.md +105 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-4.md +128 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-5.md +106 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-6.md +128 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-7.md +144 -0
- package/src/agents/specific_instructions/mlops_engineer/phases.md +671 -0
- package/src/agents/specific_instructions/mlops_engineer/review.md +164 -0
- package/src/agents/specific_instructions/mlops_engineer/service_mode.md +81 -0
- package/src/agents/specific_instructions/mlops_engineer/validation_checklist.md +151 -0
- package/src/agents/specific_instructions/researcher/critical_review.md +292 -0
- package/src/agents/specific_instructions/researcher/review_checklist.md +67 -0
- package/src/agents/specific_instructions/researcher/service_mode.md +224 -0
- package/src/agents/specific_instructions/shared/auto_verify_mode.md +141 -0
- package/src/agents/specific_instructions/shared/autonomous_research.md +1289 -0
- package/src/agents/specific_instructions/shared/behavioral_rules.md +36 -0
- package/src/agents/specific_instructions/shared/diverge_protocol.md +387 -0
- package/src/agents/specific_instructions/shared/engineering_guidelines.md +136 -0
- package/src/agents/specific_instructions/shared/experiment_versioning.md +184 -0
- package/src/agents/specific_instructions/shared/goal_mode.md +187 -0
- package/src/agents/specific_instructions/shared/incremental_testing.md +139 -0
- package/src/agents/specific_instructions/shared/intent_discovery.md +223 -0
- package/src/agents/specific_instructions/shared/join_path_protocol.md +168 -0
- package/src/agents/specific_instructions/shared/knowledge_checkpoint.md +83 -0
- package/src/agents/specific_instructions/shared/knowledge_harvest.md +220 -0
- package/src/agents/specific_instructions/shared/knowledge_retrieval.md +100 -0
- package/src/agents/specific_instructions/shared/notebook_walkthrough_protocol.md +367 -0
- package/src/agents/specific_instructions/shared/reviewer_verdict_protocol.md +74 -0
- package/src/agents/specific_instructions/shared/swarm_protocol.md +97 -0
- package/src/agents/specific_instructions/shared/validation_protocol.md +139 -0
- package/src/agents/specific_instructions/syn/arbiter.md +140 -0
- package/src/agents/specific_instructions/syn/brainstorm.md +550 -0
- package/src/agents/specific_instructions/syn/code_review.md +232 -0
- package/src/agents/specific_instructions/syn/diff.md +239 -0
- package/src/agents/specific_instructions/syn/final_review.md +65 -0
- package/src/agents/specific_instructions/syn/fixer.md +240 -0
- package/src/agents/specific_instructions/syn/free_form.md +130 -0
- package/src/agents/specific_instructions/syn/knowledge.md +468 -0
- package/src/agents/specific_instructions/syn/notebook_walkthrough.md +78 -0
- package/src/agents/specific_instructions/syn/panel_review.md +634 -0
- package/src/agents/specific_instructions/syn/pm.md +453 -0
- package/src/agents/specific_instructions/syn/pr_review.md +255 -0
- package/src/agents/specific_instructions/syn/slides.md +417 -0
- package/src/agents/syn.md +729 -0
- package/src/commands/academic.md +41 -0
- package/src/commands/ai-engineer.md +45 -0
- package/src/commands/analytics-engineer.md +48 -0
- package/src/commands/applied-ml-scientist.md +45 -0
- package/src/commands/backend-engineer.md +35 -0
- package/src/commands/bi-engineer.md +40 -0
- package/src/commands/brainstorm.md +24 -0
- package/src/commands/data-analyst.md +38 -0
- package/src/commands/data-engineer.md +37 -0
- package/src/commands/data-modeller.md +38 -0
- package/src/commands/data-scientist.md +38 -0
- package/src/commands/deep-learning-engineer.md +47 -0
- package/src/commands/end.md +49 -0
- package/src/commands/knowledge.md +24 -0
- package/src/commands/ml-engineer.md +42 -0
- package/src/commands/mlops-engineer.md +47 -0
- package/src/commands/notebook-walkthrough.md +58 -0
- package/src/commands/researcher.md +40 -0
- package/src/commands/resume.md +57 -0
- package/src/commands/review-pr.md +26 -0
- package/src/commands/shards-guide.md +41 -0
- package/src/commands/shards-ui.md +32 -0
- package/src/commands/shards.md +41 -0
- package/src/docs/01-getting-started/concepts.md +109 -0
- package/src/docs/01-getting-started/first-session.md +79 -0
- package/src/docs/01-getting-started/install.md +61 -0
- package/src/docs/02-agents/academic.md +71 -0
- package/src/docs/02-agents/ai-engineer.md +78 -0
- package/src/docs/02-agents/analytics-engineer.md +58 -0
- package/src/docs/02-agents/applied-ml-scientist.md +59 -0
- package/src/docs/02-agents/backend-engineer.md +58 -0
- package/src/docs/02-agents/bi-engineer.md +65 -0
- package/src/docs/02-agents/data-analyst.md +67 -0
- package/src/docs/02-agents/data-engineer.md +57 -0
- package/src/docs/02-agents/data-modeller.md +51 -0
- package/src/docs/02-agents/data-scientist.md +78 -0
- package/src/docs/02-agents/deep-learning-engineer.md +64 -0
- package/src/docs/02-agents/ml-engineer.md +80 -0
- package/src/docs/02-agents/mlops-engineer.md +59 -0
- package/src/docs/02-agents/overview.md +62 -0
- package/src/docs/02-agents/researcher.md +73 -0
- package/src/docs/02-agents/syn.md +88 -0
- package/src/docs/03-protocols/auto-verify.md +82 -0
- package/src/docs/03-protocols/autonomous-research.md +59 -0
- package/src/docs/03-protocols/behavioral-rules.md +35 -0
- package/src/docs/03-protocols/diverge.md +50 -0
- package/src/docs/03-protocols/engineering-guidelines.md +56 -0
- package/src/docs/03-protocols/experiment-versioning.md +38 -0
- package/src/docs/03-protocols/gate-pattern.md +65 -0
- package/src/docs/03-protocols/incremental-testing.md +68 -0
- package/src/docs/03-protocols/join-path.md +46 -0
- package/src/docs/03-protocols/knowledge-ledger.md +70 -0
- package/src/docs/03-protocols/reviewer-verdicts.md +39 -0
- package/src/docs/03-protocols/swarm.md +40 -0
- package/src/docs/03-protocols/validation.md +174 -0
- package/src/docs/04-ui/activity-bar.md +70 -0
- package/src/docs/04-ui/chat-pane.md +80 -0
- package/src/docs/04-ui/code-intel.md +62 -0
- package/src/docs/04-ui/file-editing.md +61 -0
- package/src/docs/04-ui/git.md +54 -0
- package/src/docs/04-ui/keybindings.md +79 -0
- package/src/docs/04-ui/knowledge-map.md +76 -0
- package/src/docs/04-ui/overview.md +93 -0
- package/src/docs/04-ui/panels.md +49 -0
- package/src/docs/04-ui/pinboard-selection.md +66 -0
- package/src/docs/04-ui/quick-open-palette.md +56 -0
- package/src/docs/04-ui/sessions.md +81 -0
- package/src/docs/04-ui/settings-permissions.md +56 -0
- package/src/docs/05-commands/reference.md +59 -0
- package/src/docs/06-outputs/directory-map.md +116 -0
- package/src/docs/07-workflows/ai-eval-first.md +57 -0
- package/src/docs/07-workflows/deep-study-to-production.md +76 -0
- package/src/docs/07-workflows/diverge-exploration.md +77 -0
- package/src/docs/07-workflows/quick-analysis.md +45 -0
- package/src/docs/08-integrations/claude-code-auto-mode.md +191 -0
- package/src/docs/08-integrations/google-slides.md +175 -0
- package/src/docs/README.md +30 -0
- package/src/docs/manifest.json +108 -0
- package/src/templates/analysis-template.md +20 -0
- package/src/templates/branch-report.md +46 -0
- package/src/templates/diff-report.md +88 -0
- package/src/templates/knowledge-index.md +7 -0
- package/src/templates/model-card-schema.json +186 -0
- package/src/templates/model-card-schema.md +88 -0
- package/src/templates/model-card.md +124 -0
- package/src/templates/project-plan.md +47 -0
- package/src/templates/project-specs.md +81 -0
- package/src/templates/report-template.md +43 -0
- package/src/templates/study-template.md +25 -0
- package/src/ui/cc-readonly.js +181 -0
- package/src/ui/chat-session.js +466 -0
- package/src/ui/css/base.css +136 -0
- package/src/ui/css/brainstorm.css +525 -0
- package/src/ui/css/chat.css +1405 -0
- package/src/ui/css/editor.css +546 -0
- package/src/ui/css/eval-dashboard.css +157 -0
- package/src/ui/css/experiment.css +237 -0
- package/src/ui/css/guide.css +186 -0
- package/src/ui/css/knowledge-map.css +383 -0
- package/src/ui/css/layout.css +431 -0
- package/src/ui/css/model-card.css +161 -0
- package/src/ui/css/notebook-walkthrough.css +271 -0
- package/src/ui/css/pr-review.css +403 -0
- package/src/ui/css/prompt-lab.css +325 -0
- package/src/ui/css/sessions.css +258 -0
- package/src/ui/css/sidebar.css +661 -0
- package/src/ui/css/terminal.css +113 -0
- package/src/ui/css/theme-light.css +542 -0
- package/src/ui/index.html +389 -0
- package/src/ui/js/agents.js +32 -0
- package/src/ui/js/bookmarks.js +230 -0
- package/src/ui/js/chat.js +1776 -0
- package/src/ui/js/code-intel.js +328 -0
- package/src/ui/js/command-palette.js +142 -0
- package/src/ui/js/events.js +591 -0
- package/src/ui/js/explorer.js +317 -0
- package/src/ui/js/file-view.js +477 -0
- package/src/ui/js/git.js +536 -0
- package/src/ui/js/guide.js +198 -0
- package/src/ui/js/hud.js +75 -0
- package/src/ui/js/init.js +351 -0
- package/src/ui/js/knowledge-map.js +906 -0
- package/src/ui/js/markdown.js +114 -0
- package/src/ui/js/monaco.js +164 -0
- package/src/ui/js/notebook-walkthrough.js +272 -0
- package/src/ui/js/notebook.js +448 -0
- package/src/ui/js/panels.js +2681 -0
- package/src/ui/js/pinboard.js +186 -0
- package/src/ui/js/quick-open.js +164 -0
- package/src/ui/js/selection-context.js +131 -0
- package/src/ui/js/sessions.js +256 -0
- package/src/ui/js/settings.js +476 -0
- package/src/ui/js/split-view.js +82 -0
- package/src/ui/js/state.js +343 -0
- package/src/ui/js/table.js +161 -0
- package/src/ui/js/tabs.js +284 -0
- package/src/ui/js/tabular.js +125 -0
- package/src/ui/js/terminal.js +354 -0
- package/src/ui/js/timeline.js +137 -0
- package/src/ui/js/utils.js +293 -0
- package/src/ui/notebook-kernel.py +790 -0
- package/src/ui/open-browser.js +55 -0
- package/src/ui/permission-pattern.js +42 -0
- package/src/ui/relay.js +513 -0
- package/src/ui/server.js +3072 -0
- package/src/ui/session-index.js +225 -0
- package/src/ui/shards_icon.png +0 -0
- package/src/ui/spawn-server.js +41 -0
- package/src/ui/symbol-index.js +813 -0
- package/src/ui/ui-push.js +177 -0
- package/tools/gate-hook/VALIDATION_SPEC.md +273 -0
- package/tools/gate-hook/__tests__/auto-verify.test.js +343 -0
- package/tools/gate-hook/auto-allowlist.js +179 -0
- package/tools/gate-hook/auto-state.js +68 -0
- package/tools/gate-hook/classify.js +21 -0
- package/tools/gate-hook/log.js +57 -0
- package/tools/gate-hook/parser.js +205 -0
- package/tools/gate-hook/sql-guard.js +230 -0
- package/tools/gate-hook/state.js +170 -0
- package/tools/gate-hook/sweep.js +139 -0
- package/tools/gate-hook/transcript.js +45 -0
- package/tools/gate-hook/validation.js +321 -0
- package/tools/gate-hook.js +475 -0
- package/tools/install.js +914 -0
- package/tools/shards-gates.js +311 -0
- package/tools/shards-sessions.js +261 -0
- package/tools/shards-ui.js +377 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { exec } = require('child_process');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const SHARDS_DIR = path.join(process.cwd(), '.shards');
|
|
10
|
+
const PORT_FILE = path.join(SHARDS_DIR, 'ui.port');
|
|
11
|
+
|
|
12
|
+
function getPort() {
|
|
13
|
+
try {
|
|
14
|
+
const raw = fs.readFileSync(PORT_FILE, 'utf8').trim();
|
|
15
|
+
// Support both JSON format (new) and plain number (old)
|
|
16
|
+
try {
|
|
17
|
+
const info = JSON.parse(raw);
|
|
18
|
+
return info.port;
|
|
19
|
+
} catch {
|
|
20
|
+
return parseInt(raw, 10);
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
return 7842;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const port = getPort();
|
|
28
|
+
|
|
29
|
+
// Optional --hash argument: /shards-guide passes --hash guide so the UI
|
|
30
|
+
// opens the guide panel immediately on load.
|
|
31
|
+
let hash = '';
|
|
32
|
+
const args = process.argv.slice(2);
|
|
33
|
+
for (let i = 0; i < args.length; i++) {
|
|
34
|
+
if (args[i] === '--hash' && args[i + 1]) { hash = args[i + 1]; i++; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const url = `http://localhost:${port}${hash ? '#' + hash : ''}`;
|
|
38
|
+
|
|
39
|
+
console.log(`Shards UI: ${url}`);
|
|
40
|
+
|
|
41
|
+
const platform = process.platform;
|
|
42
|
+
let command;
|
|
43
|
+
if (platform === 'darwin') {
|
|
44
|
+
command = `open "${url}"`;
|
|
45
|
+
} else if (platform === 'win32') {
|
|
46
|
+
command = `start "" "${url}"`;
|
|
47
|
+
} else {
|
|
48
|
+
command = `xdg-open "${url}"`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
exec(command, (err) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
console.log(`Could not auto-open browser. Visit: ${url}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// "Launcher" commands where the first argument is a subcommand that meaningfully
|
|
4
|
+
// narrows the permission's blast radius. Clicking Always Allow on `git status`
|
|
5
|
+
// should persist Bash(git status:*) — not Bash(git:*) — so the decision doesn't
|
|
6
|
+
// also authorize `git push` or `git reset --hard`.
|
|
7
|
+
const LAUNCHER_COMMANDS = new Set([
|
|
8
|
+
'git', 'dbt', 'npm', 'pnpm', 'yarn',
|
|
9
|
+
'pip', 'pip3', 'uv', 'poetry',
|
|
10
|
+
'docker', 'kubectl', 'helm',
|
|
11
|
+
'gh', 'aws', 'gcloud',
|
|
12
|
+
'cargo', 'go', 'mvn', 'gradle',
|
|
13
|
+
'make', 'just',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
// Build the Bash permission pattern persisted when the user clicks Always Allow
|
|
17
|
+
// (or Always Deny) on a permission card. Returns a glob like Bash(cmd:*) so
|
|
18
|
+
// future invocations with different arguments don't re-prompt.
|
|
19
|
+
//
|
|
20
|
+
// Heuristic:
|
|
21
|
+
// - Single-token commands ("python3 foo.py") → Bash(python3:*)
|
|
22
|
+
// - Launcher + subcommand ("git status --short") → Bash(git status:*)
|
|
23
|
+
// - Absolute/relative path commands are preserved as-is in the prefix so that
|
|
24
|
+
// Bash(/usr/local/bin/dbt run:*) still disambiguates from a system dbt.
|
|
25
|
+
// - Empty / whitespace-only input falls back to the literal command, matching
|
|
26
|
+
// the pre-fix behavior so we don't silently broaden scope on garbage input.
|
|
27
|
+
function permissionPattern(command) {
|
|
28
|
+
if (typeof command !== 'string') return `Bash(${command})`;
|
|
29
|
+
const tokens = command.trim().split(/\s+/).filter(Boolean);
|
|
30
|
+
if (tokens.length === 0) return `Bash(${command})`;
|
|
31
|
+
|
|
32
|
+
const head = tokens[0];
|
|
33
|
+
const lastSlash = head.lastIndexOf('/');
|
|
34
|
+
const bareHead = lastSlash >= 0 ? head.slice(lastSlash + 1) : head;
|
|
35
|
+
|
|
36
|
+
if (LAUNCHER_COMMANDS.has(bareHead) && tokens.length > 1) {
|
|
37
|
+
return `Bash(${head} ${tokens[1]}:*)`;
|
|
38
|
+
}
|
|
39
|
+
return `Bash(${head}:*)`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { permissionPattern, LAUNCHER_COMMANDS };
|
package/src/ui/relay.js
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { randomUUID } = require('crypto');
|
|
9
|
+
|
|
10
|
+
// Read-only allowlist shared with the gate hook. Used to short-circuit the
|
|
11
|
+
// HTTP round-trip to the shards-ui server for obvious read-only Bash ops.
|
|
12
|
+
// Graceful degradation: if the module isn't available (standalone deploy,
|
|
13
|
+
// install partially rolled back, etc.) we fall through to the normal POST.
|
|
14
|
+
let isAutoApprovable;
|
|
15
|
+
try {
|
|
16
|
+
({ isAutoApprovable } = require('../hooks/gate-hook/auto-allowlist.js'));
|
|
17
|
+
} catch (_) {
|
|
18
|
+
isAutoApprovable = () => false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Claude Code's built-in read-only classifier (ls, cat, grep, read-only git
|
|
22
|
+
// forms, read-only tools). Mirrors what the CLI auto-approves in every mode
|
|
23
|
+
// so the UI doesn't surface permission cards for them. Same graceful
|
|
24
|
+
// degradation as above.
|
|
25
|
+
let isCcReadOnly;
|
|
26
|
+
try {
|
|
27
|
+
({ isCcReadOnlyAutoApprovable: isCcReadOnly } = require('./cc-readonly.js'));
|
|
28
|
+
} catch (_) {
|
|
29
|
+
isCcReadOnly = () => false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SHARDS_DIR = path.join(process.cwd(), '.shards');
|
|
33
|
+
const PORT_FILE = path.join(SHARDS_DIR, 'ui.port');
|
|
34
|
+
const STATE_FILE = path.join(SHARDS_DIR, 'ui-state.json');
|
|
35
|
+
const QUEUE_FILE = path.join(SHARDS_DIR, 'event-queue.jsonl');
|
|
36
|
+
|
|
37
|
+
// P2: Queue constraints
|
|
38
|
+
const MAX_QUEUE_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
39
|
+
const MAX_QUEUE_SIZE = 500;
|
|
40
|
+
|
|
41
|
+
const eventType = process.argv[2]; // user-prompt | stop | post-tool-use | session-end
|
|
42
|
+
|
|
43
|
+
// P2: Per-event UUID for server-side dedup. Previously a process-local
|
|
44
|
+
// integer counter — but every Claude Code hook invocation spawns a fresh
|
|
45
|
+
// relay process, so two concurrent hooks both started at seq=1 and the
|
|
46
|
+
// server's dedup set saw collisions. UUIDs are opaque on the server side
|
|
47
|
+
// (used only as Set keys in the dedup check) so this is a drop-in swap.
|
|
48
|
+
function nextSeq() { return randomUUID(); }
|
|
49
|
+
|
|
50
|
+
// P1: Read port and auth token from JSON port file
|
|
51
|
+
function getServerInfo() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(PORT_FILE, 'utf8').trim();
|
|
54
|
+
// Support both old (plain number) and new (JSON) formats
|
|
55
|
+
try {
|
|
56
|
+
const info = JSON.parse(raw);
|
|
57
|
+
return { port: info.port, token: info.token || null };
|
|
58
|
+
} catch {
|
|
59
|
+
return { port: parseInt(raw, 10), token: null };
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getState() {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
69
|
+
} catch {
|
|
70
|
+
return { currentAgent: 'syn', sessionId: null, messageCount: 0 };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function saveState(state) {
|
|
75
|
+
try {
|
|
76
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── P2: Event queue ─────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
function readQueue() {
|
|
83
|
+
try {
|
|
84
|
+
const raw = fs.readFileSync(QUEUE_FILE, 'utf8').trim();
|
|
85
|
+
if (!raw) return [];
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
return raw.split('\n')
|
|
88
|
+
.map(line => { try { return JSON.parse(line); } catch { return null; } })
|
|
89
|
+
.filter(e => e !== null)
|
|
90
|
+
// Discard events older than MAX_QUEUE_AGE_MS
|
|
91
|
+
.filter(e => (now - e.timestamp) < MAX_QUEUE_AGE_MS);
|
|
92
|
+
} catch {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function writeQueue(events) {
|
|
98
|
+
try {
|
|
99
|
+
// Enforce max queue size — keep the newest events
|
|
100
|
+
const trimmed = events.slice(-MAX_QUEUE_SIZE);
|
|
101
|
+
const lines = trimmed.map(e => JSON.stringify(e)).join('\n');
|
|
102
|
+
fs.writeFileSync(QUEUE_FILE, lines ? lines + '\n' : '');
|
|
103
|
+
} catch {}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function appendToQueue(payload) {
|
|
107
|
+
const entry = { seq: nextSeq(), timestamp: Date.now(), payload };
|
|
108
|
+
try {
|
|
109
|
+
fs.appendFileSync(QUEUE_FILE, JSON.stringify(entry) + '\n');
|
|
110
|
+
} catch {}
|
|
111
|
+
return entry;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// P1 + P2: POST event with auth header, expect ack response
|
|
115
|
+
function postEvent(port, token, payload) {
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
if (!port) { resolve(false); return; }
|
|
118
|
+
const body = JSON.stringify(payload);
|
|
119
|
+
const headers = {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
'Content-Length': Buffer.byteLength(body),
|
|
122
|
+
};
|
|
123
|
+
if (token) {
|
|
124
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
125
|
+
}
|
|
126
|
+
const req = http.request({
|
|
127
|
+
hostname: '127.0.0.1',
|
|
128
|
+
port,
|
|
129
|
+
path: '/event',
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers,
|
|
132
|
+
timeout: 5000,
|
|
133
|
+
}, (res) => {
|
|
134
|
+
let data = '';
|
|
135
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
136
|
+
res.on('end', () => {
|
|
137
|
+
if (res.statusCode === 200) {
|
|
138
|
+
try {
|
|
139
|
+
const result = JSON.parse(data);
|
|
140
|
+
resolve(result.ack === true);
|
|
141
|
+
return;
|
|
142
|
+
} catch {}
|
|
143
|
+
}
|
|
144
|
+
resolve(false);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
req.on('error', () => resolve(false));
|
|
148
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
149
|
+
req.write(body);
|
|
150
|
+
req.end();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Pre-tool-use helpers ────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
function sleep(ms) {
|
|
157
|
+
return new Promise(r => setTimeout(r, ms));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function postPermissionRequest(port, token, payload) {
|
|
161
|
+
return new Promise((resolve) => {
|
|
162
|
+
if (!port) { resolve(null); return; }
|
|
163
|
+
const body = JSON.stringify(payload);
|
|
164
|
+
const headers = {
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
'Content-Length': Buffer.byteLength(body),
|
|
167
|
+
};
|
|
168
|
+
if (token) {
|
|
169
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
170
|
+
}
|
|
171
|
+
const req = http.request({
|
|
172
|
+
hostname: '127.0.0.1',
|
|
173
|
+
port,
|
|
174
|
+
path: '/pre-tool-use',
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers,
|
|
177
|
+
timeout: 5000,
|
|
178
|
+
}, (res) => {
|
|
179
|
+
let data = '';
|
|
180
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
181
|
+
res.on('end', () => {
|
|
182
|
+
try {
|
|
183
|
+
resolve(JSON.parse(data));
|
|
184
|
+
} catch {
|
|
185
|
+
resolve(null);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
req.on('error', () => resolve(null));
|
|
190
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
191
|
+
req.write(body);
|
|
192
|
+
req.end();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function pollDecision(port, token, id) {
|
|
197
|
+
return new Promise((resolve) => {
|
|
198
|
+
const headers = {};
|
|
199
|
+
if (token) {
|
|
200
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
201
|
+
}
|
|
202
|
+
const req = http.request({
|
|
203
|
+
hostname: '127.0.0.1',
|
|
204
|
+
port,
|
|
205
|
+
path: `/chat/permission/${id}`,
|
|
206
|
+
method: 'GET',
|
|
207
|
+
headers,
|
|
208
|
+
timeout: 5000,
|
|
209
|
+
}, (res) => {
|
|
210
|
+
let data = '';
|
|
211
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
212
|
+
res.on('end', () => {
|
|
213
|
+
try {
|
|
214
|
+
const result = JSON.parse(data);
|
|
215
|
+
resolve(result.status || 'pending');
|
|
216
|
+
} catch {
|
|
217
|
+
resolve('pending');
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
req.on('error', () => resolve('pending'));
|
|
222
|
+
req.on('timeout', () => { req.destroy(); resolve('pending'); });
|
|
223
|
+
req.end();
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Emit the structured PreToolUse hook output Claude Code expects to override
|
|
228
|
+
// its internal permission gate. Exit 0 for both allow and deny — exit 2 is
|
|
229
|
+
// reserved for hook *failures*, not user denials. Without this shape, a bare
|
|
230
|
+
// exit(0) doesn't actually override acceptEdits mode's internal deny of
|
|
231
|
+
// non-allowlisted Bash commands.
|
|
232
|
+
//
|
|
233
|
+
// updatedInput is an optional object the hook can use to rewrite the tool's
|
|
234
|
+
// parameters before execution (Claude Code PreToolUse contract). We use it to
|
|
235
|
+
// attach dangerouslyDisableSandbox=true on allowed Bash commands — see
|
|
236
|
+
// buildBashAllowInput below for why.
|
|
237
|
+
function emitDecision(decision, reason, updatedInput) {
|
|
238
|
+
const hookSpecificOutput = {
|
|
239
|
+
hookEventName: 'PreToolUse',
|
|
240
|
+
permissionDecision: decision,
|
|
241
|
+
permissionDecisionReason: reason || '',
|
|
242
|
+
};
|
|
243
|
+
if (updatedInput) hookSpecificOutput.updatedInput = updatedInput;
|
|
244
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput }));
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// When the user (or their persisted allowlist) approves a Bash command, attach
|
|
249
|
+
// dangerouslyDisableSandbox=true so the command isn't then silently blocked by
|
|
250
|
+
// the OS-level sandbox (Seatbelt/bubblewrap) — which would force a second
|
|
251
|
+
// permission round-trip that the shards-ui flow doesn't surface cleanly. Scope
|
|
252
|
+
// is per-call: the flag rides along with this single tool invocation only.
|
|
253
|
+
function buildBashAllowInput(toolName, toolInput) {
|
|
254
|
+
if (toolName !== 'Bash') return null;
|
|
255
|
+
if (!toolInput || typeof toolInput !== 'object') return null;
|
|
256
|
+
if (toolInput.dangerouslyDisableSandbox === true) return null; // already set
|
|
257
|
+
return { ...toolInput, dangerouslyDisableSandbox: true };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function handlePreToolUse(port, token, payload) {
|
|
261
|
+
const toolName = payload.tool_name || '';
|
|
262
|
+
const toolInput = payload.tool_input || {};
|
|
263
|
+
const command = toolInput.command || '';
|
|
264
|
+
const sessionId = payload.session_id || '';
|
|
265
|
+
|
|
266
|
+
// Fast path: read-only inspection ops auto-approve without an HTTP round-trip.
|
|
267
|
+
// The shards-ui server's settings allowlist would also approve these — this
|
|
268
|
+
// just shaves the ~5-50ms POST per call across long specialist sessions.
|
|
269
|
+
// Same authority as a server "allowed" response: deny rules still beat us
|
|
270
|
+
// (CC evaluates deny before any hook decision).
|
|
271
|
+
if (isAutoApprovable(toolName, toolInput) || isCcReadOnly(toolName, toolInput)) {
|
|
272
|
+
emitDecision('allow', 'Auto-approved by shards read-only allowlist',
|
|
273
|
+
buildBashAllowInput(toolName, toolInput));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// POST to server
|
|
277
|
+
const response = await postPermissionRequest(port, token, {
|
|
278
|
+
tool: toolName,
|
|
279
|
+
command: command,
|
|
280
|
+
sessionId: sessionId,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Server unreachable — fall through to Claude Code's own permission rules
|
|
284
|
+
// (exit 0 without structured output). Matches previous fail-open posture.
|
|
285
|
+
if (!response) {
|
|
286
|
+
process.stderr.write('shards-ui: server unreachable, deferring to Claude Code permission rules\n');
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Fast paths
|
|
291
|
+
if (response.status === 'allowed') {
|
|
292
|
+
emitDecision('allow', 'Allowed by shards-ui permission rules', buildBashAllowInput(toolName, toolInput));
|
|
293
|
+
}
|
|
294
|
+
if (response.status === 'denied') {
|
|
295
|
+
emitDecision('deny', 'Denied by shards-ui permission rules');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Pending — poll the UI until the user decides, OR until the poll deadline
|
|
299
|
+
// expires. The deadline is generous (default 10 minutes) so a user who steps
|
|
300
|
+
// away mid-call doesn't have their tool silently rejected. On expiry we exit
|
|
301
|
+
// 0 with no structured output — same posture as "server unreachable" above —
|
|
302
|
+
// which hands control back to Claude Code's built-in permission rules.
|
|
303
|
+
//
|
|
304
|
+
// Tunable via SHARDS_UI_PERMISSION_TIMEOUT_MS; the hard ceiling is the
|
|
305
|
+
// .claude/settings.json hook `timeout` (1800s today). Setting the env var to
|
|
306
|
+
// 0 restores the old wait-forever behavior.
|
|
307
|
+
const POLL_MS = 200;
|
|
308
|
+
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
309
|
+
const envTimeout = parseInt(process.env.SHARDS_UI_PERMISSION_TIMEOUT_MS, 10);
|
|
310
|
+
const timeoutMs = Number.isFinite(envTimeout) && envTimeout >= 0
|
|
311
|
+
? envTimeout
|
|
312
|
+
: DEFAULT_TIMEOUT_MS;
|
|
313
|
+
const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : null;
|
|
314
|
+
|
|
315
|
+
for (;;) {
|
|
316
|
+
await sleep(POLL_MS);
|
|
317
|
+
const decision = await pollDecision(port, token, response.id);
|
|
318
|
+
if (decision === 'allow') {
|
|
319
|
+
emitDecision('allow', 'Approved by user in Shards UI', buildBashAllowInput(toolName, toolInput));
|
|
320
|
+
}
|
|
321
|
+
if (decision === 'deny') {
|
|
322
|
+
emitDecision('deny', 'Denied by user in Shards UI');
|
|
323
|
+
}
|
|
324
|
+
if (deadline && Date.now() >= deadline) {
|
|
325
|
+
process.stderr.write(
|
|
326
|
+
`shards-ui: no user decision after ${Math.round(timeoutMs / 1000)}s — ` +
|
|
327
|
+
`deferring to Claude Code permission rules\n`
|
|
328
|
+
);
|
|
329
|
+
process.exit(0);
|
|
330
|
+
}
|
|
331
|
+
// 'pending' or 'not_found' — keep polling
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function extractTextContent(content) {
|
|
336
|
+
if (typeof content === 'string') return content;
|
|
337
|
+
if (Array.isArray(content)) {
|
|
338
|
+
return content
|
|
339
|
+
.filter((b) => b && b.type === 'text')
|
|
340
|
+
.map((b) => b.text || '')
|
|
341
|
+
.join('');
|
|
342
|
+
}
|
|
343
|
+
return '';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function main() {
|
|
347
|
+
const serverInfo = getServerInfo();
|
|
348
|
+
if (!serverInfo || !serverInfo.port) {
|
|
349
|
+
// For pre-tool-use, fail open if server info unavailable
|
|
350
|
+
if (eventType === 'pre-tool-use') process.exit(0);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const { port, token } = serverInfo;
|
|
355
|
+
|
|
356
|
+
let rawData = '';
|
|
357
|
+
process.stdin.setEncoding('utf8');
|
|
358
|
+
for await (const chunk of process.stdin) {
|
|
359
|
+
rawData += chunk;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let payload;
|
|
363
|
+
try {
|
|
364
|
+
payload = JSON.parse(rawData);
|
|
365
|
+
} catch {
|
|
366
|
+
if (eventType === 'pre-tool-use') process.exit(0);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Pre-tool-use: blocking mode — handle separately and exit
|
|
371
|
+
if (eventType === 'pre-tool-use') {
|
|
372
|
+
await handlePreToolUse(port, token, payload);
|
|
373
|
+
return; // handlePreToolUse always calls process.exit
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const state = getState();
|
|
377
|
+
|
|
378
|
+
// Claude Code injects session_id into every hook stdin payload (verified by
|
|
379
|
+
// the pre-tool-use branch above which already reads it). Forward it on every
|
|
380
|
+
// event we post so the server can target SSE delivery per browser session
|
|
381
|
+
// instead of misattributing to "most recently active session." Sessions
|
|
382
|
+
// started via the shards-ui server use --session-id <uuid>, so this value
|
|
383
|
+
// matches the SessionStore key 1:1 for UI-spawned sessions; sessions started
|
|
384
|
+
// outside the UI simply won't match any store (server falls through to the
|
|
385
|
+
// legacy broadcast path).
|
|
386
|
+
const ccSessionId = payload.session_id || null;
|
|
387
|
+
|
|
388
|
+
// ─── P2: Drain pending events from queue before processing current ───
|
|
389
|
+
// Note: queued events were captured with their *original* sessionId. Don't
|
|
390
|
+
// overwrite it with the current process's session_id — that would re-target
|
|
391
|
+
// an old session's event to the wrong browser. Fall through to the entry's
|
|
392
|
+
// stored sessionId, or null for legacy entries written before this change.
|
|
393
|
+
const pending = readQueue();
|
|
394
|
+
const stillPending = [];
|
|
395
|
+
for (const entry of pending) {
|
|
396
|
+
const acked = await postEvent(port, token, {
|
|
397
|
+
...entry.payload,
|
|
398
|
+
seq: entry.seq,
|
|
399
|
+
sessionId: entry.payload && entry.payload.sessionId !== undefined
|
|
400
|
+
? entry.payload.sessionId
|
|
401
|
+
: (entry.sessionId || null),
|
|
402
|
+
});
|
|
403
|
+
if (!acked) {
|
|
404
|
+
stillPending.push(entry);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ─── Build current event payload ───
|
|
409
|
+
let currentPayload = null;
|
|
410
|
+
|
|
411
|
+
if (eventType === 'user-prompt') {
|
|
412
|
+
const transcript = payload.transcript || [];
|
|
413
|
+
const lastMsg = transcript[transcript.length - 1];
|
|
414
|
+
if (lastMsg && lastMsg.role === 'user') {
|
|
415
|
+
const content = extractTextContent(lastMsg.content);
|
|
416
|
+
currentPayload = {
|
|
417
|
+
eventType: 'user-message',
|
|
418
|
+
content,
|
|
419
|
+
agent: state.currentAgent,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
} else if (eventType === 'stop') {
|
|
424
|
+
const transcript = payload.transcript || [];
|
|
425
|
+
const lastMsg = transcript[transcript.length - 1];
|
|
426
|
+
if (lastMsg && lastMsg.role === 'assistant') {
|
|
427
|
+
const content = extractTextContent(lastMsg.content);
|
|
428
|
+
if (content) {
|
|
429
|
+
currentPayload = {
|
|
430
|
+
eventType: 'agent-message',
|
|
431
|
+
content,
|
|
432
|
+
agent: state.currentAgent,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
} else if (eventType === 'post-tool-use') {
|
|
438
|
+
const toolName = payload.tool_name || '';
|
|
439
|
+
const toolInput = payload.tool_input || {};
|
|
440
|
+
|
|
441
|
+
if (toolName === 'Task') {
|
|
442
|
+
const consultAgent = toolInput.subagent_type;
|
|
443
|
+
if (consultAgent) {
|
|
444
|
+
// Send both events — queue the first, send second as current
|
|
445
|
+
const evt1 = { eventType: 'agent-consulting', agent: consultAgent, sessionId: ccSessionId };
|
|
446
|
+
const entry1 = { seq: nextSeq(), timestamp: Date.now(), payload: evt1 };
|
|
447
|
+
const acked1 = await postEvent(port, token, { ...evt1, seq: entry1.seq });
|
|
448
|
+
if (!acked1) stillPending.push(entry1);
|
|
449
|
+
|
|
450
|
+
currentPayload = { eventType: 'event-log', text: `Consulting ${consultAgent}...`, sessionId: ccSessionId };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
} else if (toolName === 'Read') {
|
|
454
|
+
const filePath = toolInput.file_path || '';
|
|
455
|
+
const agentMatch = filePath.match(/\.claude\/agents\/([^/]+)\.md$/);
|
|
456
|
+
if (agentMatch && state.currentAgent === 'syn') {
|
|
457
|
+
const newAgent = agentMatch[1];
|
|
458
|
+
const prevAgent = state.currentAgent;
|
|
459
|
+
state.currentAgent = newAgent;
|
|
460
|
+
saveState(state);
|
|
461
|
+
|
|
462
|
+
// Send activation events
|
|
463
|
+
const evt1 = { eventType: 'agent-activated', agent: newAgent, sessionId: ccSessionId };
|
|
464
|
+
const entry1 = { seq: nextSeq(), timestamp: Date.now(), payload: evt1 };
|
|
465
|
+
const acked1 = await postEvent(port, token, { ...evt1, seq: entry1.seq });
|
|
466
|
+
if (!acked1) stillPending.push(entry1);
|
|
467
|
+
|
|
468
|
+
const evt2 = { eventType: 'agent-changed', from: prevAgent, to: newAgent, sessionId: ccSessionId };
|
|
469
|
+
const entry2 = { seq: nextSeq(), timestamp: Date.now(), payload: evt2 };
|
|
470
|
+
const acked2 = await postEvent(port, token, { ...evt2, seq: entry2.seq });
|
|
471
|
+
if (!acked2) stillPending.push(entry2);
|
|
472
|
+
|
|
473
|
+
currentPayload = { eventType: 'event-log', text: `Persona transfer: ${prevAgent} -> ${newAgent}`, sessionId: ccSessionId };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
} else if (toolName === 'Write' || toolName === 'Edit') {
|
|
477
|
+
const fp = toolInput.file_path || toolInput.path || 'file';
|
|
478
|
+
const evt1 = { eventType: 'event-log', text: `${toolName}: ${fp}`, sessionId: ccSessionId };
|
|
479
|
+
const entry1 = { seq: nextSeq(), timestamp: Date.now(), payload: evt1 };
|
|
480
|
+
const acked1 = await postEvent(port, token, { ...evt1, seq: entry1.seq });
|
|
481
|
+
if (!acked1) stillPending.push(entry1);
|
|
482
|
+
|
|
483
|
+
currentPayload = { eventType: 'file-touched', filePath: fp, sessionId: ccSessionId };
|
|
484
|
+
|
|
485
|
+
} else if (toolName === 'Bash') {
|
|
486
|
+
currentPayload = { eventType: 'event-log', text: `Bash: ${(toolInput.command || '').slice(0, 60)}`, sessionId: ccSessionId };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
} else if (eventType === 'session-end') {
|
|
490
|
+
currentPayload = { eventType: 'session-end', sessionId: ccSessionId };
|
|
491
|
+
saveState({ currentAgent: 'syn', sessionId: null, messageCount: 0 });
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Tag user-prompt / stop payloads built earlier with the CC session_id too
|
|
495
|
+
// so the server can target SSE delivery per browser session.
|
|
496
|
+
if (currentPayload && currentPayload.sessionId === undefined) {
|
|
497
|
+
currentPayload.sessionId = ccSessionId;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ─── P2: Send current event, queue on failure ───
|
|
501
|
+
if (currentPayload) {
|
|
502
|
+
const entry = { seq: nextSeq(), timestamp: Date.now(), payload: currentPayload };
|
|
503
|
+
const acked = await postEvent(port, token, { ...currentPayload, seq: entry.seq });
|
|
504
|
+
if (!acked) {
|
|
505
|
+
stillPending.push(entry);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Write remaining unacked events back to queue
|
|
510
|
+
writeQueue(stillPending);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
main().catch(function(err) { process.stderr.write('shards-ui relay: ' + (err && err.message || err) + '\n'); });
|