@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,205 @@
|
|
|
1
|
+
// Parse ::GATE:: ... ::ENDGATE:: fences from text
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Matches ::GATE:: <attrs>\n<body>\n::ENDGATE::
|
|
7
|
+
const FENCE = /^::GATE::\s+([^\n]+)\n([\s\S]*?)\n::ENDGATE::\s*$/gm;
|
|
8
|
+
|
|
9
|
+
// Authoritative kind enum. See CLAUDE.md "The gate pattern" section.
|
|
10
|
+
// phase — intermediate phase gate; advances phase on confirm.
|
|
11
|
+
// final — last phase of a track; advances phase on confirm.
|
|
12
|
+
// checkpoint — mid-build component seam; does NOT advance phase. Advisory
|
|
13
|
+
// under SHARDS_CHECKPOINT_ENFORCE=0.
|
|
14
|
+
// execute — runs an experiment/AR iteration; advances phase on confirm
|
|
15
|
+
// (labeled "Execute gate").
|
|
16
|
+
// confirm — micro-confirmation checkpoint used in AR; does NOT advance
|
|
17
|
+
// phase. Advisory under SHARDS_CHECKPOINT_ENFORCE=0.
|
|
18
|
+
// handoff — specialist-to-specialist transition; advances on confirm.
|
|
19
|
+
const VALID_KINDS = new Set(['phase', 'final', 'checkpoint', 'execute', 'confirm', 'handoff']);
|
|
20
|
+
|
|
21
|
+
// Kinds that behave like checkpoints (do not advance phase; advisory-eligible).
|
|
22
|
+
const NON_ADVANCING_KINDS = new Set(['checkpoint', 'confirm']);
|
|
23
|
+
|
|
24
|
+
function parseAttrs(line) {
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const pair of line.trim().split(/\s+/)) {
|
|
27
|
+
const [k, v] = pair.split('=');
|
|
28
|
+
if (k && v) out[k] = v;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Lazy require to avoid circular dependency on log.js (log.js does not require
|
|
34
|
+
// parser, but defensive). Falls back to stderr-only logging if log.js fails
|
|
35
|
+
// to load for any reason.
|
|
36
|
+
function logUnknownKind(kindRaw, attrs) {
|
|
37
|
+
try {
|
|
38
|
+
process.stderr.write(`[shards gate-hook] warning: unknown gate kind '${kindRaw}' on gate id='${attrs.id || '<unknown>'}' — normalizing to 'phase'\n`);
|
|
39
|
+
} catch {}
|
|
40
|
+
try {
|
|
41
|
+
const { appendViolation } = require(path.join(__dirname, 'log.js'));
|
|
42
|
+
appendViolation({
|
|
43
|
+
type: 'unknown-gate-kind',
|
|
44
|
+
gate_id: attrs.id || null,
|
|
45
|
+
reason: `unknown gate kind '${kindRaw}' — normalized to 'phase'`,
|
|
46
|
+
});
|
|
47
|
+
} catch {
|
|
48
|
+
// logging is best-effort
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate and normalize the kind= attribute on a parsed gate. Unknown kinds
|
|
53
|
+
// are normalized to 'phase' (backward-compatible) and a warning is logged.
|
|
54
|
+
function normalizeKind(attrs) {
|
|
55
|
+
const raw = attrs.kind;
|
|
56
|
+
if (!raw) {
|
|
57
|
+
attrs.kind = 'phase';
|
|
58
|
+
return attrs;
|
|
59
|
+
}
|
|
60
|
+
if (VALID_KINDS.has(raw)) return attrs;
|
|
61
|
+
// Unknown kind — warn + normalize.
|
|
62
|
+
logUnknownKind(raw, attrs);
|
|
63
|
+
attrs.kind = 'phase';
|
|
64
|
+
return attrs;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Pair-up ``` (or longer-backtick) lines into [start, end] character ranges so
|
|
68
|
+
// gate markers that only appear inside a quoted markdown code block don't open
|
|
69
|
+
// real gates. Without this, the assistant cannot quote a phase template or
|
|
70
|
+
// explain the gate pattern in this repo without tripping enforcement.
|
|
71
|
+
const CODE_FENCE_LINE = /^(`{3,})[^\n]*$/gm;
|
|
72
|
+
|
|
73
|
+
function findCodeBlockRanges(text) {
|
|
74
|
+
const fences = [];
|
|
75
|
+
CODE_FENCE_LINE.lastIndex = 0;
|
|
76
|
+
let m;
|
|
77
|
+
while ((m = CODE_FENCE_LINE.exec(text)) !== null) {
|
|
78
|
+
fences.push({ start: m.index, end: m.index + m[0].length, ticks: m[1].length });
|
|
79
|
+
}
|
|
80
|
+
const ranges = [];
|
|
81
|
+
let i = 0;
|
|
82
|
+
while (i < fences.length) {
|
|
83
|
+
const open = fences[i];
|
|
84
|
+
// A closing fence must use the same number of backticks (CommonMark rule —
|
|
85
|
+
// longer-tick fences nest shorter ones). Find the next fence with matching
|
|
86
|
+
// tick count.
|
|
87
|
+
let j = i + 1;
|
|
88
|
+
while (j < fences.length && fences[j].ticks !== open.ticks) j++;
|
|
89
|
+
if (j < fences.length) {
|
|
90
|
+
ranges.push({ start: open.start, end: fences[j].end });
|
|
91
|
+
i = j + 1;
|
|
92
|
+
} else {
|
|
93
|
+
// Unclosed fence — treat rest of doc as inside-code, conservative.
|
|
94
|
+
ranges.push({ start: open.start, end: text.length });
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return ranges;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isInsideAnyRange(idx, ranges) {
|
|
102
|
+
for (const r of ranges) if (idx >= r.start && idx < r.end) return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseGates(text) {
|
|
107
|
+
const gates = [];
|
|
108
|
+
const codeRanges = findCodeBlockRanges(text);
|
|
109
|
+
FENCE.lastIndex = 0;
|
|
110
|
+
let m;
|
|
111
|
+
while ((m = FENCE.exec(text)) !== null) {
|
|
112
|
+
if (isInsideAnyRange(m.index, codeRanges)) continue;
|
|
113
|
+
const attrs = normalizeKind(parseAttrs(m[1]));
|
|
114
|
+
gates.push({ attrs, body: m[2], raw: m[0], index: m.index, fenceEnd: m.index + m[0].length });
|
|
115
|
+
}
|
|
116
|
+
return gates;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Violation heuristic: check for content after ::ENDGATE:: that looks like phase advance.
|
|
120
|
+
// Patterns must be phase-specific — generic forward-looking prose ("moving on to wrap up
|
|
121
|
+
// the docs") is a common legitimate sign-off and previously triggered false positives.
|
|
122
|
+
const PHASE_ADVANCE_PATTERNS = [
|
|
123
|
+
/^##\s*Phase\s+\d+/im,
|
|
124
|
+
/^Phase\s+\d+\b/im,
|
|
125
|
+
/\bproceed(ing)?\s+to\s+phase\b/i,
|
|
126
|
+
/\bmoving\s+on\s+to\s+(the\s+next\s+phase|phase\s+\d+)\b/i,
|
|
127
|
+
/```(python|sql|bash)/i,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
// Use the actual parsed fence end position so an ENDGATE token nested inside
|
|
131
|
+
// an unclosed code block (which the FENCE regex would not match as a real
|
|
132
|
+
// fence) does not confuse violation detection. Falls back to indexOf for
|
|
133
|
+
// backward compat when no gates parsed.
|
|
134
|
+
function checkViolation(fullMessage) {
|
|
135
|
+
const gates = parseGates(fullMessage);
|
|
136
|
+
let endIdx;
|
|
137
|
+
if (gates.length > 0) {
|
|
138
|
+
endIdx = gates[gates.length - 1].fenceEnd;
|
|
139
|
+
} else {
|
|
140
|
+
const literalIdx = fullMessage.indexOf('::ENDGATE::');
|
|
141
|
+
if (literalIdx === -1) return null;
|
|
142
|
+
endIdx = literalIdx + '::ENDGATE::'.length;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const postFence = fullMessage.slice(endIdx).trim();
|
|
146
|
+
const wordCount = postFence.split(/\s+/).filter(Boolean).length;
|
|
147
|
+
|
|
148
|
+
if (wordCount === 0) return null;
|
|
149
|
+
if (wordCount > 40) return 'Post-fence content exceeds 40-word limit (' + wordCount + ' words)';
|
|
150
|
+
|
|
151
|
+
for (const pattern of PHASE_ADVANCE_PATTERNS) {
|
|
152
|
+
if (pattern.test(postFence)) {
|
|
153
|
+
return 'Post-fence content matches phase-advance pattern: ' + pattern.toString();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── Auto-verify markers ─────────────────────────────────────────────────────
|
|
160
|
+
//
|
|
161
|
+
// Open marker: ::AUTO-VERIFY:: <attrs> (single-line, no fence body)
|
|
162
|
+
// Close marker: ::ENDAUTO:: (bare, no attrs)
|
|
163
|
+
//
|
|
164
|
+
// Example:
|
|
165
|
+
// ::AUTO-VERIFY:: agent=data-modeller phase=6 tool_budget=20 ttl_minutes=10
|
|
166
|
+
// ... read-only verification work ...
|
|
167
|
+
// ::ENDAUTO::
|
|
168
|
+
|
|
169
|
+
const AUTO_OPEN = /^::AUTO-VERIFY::\s+([^\n]+)$/gm;
|
|
170
|
+
const AUTO_CLOSE = /^::ENDAUTO::\s*$/gm;
|
|
171
|
+
|
|
172
|
+
function parseAutoVerify(text) {
|
|
173
|
+
const opens = [];
|
|
174
|
+
AUTO_OPEN.lastIndex = 0;
|
|
175
|
+
let m;
|
|
176
|
+
while ((m = AUTO_OPEN.exec(text)) !== null) {
|
|
177
|
+
opens.push({ attrs: parseAttrs(m[1]), index: m.index });
|
|
178
|
+
}
|
|
179
|
+
// Process all close markers in position order — the prior implementation
|
|
180
|
+
// used .exec() which finds only the first, so a message with multiple
|
|
181
|
+
// close markers (e.g. open-close-open-close in one assistant turn) would
|
|
182
|
+
// miss the later closes.
|
|
183
|
+
const closes = [];
|
|
184
|
+
AUTO_CLOSE.lastIndex = 0;
|
|
185
|
+
let cm;
|
|
186
|
+
while ((cm = AUTO_CLOSE.exec(text)) !== null) {
|
|
187
|
+
closes.push({ index: cm.index });
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
opens,
|
|
191
|
+
closes,
|
|
192
|
+
// Back-compat: keep `close` as the first close marker. Callers that need
|
|
193
|
+
// all markers should use `closes`.
|
|
194
|
+
close: closes.length > 0 ? closes[0] : null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = {
|
|
199
|
+
parseGates,
|
|
200
|
+
checkViolation,
|
|
201
|
+
parseAutoVerify,
|
|
202
|
+
parseAttrs,
|
|
203
|
+
VALID_KINDS,
|
|
204
|
+
NON_ADVANCING_KINDS,
|
|
205
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// SELECT-only SQL guard for warehouse-CLI invocations.
|
|
2
|
+
//
|
|
3
|
+
// Default-deny posture: this module returns true ONLY for invocations it can
|
|
4
|
+
// fully parse and confirm consist exclusively of SELECT/WITH statements. Any
|
|
5
|
+
// ambiguity (unknown CLI, unparseable quoting, comment-smuggled DDL, etc.)
|
|
6
|
+
// returns false and the call falls through to the normal permission prompt.
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ─── Step 1: extract the SQL argument from a CLI invocation ──────────────────
|
|
10
|
+
|
|
11
|
+
// Recognized CLI invocation patterns. Each entry returns the SQL string or
|
|
12
|
+
// null if the invocation isn't recognized / can't be parsed cleanly.
|
|
13
|
+
function extractSql(command) {
|
|
14
|
+
if (typeof command !== 'string') return null;
|
|
15
|
+
const cmd = command.trim();
|
|
16
|
+
if (!cmd) return null;
|
|
17
|
+
|
|
18
|
+
// bq query — the SQL is whatever comes after the flags. Format:
|
|
19
|
+
// bq query --use_legacy_sql=false 'SELECT ...'
|
|
20
|
+
// bq query --nouse_legacy_sql --format=json "SELECT ..."
|
|
21
|
+
// We accept it only if the last token is a single quoted string and the
|
|
22
|
+
// command doesn't have --destination_table / write flags.
|
|
23
|
+
if (/^bq\s+query\b/.test(cmd)) {
|
|
24
|
+
if (/--destination_table\b/i.test(cmd)) return null;
|
|
25
|
+
if (/--replace\b/i.test(cmd)) return null;
|
|
26
|
+
if (/--append_table\b/i.test(cmd)) return null;
|
|
27
|
+
return extractTrailingQuoted(cmd);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// psql -c "SELECT ..." or psql --command="SELECT ..."
|
|
31
|
+
if (/^psql\b/.test(cmd)) {
|
|
32
|
+
return extractFlagValue(cmd, ['-c', '--command']);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// snowsql -q "SELECT ..." or snowsql --query "SELECT ..."
|
|
36
|
+
if (/^snowsql\b/.test(cmd)) {
|
|
37
|
+
return extractFlagValue(cmd, ['-q', '--query']);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// clickhouse-client --query "SELECT ..." or -q
|
|
41
|
+
if (/^clickhouse-client\b/.test(cmd)) {
|
|
42
|
+
return extractFlagValue(cmd, ['-q', '--query']);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// duckdb db.duckdb -c "SELECT ..."
|
|
46
|
+
if (/^duckdb\b/.test(cmd)) {
|
|
47
|
+
return extractFlagValue(cmd, ['-c', '--command']);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// mysql -e "SELECT ..."
|
|
51
|
+
if (/^mysql\b/.test(cmd)) {
|
|
52
|
+
return extractFlagValue(cmd, ['-e', '--execute']);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Extract the value following one of the named flags. Handles both
|
|
59
|
+
// --flag=value and --flag value forms, with single or double quoted values.
|
|
60
|
+
// Returns null if it can't find a clean match.
|
|
61
|
+
function extractFlagValue(command, flags) {
|
|
62
|
+
for (const flag of flags) {
|
|
63
|
+
// --flag="..."
|
|
64
|
+
const eqRe = new RegExp(`${escapeRegex(flag)}=(['"])([\\s\\S]*?)\\1(?:\\s|$)`);
|
|
65
|
+
const eq = command.match(eqRe);
|
|
66
|
+
if (eq) return eq[2];
|
|
67
|
+
|
|
68
|
+
// --flag '...' or --flag "..."
|
|
69
|
+
const spRe = new RegExp(`${escapeRegex(flag)}\\s+(['"])([\\s\\S]*?)\\1(?:\\s|$)`);
|
|
70
|
+
const sp = command.match(spRe);
|
|
71
|
+
if (sp) return sp[2];
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extract the last quoted argument in the command. Used for `bq query '...'`.
|
|
77
|
+
function extractTrailingQuoted(command) {
|
|
78
|
+
const m = command.match(/(['"])([\s\S]*?)\1\s*$/);
|
|
79
|
+
return m ? m[2] : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function escapeRegex(s) {
|
|
83
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ─── Step 2: strip SQL comments ──────────────────────────────────────────────
|
|
87
|
+
//
|
|
88
|
+
// Strips both `-- line comments` and `/* block comments */` so that:
|
|
89
|
+
// (a) a comment containing "DROP" doesn't trigger the deny check, and
|
|
90
|
+
// (b) a comment can't smuggle a real DDL after a `;` (e.g. `SELECT 1; -- \n DROP TABLE x`)
|
|
91
|
+
function stripComments(sql) {
|
|
92
|
+
if (typeof sql !== 'string') return '';
|
|
93
|
+
let out = '';
|
|
94
|
+
let i = 0;
|
|
95
|
+
let inSingle = false;
|
|
96
|
+
let inDouble = false;
|
|
97
|
+
|
|
98
|
+
while (i < sql.length) {
|
|
99
|
+
const c = sql[i];
|
|
100
|
+
const next = sql[i + 1];
|
|
101
|
+
|
|
102
|
+
if (!inSingle && !inDouble) {
|
|
103
|
+
if (c === '-' && next === '-') {
|
|
104
|
+
// Line comment — skip to newline
|
|
105
|
+
const nl = sql.indexOf('\n', i);
|
|
106
|
+
if (nl === -1) return out;
|
|
107
|
+
i = nl + 1;
|
|
108
|
+
out += '\n';
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (c === '/' && next === '*') {
|
|
112
|
+
// Block comment — skip to */
|
|
113
|
+
const end = sql.indexOf('*/', i + 2);
|
|
114
|
+
if (end === -1) return out; // unterminated block comment — bail
|
|
115
|
+
i = end + 2;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (c === "'" && !inDouble) {
|
|
121
|
+
// Handle escaped quote ''
|
|
122
|
+
if (inSingle && next === "'") { out += "''"; i += 2; continue; }
|
|
123
|
+
inSingle = !inSingle;
|
|
124
|
+
} else if (c === '"' && !inSingle) {
|
|
125
|
+
inDouble = !inDouble;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
out += c;
|
|
129
|
+
i++;
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── Step 3: split on ; respecting quotes ────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
function splitStatements(sql) {
|
|
137
|
+
const out = [];
|
|
138
|
+
let cur = '';
|
|
139
|
+
let inSingle = false;
|
|
140
|
+
let inDouble = false;
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < sql.length; i++) {
|
|
143
|
+
const c = sql[i];
|
|
144
|
+
if (c === "'" && !inDouble) {
|
|
145
|
+
if (inSingle && sql[i + 1] === "'") { cur += "''"; i++; continue; }
|
|
146
|
+
inSingle = !inSingle;
|
|
147
|
+
} else if (c === '"' && !inSingle) {
|
|
148
|
+
inDouble = !inDouble;
|
|
149
|
+
}
|
|
150
|
+
if (c === ';' && !inSingle && !inDouble) {
|
|
151
|
+
out.push(cur);
|
|
152
|
+
cur = '';
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
cur += c;
|
|
156
|
+
}
|
|
157
|
+
if (cur.trim()) out.push(cur);
|
|
158
|
+
return out.map(s => s.trim()).filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── Step 4: classify a statement ────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
const FORBIDDEN = [
|
|
164
|
+
'INTO', 'INSERT', 'UPDATE', 'DELETE', 'MERGE', 'CREATE', 'DROP', 'ALTER',
|
|
165
|
+
'TRUNCATE', 'GRANT', 'REVOKE', 'COPY', 'CALL', 'EXECUTE', 'EXPORT', 'LOAD',
|
|
166
|
+
'REPLACE', 'RENAME', 'COMMENT', 'ANALYZE', 'VACUUM', 'CLUSTER', 'REFRESH',
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
function isReadOnlyStatement(stmt) {
|
|
170
|
+
if (!stmt) return false;
|
|
171
|
+
const head = stmt.replace(/^\(+/, '').match(/^\s*(\w+)/);
|
|
172
|
+
if (!head) return false;
|
|
173
|
+
const verb = head[1].toUpperCase();
|
|
174
|
+
if (verb !== 'SELECT' && verb !== 'WITH') return false;
|
|
175
|
+
|
|
176
|
+
// Check for forbidden keywords as whole words. We strip string literals
|
|
177
|
+
// first so a literal value like 'INSERT INTO logs' doesn't trigger.
|
|
178
|
+
const stripped = stripStringLiterals(stmt);
|
|
179
|
+
for (const kw of FORBIDDEN) {
|
|
180
|
+
const re = new RegExp(`\\b${kw}\\b`, 'i');
|
|
181
|
+
if (re.test(stripped)) return false;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function stripStringLiterals(sql) {
|
|
187
|
+
let out = '';
|
|
188
|
+
let inSingle = false;
|
|
189
|
+
let inDouble = false;
|
|
190
|
+
for (let i = 0; i < sql.length; i++) {
|
|
191
|
+
const c = sql[i];
|
|
192
|
+
if (c === "'" && !inDouble) {
|
|
193
|
+
if (inSingle && sql[i + 1] === "'") { i++; continue; }
|
|
194
|
+
inSingle = !inSingle;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (c === '"' && !inSingle) {
|
|
198
|
+
inDouble = !inDouble;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (inSingle || inDouble) continue;
|
|
202
|
+
out += c;
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
// isReadOnlyCliInvocation(command) → true only if:
|
|
210
|
+
// 1. We recognize the CLI shape and can extract the SQL cleanly
|
|
211
|
+
// 2. Every statement in the SQL starts with SELECT or WITH
|
|
212
|
+
// 3. No statement contains any forbidden DDL/DML keyword (after stripping
|
|
213
|
+
// comments and string literals)
|
|
214
|
+
function isReadOnlyCliInvocation(command) {
|
|
215
|
+
const sql = extractSql(command);
|
|
216
|
+
if (!sql) return false;
|
|
217
|
+
const stripped = stripComments(sql);
|
|
218
|
+
const statements = splitStatements(stripped);
|
|
219
|
+
if (statements.length === 0) return false;
|
|
220
|
+
return statements.every(isReadOnlyStatement);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
isReadOnlyCliInvocation,
|
|
225
|
+
// exported for tests
|
|
226
|
+
_extractSql: extractSql,
|
|
227
|
+
_stripComments: stripComments,
|
|
228
|
+
_splitStatements: splitStatements,
|
|
229
|
+
_isReadOnlyStatement: isReadOnlyStatement,
|
|
230
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Gate state read/write for .shards/gates/state.json
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const GATE_DIR = path.join(process.cwd(), '.shards', 'gates');
|
|
8
|
+
const STATE = path.join(GATE_DIR, 'state.json');
|
|
9
|
+
|
|
10
|
+
// On-disk v2 shape:
|
|
11
|
+
// { version: 2, sessions: { "<sessionId>": { open, id, phase, kind, agent,
|
|
12
|
+
// opened_at, opened_in_turn, transcript_ref } }, history: [ ... ] }
|
|
13
|
+
//
|
|
14
|
+
// `history` is a SHARED, append-only array of closed/swept/force-closed gates
|
|
15
|
+
// from every session. Each session slot holds at most one open gate; a closed
|
|
16
|
+
// slot is simply absent (treat missing as closed).
|
|
17
|
+
//
|
|
18
|
+
// Legacy v1 shape was a single global slot: { open, id, phase, kind, agent,
|
|
19
|
+
// opened_at, ..., history }. read() upgrades that to v2 in memory so an in-
|
|
20
|
+
// flight gate during the format upgrade still enforces.
|
|
21
|
+
|
|
22
|
+
// Stable key for a legacy OPEN gate that has no session attribution. We use the
|
|
23
|
+
// gate's `agent` field when present (matches handleStop's `agent || sessionId`
|
|
24
|
+
// fallback) so the next confirm/sweep can find it, else the literal sentinel.
|
|
25
|
+
const LEGACY_KEY = '__legacy__';
|
|
26
|
+
|
|
27
|
+
function ensureDir() {
|
|
28
|
+
// `recursive: true` is idempotent — safe even if a concurrent writer just
|
|
29
|
+
// created the directory between calls.
|
|
30
|
+
fs.mkdirSync(GATE_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Normalize any parsed object into the canonical v2 shape. Backward-compatible:
|
|
34
|
+
// a legacy single-slot object (no `version`/`sessions`, has top-level `open`)
|
|
35
|
+
// is transparently upgraded in memory. A legacy OPEN gate is placed under the
|
|
36
|
+
// gate's `agent` field if present, else LEGACY_KEY, so it still enforces.
|
|
37
|
+
function normalize(obj) {
|
|
38
|
+
if (!obj || typeof obj !== 'object') {
|
|
39
|
+
return { version: 2, sessions: {}, history: [] };
|
|
40
|
+
}
|
|
41
|
+
// Already v2 — just backfill missing collections defensively.
|
|
42
|
+
if (obj.version === 2 || obj.sessions) {
|
|
43
|
+
return {
|
|
44
|
+
version: 2,
|
|
45
|
+
sessions: (obj.sessions && typeof obj.sessions === 'object') ? obj.sessions : {},
|
|
46
|
+
history: Array.isArray(obj.history) ? obj.history : [],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Legacy single-slot upgrade.
|
|
50
|
+
const history = Array.isArray(obj.history) ? obj.history : [];
|
|
51
|
+
const sessions = {};
|
|
52
|
+
if (obj.open) {
|
|
53
|
+
const key = obj.agent || LEGACY_KEY;
|
|
54
|
+
sessions[key] = {
|
|
55
|
+
open: true,
|
|
56
|
+
id: obj.id,
|
|
57
|
+
phase: typeof obj.phase !== 'undefined' ? obj.phase : null,
|
|
58
|
+
kind: obj.kind || null,
|
|
59
|
+
agent: obj.agent || null,
|
|
60
|
+
opened_at: obj.opened_at || null,
|
|
61
|
+
opened_in_turn: typeof obj.opened_in_turn !== 'undefined' ? obj.opened_in_turn : null,
|
|
62
|
+
transcript_ref: typeof obj.transcript_ref !== 'undefined' ? obj.transcript_ref : null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { version: 2, sessions, history };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function read() {
|
|
69
|
+
try {
|
|
70
|
+
return normalize(JSON.parse(fs.readFileSync(STATE, 'utf8')));
|
|
71
|
+
} catch {
|
|
72
|
+
return { version: 2, sessions: {}, history: [] };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Atomic write via tmp + rename. Reference: src/ui/session-index.js.
|
|
77
|
+
// Three concurrent readers (UI 2s poll, /chat/end snapshot, gate-hook's own
|
|
78
|
+
// pre-tool-use/user-prompt-submit handlers) can hit during a bare writeFileSync
|
|
79
|
+
// and observe a partial file → JSON.parse throws → read() silently returns
|
|
80
|
+
// default closed state. tmp+rename ensures readers always see the full
|
|
81
|
+
// previous or the full next content.
|
|
82
|
+
//
|
|
83
|
+
// Unique per-PID tmp name avoids same-PID collisions (multiple hook
|
|
84
|
+
// invocations chained on the same process aren't expected, but the cost is
|
|
85
|
+
// negligible and it matches the session-index.js reference primitive).
|
|
86
|
+
function write(s) {
|
|
87
|
+
ensureDir();
|
|
88
|
+
const tmp = `${STATE}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
89
|
+
try {
|
|
90
|
+
fs.writeFileSync(tmp, JSON.stringify(s, null, 2));
|
|
91
|
+
fs.renameSync(tmp, STATE);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
// Best-effort cleanup so a failed write doesn't leak per-writer tmps.
|
|
94
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─── Per-session helpers ──────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
// The gate object for one session, or a closed sentinel if that slot is absent.
|
|
102
|
+
//
|
|
103
|
+
// Backward-compat: a legacy single-slot gate that was upgraded with no agent
|
|
104
|
+
// attribution lives under LEGACY_KEY. Since the original v1 slot was global
|
|
105
|
+
// (it blocked whichever session was active), we fall back to that LEGACY_KEY
|
|
106
|
+
// slot for ANY session that has no slot of its own — so an in-flight gate
|
|
107
|
+
// during the format upgrade keeps enforcing regardless of session_id.
|
|
108
|
+
function getGate(state, sessionId) {
|
|
109
|
+
const sessions = (state && state.sessions) || {};
|
|
110
|
+
const slot = sessions[sessionId];
|
|
111
|
+
if (slot && slot.open) return slot;
|
|
112
|
+
if (sessionId !== LEGACY_KEY) {
|
|
113
|
+
const legacy = sessions[LEGACY_KEY];
|
|
114
|
+
if (legacy && legacy.open) return legacy;
|
|
115
|
+
}
|
|
116
|
+
return { open: false };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Return a NEW state object with `sessionId`'s slot set to `gate`. Does not
|
|
120
|
+
// mutate the input; preserves `history` and all other sessions.
|
|
121
|
+
function setGate(state, sessionId, gate) {
|
|
122
|
+
const base = normalize(state);
|
|
123
|
+
return {
|
|
124
|
+
version: 2,
|
|
125
|
+
sessions: { ...base.sessions, [sessionId]: gate },
|
|
126
|
+
history: base.history,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Return a NEW state object with `sessionId`'s slot removed (closed). History
|
|
131
|
+
// and other sessions preserved.
|
|
132
|
+
function clearGate(state, sessionId) {
|
|
133
|
+
const base = normalize(state);
|
|
134
|
+
const sessions = { ...base.sessions };
|
|
135
|
+
delete sessions[sessionId];
|
|
136
|
+
return { version: 2, sessions, history: base.history };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// True if any session slot is currently open.
|
|
140
|
+
function anyOpen(state) {
|
|
141
|
+
const base = normalize(state);
|
|
142
|
+
return Object.values(base.sessions).some(g => g && g.open);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The most-recently-opened still-open gate across all sessions (by opened_at),
|
|
146
|
+
// or null. Used by the UI read side to reduce the per-session map to a single
|
|
147
|
+
// flat gate for the legacy browser contract.
|
|
148
|
+
function firstOpen(state) {
|
|
149
|
+
const base = normalize(state);
|
|
150
|
+
let best = null;
|
|
151
|
+
let bestT = -Infinity;
|
|
152
|
+
for (const [sessionId, g] of Object.entries(base.sessions)) {
|
|
153
|
+
if (!g || !g.open) continue;
|
|
154
|
+
const t = g.opened_at ? Date.parse(g.opened_at) : NaN;
|
|
155
|
+
const score = Number.isNaN(t) ? -Infinity : t;
|
|
156
|
+
// Prefer the most recent; a gate with no/invalid opened_at still wins over
|
|
157
|
+
// nothing (best === null) but loses to any dated gate.
|
|
158
|
+
if (best === null || score > bestT) {
|
|
159
|
+
best = { sessionId, ...g };
|
|
160
|
+
bestT = score;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return best;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = {
|
|
167
|
+
read, write, STATE, GATE_DIR,
|
|
168
|
+
getGate, setGate, clearGate, anyOpen, firstOpen,
|
|
169
|
+
LEGACY_KEY,
|
|
170
|
+
};
|