@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,343 @@
|
|
|
1
|
+
// End-to-end tests for auto-verify mode.
|
|
2
|
+
//
|
|
3
|
+
// These tests pipe synthetic stdin payloads to gate-hook.js and assert on
|
|
4
|
+
// stdout (the hook response) and on .shards/auto/state.json (the persisted
|
|
5
|
+
// state). They run in an isolated tmp directory per test so concurrent
|
|
6
|
+
// runs don't collide.
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { spawnSync } from 'child_process';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
19
|
+
const HOOK = path.join(REPO_ROOT, 'tools', 'gate-hook.js');
|
|
20
|
+
|
|
21
|
+
// ─── Test infrastructure ──────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
let tmp;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'shards-auto-test-'));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
if (tmp) {
|
|
31
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
32
|
+
tmp = null;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Write a fake transcript file with one assistant message containing `text`.
|
|
37
|
+
// Mirrors the JSONL shape Claude Code writes:
|
|
38
|
+
// {type: "assistant", message: {role: "assistant", content: [...]}}
|
|
39
|
+
function writeTranscript(text, shape = 'real') {
|
|
40
|
+
const file = path.join(tmp, 'transcript.jsonl');
|
|
41
|
+
let entry;
|
|
42
|
+
if (shape === 'real') {
|
|
43
|
+
entry = {
|
|
44
|
+
type: 'assistant',
|
|
45
|
+
message: { role: 'assistant', content: [{ type: 'text', text }] },
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
// legacy — kept for symmetry; transcript.js still accepts this shape
|
|
49
|
+
entry = { role: 'assistant', content: [{ type: 'text', text }] };
|
|
50
|
+
}
|
|
51
|
+
fs.writeFileSync(file, JSON.stringify(entry) + '\n');
|
|
52
|
+
return file;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function runHook(event, payload, env = {}) {
|
|
56
|
+
const result = spawnSync('node', [HOOK, event], {
|
|
57
|
+
cwd: tmp,
|
|
58
|
+
input: JSON.stringify(payload),
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
env: { ...process.env, ...env },
|
|
61
|
+
});
|
|
62
|
+
let parsed = null;
|
|
63
|
+
if (result.stdout && result.stdout.trim()) {
|
|
64
|
+
try { parsed = JSON.parse(result.stdout.trim()); } catch { /* not JSON */ }
|
|
65
|
+
}
|
|
66
|
+
return { stdout: result.stdout, stderr: result.stderr, status: result.status, parsed };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function readAutoState() {
|
|
70
|
+
const file = path.join(tmp, '.shards', 'auto', 'state.json');
|
|
71
|
+
if (!fs.existsSync(file)) return { open: false };
|
|
72
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readGateState() {
|
|
76
|
+
const file = path.join(tmp, '.shards', 'gates', 'state.json');
|
|
77
|
+
if (!fs.existsSync(file)) return { open: false };
|
|
78
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
79
|
+
// v2 per-session shape: reduce to a flat { open, ... } by surfacing any open
|
|
80
|
+
// session slot (legacy single-slot files just pass through).
|
|
81
|
+
if (raw && raw.sessions && typeof raw.sessions === 'object') {
|
|
82
|
+
const open = Object.values(raw.sessions).find(g => g && g.open);
|
|
83
|
+
return open ? { open: true, ...open, history: raw.history || [] }
|
|
84
|
+
: { open: false, history: raw.history || [] };
|
|
85
|
+
}
|
|
86
|
+
return raw;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
describe('auto-verify', () => {
|
|
92
|
+
it('::AUTO-VERIFY:: marker opens state', () => {
|
|
93
|
+
const transcript = writeTranscript(
|
|
94
|
+
'::AUTO-VERIFY:: agent=data-modeller phase=6 tool_budget=5 ttl_minutes=2'
|
|
95
|
+
);
|
|
96
|
+
runHook('stop', { transcript_path: transcript, session_id: 's1' });
|
|
97
|
+
const s = readAutoState();
|
|
98
|
+
expect(s.open).toBe(true);
|
|
99
|
+
expect(s.agent).toBe('data-modeller');
|
|
100
|
+
expect(s.phase).toBe('6');
|
|
101
|
+
expect(s.tool_budget_remaining).toBe(5);
|
|
102
|
+
expect(s.tool_budget_initial).toBe(5);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('::ENDAUTO:: marker closes state', () => {
|
|
106
|
+
// Open
|
|
107
|
+
const t1 = writeTranscript('::AUTO-VERIFY:: agent=ml-engineer phase=3 tool_budget=10');
|
|
108
|
+
runHook('stop', { transcript_path: t1, session_id: 's1' });
|
|
109
|
+
expect(readAutoState().open).toBe(true);
|
|
110
|
+
// Close
|
|
111
|
+
const t2 = writeTranscript('Done with verification.\n\n::ENDAUTO::');
|
|
112
|
+
runHook('stop', { transcript_path: t2, session_id: 's1' });
|
|
113
|
+
expect(readAutoState().open).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('PreToolUse auto-approves dbt show when block open', () => {
|
|
117
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=data-modeller phase=6 tool_budget=5');
|
|
118
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
119
|
+
const r = runHook('pre-tool-use', {
|
|
120
|
+
tool_name: 'Bash',
|
|
121
|
+
tool_input: { command: 'dbt show --select my_model' },
|
|
122
|
+
});
|
|
123
|
+
expect(r.parsed && r.parsed.hookSpecificOutput).toBeTruthy();
|
|
124
|
+
expect(r.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
125
|
+
const s = readAutoState();
|
|
126
|
+
expect(s.tool_budget_remaining).toBe(4);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('PreToolUse passes through non-allowlisted tool', () => {
|
|
130
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=data-modeller phase=6');
|
|
131
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
132
|
+
// A Write tool call should NOT be auto-approved.
|
|
133
|
+
const r = runHook('pre-tool-use', {
|
|
134
|
+
tool_name: 'Write',
|
|
135
|
+
tool_input: { file_path: '/x.sql' },
|
|
136
|
+
});
|
|
137
|
+
expect(r.parsed).toBeNull();
|
|
138
|
+
// Budget should not have decremented.
|
|
139
|
+
const s = readAutoState();
|
|
140
|
+
expect(s.tool_budget_remaining).toBe(20);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('PreToolUse rejects dbt build (writes never auto-approved)', () => {
|
|
144
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=ae phase=7');
|
|
145
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
146
|
+
const r = runHook('pre-tool-use', {
|
|
147
|
+
tool_name: 'Bash',
|
|
148
|
+
tool_input: { command: 'dbt build --select my_model' },
|
|
149
|
+
});
|
|
150
|
+
expect(r.parsed).toBeNull();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('PreToolUse rejects dbt run', () => {
|
|
154
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=ae phase=7');
|
|
155
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
156
|
+
const r = runHook('pre-tool-use', {
|
|
157
|
+
tool_name: 'Bash',
|
|
158
|
+
tool_input: { command: 'dbt run --select my_model' },
|
|
159
|
+
});
|
|
160
|
+
expect(r.parsed).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('PreToolUse approves SELECT-only psql -c', () => {
|
|
164
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
165
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
166
|
+
const r = runHook('pre-tool-use', {
|
|
167
|
+
tool_name: 'Bash',
|
|
168
|
+
tool_input: { command: 'psql -c "SELECT count(*) FROM events"' },
|
|
169
|
+
});
|
|
170
|
+
expect(r.parsed && r.parsed.hookSpecificOutput).toBeTruthy();
|
|
171
|
+
expect(r.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('PreToolUse rejects INSERT via psql', () => {
|
|
175
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
176
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
177
|
+
const r = runHook('pre-tool-use', {
|
|
178
|
+
tool_name: 'Bash',
|
|
179
|
+
tool_input: { command: 'psql -c "INSERT INTO events VALUES (1)"' },
|
|
180
|
+
});
|
|
181
|
+
expect(r.parsed).toBeNull();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('Gate open suspends auto-verify (gates win)', () => {
|
|
185
|
+
// Open both auto-verify and gate
|
|
186
|
+
const t1 = writeTranscript('::AUTO-VERIFY:: agent=ae phase=7 tool_budget=5');
|
|
187
|
+
runHook('stop', { transcript_path: t1, session_id: 's1' });
|
|
188
|
+
const t2 = writeTranscript(
|
|
189
|
+
'::GATE:: id=test-gate phase=7 kind=phase agent=ae\nbody\n::ENDGATE::'
|
|
190
|
+
);
|
|
191
|
+
runHook('stop', { transcript_path: t2, session_id: 's1' });
|
|
192
|
+
expect(readGateState().open).toBe(true);
|
|
193
|
+
expect(readAutoState().open).toBe(true);
|
|
194
|
+
|
|
195
|
+
// Tool call should be BLOCKED by gate (not auto-approved). The hook
|
|
196
|
+
// returns the canonical PreToolUse contract — hookSpecificOutput.permission
|
|
197
|
+
// Decision: 'deny' — not the legacy `decision: 'block'` form.
|
|
198
|
+
const r = runHook('pre-tool-use', {
|
|
199
|
+
tool_name: 'Bash',
|
|
200
|
+
tool_input: { command: 'dbt show --select my_model' },
|
|
201
|
+
// Same session that opened the gate — under the v2 per-session model the
|
|
202
|
+
// gate only blocks its own session, so the session_id must match.
|
|
203
|
+
session_id: 's1',
|
|
204
|
+
});
|
|
205
|
+
expect(r.parsed && r.parsed.hookSpecificOutput).toBeTruthy();
|
|
206
|
+
expect(r.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
207
|
+
expect(r.parsed.hookSpecificOutput.permissionDecisionReason).toMatch(/GATE-BLOCK/);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('Tool budget exhaustion closes block', () => {
|
|
211
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3 tool_budget=2');
|
|
212
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
213
|
+
// Use 2 approvals
|
|
214
|
+
for (let i = 0; i < 2; i++) {
|
|
215
|
+
runHook('pre-tool-use', {
|
|
216
|
+
tool_name: 'Bash', tool_input: { command: 'dbt show' },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Third call: budget is 0 → isExpired returns true → block closes, falls through
|
|
220
|
+
const r = runHook('pre-tool-use', {
|
|
221
|
+
tool_name: 'Bash', tool_input: { command: 'dbt show' },
|
|
222
|
+
});
|
|
223
|
+
expect(r.parsed).toBeNull();
|
|
224
|
+
expect(readAutoState().open).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('SHARDS_AUTO_VERIFY=0 disables the branch', () => {
|
|
228
|
+
// Open without env disabled
|
|
229
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
230
|
+
runHook('stop', { transcript_path: t, session_id: 's1' }, { SHARDS_AUTO_VERIFY: '0' });
|
|
231
|
+
// State should NOT have been written
|
|
232
|
+
expect(readAutoState().open).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('User halt prompt closes auto-verify', () => {
|
|
236
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
237
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
238
|
+
expect(readAutoState().open).toBe(true);
|
|
239
|
+
runHook('user-prompt-submit', { prompt: 'stop, hold on a moment' });
|
|
240
|
+
expect(readAutoState().open).toBe(false);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('Compound bash command is rejected (no auto-approve on rm via &&)', () => {
|
|
244
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
245
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
246
|
+
const r = runHook('pre-tool-use', {
|
|
247
|
+
tool_name: 'Bash',
|
|
248
|
+
tool_input: { command: 'dbt show && rm -rf /tmp/foo' },
|
|
249
|
+
});
|
|
250
|
+
expect(r.parsed).toBeNull();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('Read tool always auto-approves when block open', () => {
|
|
254
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
255
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
256
|
+
const r = runHook('pre-tool-use', {
|
|
257
|
+
tool_name: 'Read',
|
|
258
|
+
tool_input: { file_path: '/x' },
|
|
259
|
+
});
|
|
260
|
+
expect(r.parsed && r.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('Write tool blocked even with auto-verify open', () => {
|
|
264
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
265
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
266
|
+
const r = runHook('pre-tool-use', {
|
|
267
|
+
tool_name: 'Write',
|
|
268
|
+
tool_input: { file_path: '/x' },
|
|
269
|
+
});
|
|
270
|
+
expect(r.parsed).toBeNull();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('Auto-verify clamps tool_budget to AUTO_MAX_TOOL_BUDGET', () => {
|
|
274
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3 tool_budget=999');
|
|
275
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
276
|
+
const s = readAutoState();
|
|
277
|
+
expect(s.tool_budget_initial).toBe(50);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('reads real Claude Code transcript shape ({type, message: {role, content}})', () => {
|
|
281
|
+
// This is the canonical regression test — the actual transcript shape Claude
|
|
282
|
+
// Code writes. transcript.js previously only matched the legacy {role, content}
|
|
283
|
+
// shape, which silently broke gate + auto-verify enforcement.
|
|
284
|
+
const transcript = writeTranscript(
|
|
285
|
+
'::AUTO-VERIFY:: agent=data-modeller phase=6 tool_budget=5',
|
|
286
|
+
'real'
|
|
287
|
+
);
|
|
288
|
+
runHook('stop', { transcript_path: transcript, session_id: 's1' });
|
|
289
|
+
const s = readAutoState();
|
|
290
|
+
expect(s.open).toBe(true);
|
|
291
|
+
expect(s.tool_budget_remaining).toBe(5);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('reads legacy transcript shape for backward compat', () => {
|
|
295
|
+
const transcript = writeTranscript(
|
|
296
|
+
'::AUTO-VERIFY:: agent=data-modeller phase=6 tool_budget=5',
|
|
297
|
+
'legacy'
|
|
298
|
+
);
|
|
299
|
+
runHook('stop', { transcript_path: transcript, session_id: 's1' });
|
|
300
|
+
const s = readAutoState();
|
|
301
|
+
expect(s.open).toBe(true);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('open+close in same message: end state is closed', () => {
|
|
305
|
+
const transcript = writeTranscript(
|
|
306
|
+
'::AUTO-VERIFY:: agent=da phase=3 tool_budget=5\nverification done\n::ENDAUTO::'
|
|
307
|
+
);
|
|
308
|
+
runHook('stop', { transcript_path: transcript, session_id: 's1' });
|
|
309
|
+
const s = readAutoState();
|
|
310
|
+
expect(s.open).toBe(false);
|
|
311
|
+
expect(Array.isArray(s.history) && s.history.length >= 1).toBe(true);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('close+open in same message: end state is open (re-opened)', () => {
|
|
315
|
+
// First open a block in turn 1
|
|
316
|
+
const t1 = writeTranscript('::AUTO-VERIFY:: agent=da phase=3 tool_budget=5');
|
|
317
|
+
runHook('stop', { transcript_path: t1, session_id: 's1' });
|
|
318
|
+
expect(readAutoState().open).toBe(true);
|
|
319
|
+
// Turn 2 closes the previous and opens a new one
|
|
320
|
+
const t2 = writeTranscript(
|
|
321
|
+
'::ENDAUTO::\n\n::AUTO-VERIFY:: agent=da phase=3 tool_budget=10'
|
|
322
|
+
);
|
|
323
|
+
runHook('stop', { transcript_path: t2, session_id: 's1' });
|
|
324
|
+
const s = readAutoState();
|
|
325
|
+
expect(s.open).toBe(true);
|
|
326
|
+
expect(s.tool_budget_initial).toBe(10);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('audit log written to .shards/auto/history.jsonl', () => {
|
|
330
|
+
const t = writeTranscript('::AUTO-VERIFY:: agent=da phase=3');
|
|
331
|
+
runHook('stop', { transcript_path: t, session_id: 's1' });
|
|
332
|
+
runHook('pre-tool-use', {
|
|
333
|
+
tool_name: 'Bash',
|
|
334
|
+
tool_input: { command: 'dbt show --select x' },
|
|
335
|
+
});
|
|
336
|
+
const log = path.join(tmp, '.shards', 'auto', 'history.jsonl');
|
|
337
|
+
expect(fs.existsSync(log)).toBe(true);
|
|
338
|
+
const lines = fs.readFileSync(log, 'utf8').trim().split('\n');
|
|
339
|
+
const events = lines.map(l => JSON.parse(l));
|
|
340
|
+
expect(events.some(e => e.event === 'opened')).toBe(true);
|
|
341
|
+
expect(events.some(e => e.event === 'auto-approved' && e.tool === 'Bash')).toBe(true);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Hardcoded read-only allowlist for auto-verify mode.
|
|
2
|
+
//
|
|
3
|
+
// Returns true ONLY for tool calls that are confirmed safe verification
|
|
4
|
+
// operations. Everything else falls through to the normal permission prompt.
|
|
5
|
+
//
|
|
6
|
+
// This list is intentionally NOT user-configurable. Users can extend the
|
|
7
|
+
// session-wide allowlist via .claude/settings.json permissions.allow[]
|
|
8
|
+
// (which Claude Code already honors before the hook fires). Auto-verify
|
|
9
|
+
// is for the *additional* operations agents do during bulk verification
|
|
10
|
+
// stretches that would otherwise spam prompts.
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const sqlGuard = require('./sql-guard.js');
|
|
14
|
+
|
|
15
|
+
// Tools that are always read-only.
|
|
16
|
+
const READ_ONLY_TOOLS = new Set([
|
|
17
|
+
'Read', 'Glob', 'Grep', 'WebSearch',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
// Bash command prefixes that are read-only verification operations.
|
|
21
|
+
// Matched as: command.trim() === prefix || command.startsWith(prefix + ' ')
|
|
22
|
+
// Use the most specific prefix (e.g. "dbt show" not just "dbt") so that
|
|
23
|
+
// destructive subcommands like "dbt run" or "dbt build" are NOT matched.
|
|
24
|
+
const READ_ONLY_BASH_PREFIXES = [
|
|
25
|
+
// dbt — read-only subcommands only
|
|
26
|
+
'dbt show',
|
|
27
|
+
'dbt ls',
|
|
28
|
+
'dbt list',
|
|
29
|
+
'dbt parse',
|
|
30
|
+
'dbt compile',
|
|
31
|
+
'dbt deps',
|
|
32
|
+
'dbt debug',
|
|
33
|
+
'dbt source freshness',
|
|
34
|
+
|
|
35
|
+
// BigQuery CLI — metadata reads
|
|
36
|
+
'bq show',
|
|
37
|
+
'bq ls',
|
|
38
|
+
'bq head',
|
|
39
|
+
|
|
40
|
+
// git — already in the readonly preset, but listed here too as a
|
|
41
|
+
// belt-and-braces guarantee for users who edited their settings.json.
|
|
42
|
+
// Only read-only subcommand forms: bare `git branch`/`git tag`/`git remote`
|
|
43
|
+
// also match -d/-D/-m/-M, add/remove/set-url, and positional-arg creation.
|
|
44
|
+
'git status',
|
|
45
|
+
'git log',
|
|
46
|
+
'git diff',
|
|
47
|
+
'git show',
|
|
48
|
+
'git rev-parse',
|
|
49
|
+
'git stash list',
|
|
50
|
+
'git branch --list',
|
|
51
|
+
'git branch -a',
|
|
52
|
+
'git branch -r',
|
|
53
|
+
'git branch -v',
|
|
54
|
+
'git branch -vv',
|
|
55
|
+
'git branch --remotes',
|
|
56
|
+
'git branch --merged',
|
|
57
|
+
'git branch --no-merged',
|
|
58
|
+
'git branch --show-current',
|
|
59
|
+
'git branch --contains',
|
|
60
|
+
'git tag --list',
|
|
61
|
+
'git tag -l',
|
|
62
|
+
'git remote -v',
|
|
63
|
+
'git remote show',
|
|
64
|
+
'git remote get-url',
|
|
65
|
+
|
|
66
|
+
// python/pip metadata
|
|
67
|
+
'pip list',
|
|
68
|
+
'pip show',
|
|
69
|
+
'pip freeze',
|
|
70
|
+
|
|
71
|
+
// node/npm metadata
|
|
72
|
+
'npm ls',
|
|
73
|
+
'npm list',
|
|
74
|
+
'npm outdated',
|
|
75
|
+
'npm view',
|
|
76
|
+
|
|
77
|
+
// generic file inspection
|
|
78
|
+
'ls', 'cat', 'head', 'tail', 'wc', 'file', 'stat',
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
// Bash patterns that indicate destructive intent. Even if a more permissive
|
|
82
|
+
// prefix matches above, these veto the auto-approval. Belt-and-braces.
|
|
83
|
+
const DESTRUCTIVE_MARKERS = [
|
|
84
|
+
/\brm\b/, /\bmv\b/, /\bcp\s+-/, // basic destructive
|
|
85
|
+
/>\s*[^|]/, // shell redirect to file
|
|
86
|
+
/>>\s*/, // append redirect
|
|
87
|
+
/\bsudo\b/,
|
|
88
|
+
/\bcurl\b/, /\bwget\b/, // network fetches that may exfiltrate
|
|
89
|
+
/\|\s*sh\b/, /\|\s*bash\b/, // pipe-to-shell
|
|
90
|
+
/\$\([^)]/, // command substitution — bail (could hide anything)
|
|
91
|
+
/`[^`]/, // backtick command substitution
|
|
92
|
+
// find — -delete and -exec/-execdir/-ok/-okdir run arbitrary (often
|
|
93
|
+
// destructive) work with no `rm`/`;` present to trip the other markers;
|
|
94
|
+
// -fprint/-fprint0/-fprintf/-fls write output to a file (path-controlled
|
|
95
|
+
// write). `-ok` must not match `-okdir` (its exec-per-file prompt sibling),
|
|
96
|
+
// and `-fprint` must not match `-fprintf`, so each is listed separately;
|
|
97
|
+
// `-printf`/`-ls`/`-print0` (stdout-only forms) stay read-only.
|
|
98
|
+
/\s-delete\b/,
|
|
99
|
+
/\s-exec\b/,
|
|
100
|
+
/\s-execdir\b/,
|
|
101
|
+
/\s-ok\b/,
|
|
102
|
+
/\s-okdir\b/,
|
|
103
|
+
/\s-fprint(?:0)?\b/,
|
|
104
|
+
/\s-fprintf\b/,
|
|
105
|
+
/\s-fls\b/,
|
|
106
|
+
// git branch — the `-v`/`-vv` listing forms silently become CREATES when a
|
|
107
|
+
// bare branch name follows (`git branch -v feature` creates `feature`).
|
|
108
|
+
// Listing with `-v`/`-vv` never takes a positional, so any non-option token
|
|
109
|
+
// right after them is a create intent.
|
|
110
|
+
/\bgit\s+branch\s+-v+\s+[^\s-]/,
|
|
111
|
+
// git diff/show/log — --output=FILE / --output FILE / -o FILE silently
|
|
112
|
+
// writes a patch file. `--output-indicator-*` (a read-only diff styling
|
|
113
|
+
// flag) must not match.
|
|
114
|
+
/--output(?=\s|=)/,
|
|
115
|
+
/\bgit\s+(?:diff|show|log)\b[^\n]*\s-o(?=\s*\S)/,
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
// Compound separator detection — a single allow shouldn't authorize
|
|
119
|
+
// `safe-cmd && rm -rf /`. If we see compound separators, bail.
|
|
120
|
+
// Covers `&&`, `||`, `;`, `|`, a lone backgrounding `&` (`cat x & rm ...`),
|
|
121
|
+
// and a literal newline (a command separator in bash when the model emits a
|
|
122
|
+
// multi-line tool call). `&&`/`||` are matched by their own alternatives so
|
|
123
|
+
// lone `&`/`|` (e.g. inside `$((..))` arithmetic) can't double-match.
|
|
124
|
+
const COMPOUND_SEPARATORS = /(\&\&|\|\||[;&\n]|\|(?!\|))/;
|
|
125
|
+
|
|
126
|
+
function isAutoApprovable(toolName, toolInput) {
|
|
127
|
+
if (!toolName) return false;
|
|
128
|
+
|
|
129
|
+
// Always-safe tools
|
|
130
|
+
if (READ_ONLY_TOOLS.has(toolName)) return true;
|
|
131
|
+
|
|
132
|
+
if (toolName !== 'Bash') return false;
|
|
133
|
+
if (!toolInput || typeof toolInput.command !== 'string') return false;
|
|
134
|
+
|
|
135
|
+
const cmd = toolInput.command.trim();
|
|
136
|
+
if (!cmd) return false;
|
|
137
|
+
|
|
138
|
+
// Reject compound commands outright. A user-facing rule should match each
|
|
139
|
+
// subcommand independently; we don't try to split-and-recheck because that
|
|
140
|
+
// gets risky. Agents doing verification should run one statement at a time.
|
|
141
|
+
if (COMPOUND_SEPARATORS.test(cmd)) {
|
|
142
|
+
// Special case: SQL guard already handles `;`-separated SELECT statements
|
|
143
|
+
// inside a quoted argument. The compound check fires on bare shell `;`.
|
|
144
|
+
// SQL invocations have the `;` *inside* a quoted SQL string, so the bare
|
|
145
|
+
// shell-level check would match. We give SQL guard first crack:
|
|
146
|
+
if (sqlGuard.isReadOnlyCliInvocation(cmd)) return true;
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Destructive markers veto.
|
|
151
|
+
for (const re of DESTRUCTIVE_MARKERS) {
|
|
152
|
+
if (re.test(cmd)) {
|
|
153
|
+
// Same SQL-guard exception: a SELECT may legitimately reference a column
|
|
154
|
+
// called `rm` or contain `>`. But destructive markers in non-SQL Bash
|
|
155
|
+
// commands are hard veto.
|
|
156
|
+
if (sqlGuard.isReadOnlyCliInvocation(cmd)) return true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Prefix match against the read-only list.
|
|
162
|
+
for (const prefix of READ_ONLY_BASH_PREFIXES) {
|
|
163
|
+
if (cmd === prefix) return true;
|
|
164
|
+
if (cmd.startsWith(prefix + ' ')) return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// bq query --dry_run is a metadata-only call.
|
|
168
|
+
if (/^bq\s+query\b/.test(cmd) && /--dry_run\b/.test(cmd)) {
|
|
169
|
+
// Dry-run still doesn't write. Allow.
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// SELECT-only warehouse-CLI SQL.
|
|
174
|
+
if (sqlGuard.isReadOnlyCliInvocation(cmd)) return true;
|
|
175
|
+
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { isAutoApprovable, READ_ONLY_BASH_PREFIXES, READ_ONLY_TOOLS };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Auto-verify state read/write for .shards/auto/state.json
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const AUTO_DIR = path.join(process.cwd(), '.shards', 'auto');
|
|
8
|
+
const STATE = path.join(AUTO_DIR, 'state.json');
|
|
9
|
+
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
fs.mkdirSync(AUTO_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function read() {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(fs.readFileSync(STATE, 'utf8'));
|
|
17
|
+
} catch {
|
|
18
|
+
return { open: false, history: [] };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Atomic write via tmp + rename. See state.js for rationale. Concurrent UI
|
|
23
|
+
// polls and hook invocations would otherwise observe partial JSON during a
|
|
24
|
+
// bare writeFileSync and silently fall back to { open: false }.
|
|
25
|
+
function write(s) {
|
|
26
|
+
ensureDir();
|
|
27
|
+
const tmp = `${STATE}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(tmp, JSON.stringify(s, null, 2));
|
|
30
|
+
fs.renameSync(tmp, STATE);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Returns true if the auto-verify block has expired (ttl elapsed or budget
|
|
38
|
+
// exhausted). Caller is responsible for closing the state when this returns
|
|
39
|
+
// true.
|
|
40
|
+
function isExpired(s) {
|
|
41
|
+
if (!s || !s.open) return false;
|
|
42
|
+
if (typeof s.tool_budget_remaining === 'number' && s.tool_budget_remaining <= 0) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (s.expires_at) {
|
|
46
|
+
return new Date(s.expires_at).getTime() <= Date.now();
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function close(s, reason) {
|
|
52
|
+
const closed_at = new Date().toISOString();
|
|
53
|
+
const historyEntry = {
|
|
54
|
+
id: s.id,
|
|
55
|
+
agent: s.agent,
|
|
56
|
+
phase: s.phase,
|
|
57
|
+
opened_at: s.opened_at,
|
|
58
|
+
closed_at,
|
|
59
|
+
closed_reason: reason,
|
|
60
|
+
approvals_used: (s.tool_budget_initial || 0) - (s.tool_budget_remaining || 0),
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
open: false,
|
|
64
|
+
history: [...(s.history || []), historyEntry],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { read, write, isExpired, close, STATE, AUTO_DIR };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Classify user prompts as confirm / deny / ambiguous
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const CONFIRM_RE = /^\s*(y|yes|yep|yeah|yup|confirm(ed)?|proceed|go|go ahead|approved|lgtm|ship it|ok|okay|sounds good|looks good|continue|next|advance|move on)\b/i;
|
|
5
|
+
const DENY_RE = /^\s*(n|no|nope|stop|hold|wait|change|revise|actually|not quite|let me)\b/i;
|
|
6
|
+
|
|
7
|
+
// ::GATE-CONFIRM:: <id> — explicit confirm from advanced users
|
|
8
|
+
const EXPLICIT_CONFIRM_RE = /^\s*::GATE-CONFIRM::\s+(\S+)/i;
|
|
9
|
+
|
|
10
|
+
function classify(prompt) {
|
|
11
|
+
if (!prompt || !prompt.trim()) return 'ambiguous';
|
|
12
|
+
|
|
13
|
+
const m = EXPLICIT_CONFIRM_RE.exec(prompt);
|
|
14
|
+
if (m) return 'confirm';
|
|
15
|
+
|
|
16
|
+
if (CONFIRM_RE.test(prompt)) return 'confirm';
|
|
17
|
+
if (DENY_RE.test(prompt)) return 'deny';
|
|
18
|
+
return 'ambiguous';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { classify };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Append entries to violations.jsonl / gate-history.jsonl
|
|
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
|
+
|
|
9
|
+
function ensureDir(dir) {
|
|
10
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Atomic-append: open with O_APPEND and write the full JSON line in a single
|
|
14
|
+
// syscall. POSIX guarantees that writes <= PIPE_BUF (typically 4096 bytes) to
|
|
15
|
+
// a file opened with O_APPEND are atomic with respect to other O_APPEND
|
|
16
|
+
// writers — concurrent gate-hook invocations cannot interleave bytes within a
|
|
17
|
+
// single line. JSONL entries here are well under PIPE_BUF.
|
|
18
|
+
//
|
|
19
|
+
// We deliberately do NOT use fs.appendFileSync here because Node's
|
|
20
|
+
// implementation can fall back to non-atomic open/write/close patterns under
|
|
21
|
+
// some conditions. Doing the open + writeSync + close explicitly is a
|
|
22
|
+
// belt-and-braces guarantee.
|
|
23
|
+
function atomicAppendLine(file, line) {
|
|
24
|
+
let fd;
|
|
25
|
+
try {
|
|
26
|
+
fd = fs.openSync(file, 'a');
|
|
27
|
+
fs.writeSync(fd, line);
|
|
28
|
+
} finally {
|
|
29
|
+
if (typeof fd === 'number') {
|
|
30
|
+
try { fs.closeSync(fd); } catch {}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function appendViolation(entry) {
|
|
36
|
+
ensureDir(GATE_DIR);
|
|
37
|
+
const file = path.join(GATE_DIR, 'violations.jsonl');
|
|
38
|
+
const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
|
|
39
|
+
atomicAppendLine(file, line);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function appendHistory(entry) {
|
|
43
|
+
ensureDir(GATE_DIR);
|
|
44
|
+
const file = path.join(GATE_DIR, 'gates.jsonl');
|
|
45
|
+
const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
|
|
46
|
+
atomicAppendLine(file, line);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function appendAutoHistory(entry) {
|
|
50
|
+
const dir = path.join(process.cwd(), '.shards', 'auto');
|
|
51
|
+
ensureDir(dir);
|
|
52
|
+
const file = path.join(dir, 'history.jsonl');
|
|
53
|
+
const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
|
|
54
|
+
atomicAppendLine(file, line);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { appendViolation, appendHistory, appendAutoHistory };
|