@r1z4x/tezgah 1.9.2 → 1.13.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/CHANGELOG.md +415 -1
- package/MANIFEST +404 -144
- package/README.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.md +5 -4
- package/README.tr.md +1 -1
- package/bin/codegen +5 -2
- package/bin/tezgah-design +288 -1
- package/bin/tezgah-docs +1 -1
- package/bin/tezgah-doctor +39 -2
- package/bin/tezgah-import-ai-research +26 -12
- package/bin/tezgah-ocr-review +296 -0
- package/bin/tezgah-research +255 -44
- package/bin/tezgah-review-quote +185 -0
- package/bin/tezgah-setup +1365 -55
- package/bin/tezgah-skill +1 -1
- package/bin/tezgah-status +201 -3
- package/bin/tezgah-task +258 -20
- package/commands/adhd.md +2 -2
- package/commands/ponytail.md +4 -4
- package/docs/architecture.md +2 -2
- package/docs/citations-baseline.json +3 -3
- package/docs/clarity-baseline.json +11 -5
- package/docs/contract.md +91 -43
- package/docs/environment.md +5 -2
- package/docs/evidence.md +109 -17
- package/docs/feature-audit.md +2 -2
- package/docs/gate.md +60 -33
- package/docs/glossary.md +6 -3
- package/docs/hosts.md +313 -17
- package/docs/index.json +34 -8
- package/docs/judge.md +4 -2
- package/docs/layers.md +4 -4
- package/docs/models.md +37 -9
- package/docs/operations.md +141 -5
- package/docs/research.md +815 -8
- package/docs/skills.md +137 -13
- package/docs/status-line.md +121 -1
- package/docs/testing.md +3 -1
- package/hooks/hooks.json +2 -2
- package/hooks/lang/en.json +21 -0
- package/hooks/lang/tr.json +22 -1
- package/hooks/projects-stop.py +2 -1
- package/hooks/tezgah_agents.py +65 -25
- package/hooks/tezgah_apps.py +18 -0
- package/hooks/tezgah_attest.py +79 -10
- package/hooks/tezgah_components.py +4 -0
- package/hooks/tezgah_context.py +207 -55
- package/hooks/tezgah_extension.py +337 -0
- package/hooks/tezgah_gate.py +267 -79
- package/hooks/tezgah_integrity.py +864 -143
- package/hooks/tezgah_judge.py +6 -4
- package/hooks/tezgah_models.py +59 -19
- package/hooks/tezgah_orca.py +1 -1
- package/hooks/tezgah_paths.py +145 -8
- package/hooks/tezgah_policy.py +128 -57
- package/hooks/tezgah_rank.py +9 -6
- package/hooks/tezgah_replay.py +12 -6
- package/hooks/tezgah_research.py +1414 -76
- package/hooks/tezgah_research_capture.py +1544 -0
- package/hooks/tezgah_research_evidence.py +842 -0
- package/hooks/tezgah_research_stats.py +258 -0
- package/hooks/tezgah_skill_pick.py +408 -106
- package/hooks/tezgah_stack.py +236 -1
- package/hooks/tezgah_stopspec.py +8 -1
- package/hooks/tezgah_task.py +14 -5
- package/hooks/tezgah_taste_ledger.py +1 -1
- package/hosts/amp/hook.py +217 -0
- package/hosts/amp/tezgah-plugin.ts.in +386 -0
- package/hosts/antigravity/hook.py +255 -0
- package/hosts/codex/hook.py +2 -1
- package/hosts/commandcode/hook.py +239 -0
- package/hosts/copilot/hook.py +284 -0
- package/hosts/cursor/hook.py +31 -3
- package/hosts/droid/hook.py +259 -0
- package/hosts/dsh/hooks.json +1 -1
- package/hosts/gemini/hook.py +213 -0
- package/hosts/goose/hook.py +145 -0
- package/hosts/omp/hook.py +4 -1
- package/hosts/omp/tezgah-hook.ts.in +30 -7
- package/hosts/opencode/plugins/tezgah.js +309 -48
- package/hosts/opencode/tui/cli/tui.tsx +100 -0
- package/hosts/opencode/tui/tezgah-tui.tsx +2 -1
- package/hosts/qwen/hook.py +206 -0
- package/output-styles/tezgah.md +20 -15
- package/package.json +1 -1
- package/skills/ai-research/SKILL.md +21 -14
- package/skills/ai-research/SOURCE +7 -5
- package/skills/ai-research/index/1-frame.md +6 -6
- package/skills/ai-research/index/2-data.md +9 -9
- package/skills/ai-research/index/3-train.md +36 -36
- package/skills/ai-research/index/4-measure.md +13 -13
- package/skills/ai-research/index/5-run.md +11 -11
- package/skills/ai-research/index/6-write.md +23 -23
- package/skills/ai-research/library.json +104 -104
- package/skills/analyze-app/SKILL.md +17 -12
- package/skills/design-contract/SKILL.md +42 -7
- package/skills/design-contract/data-views.md +2 -2
- package/skills/design-library/EVALS.md +1 -1
- package/skills/design-library/INDEX.md +39 -39
- package/skills/design-library/SKILL.md +19 -13
- package/skills/design-library/SOURCE +45 -43
- package/skills/feature-audit/SKILL.md +20 -14
- package/skills/hallmark/LICENSE +21 -0
- package/skills/hallmark/SKILL.md +154 -0
- package/skills/hallmark/SOURCE +207 -0
- package/skills/hallmark/agents/openai.yaml +13 -0
- package/skills/hallmark/references/anatomy.md +71 -0
- package/skills/hallmark/references/brand-truth.md +40 -0
- package/skills/hallmark/references/comp.md +104 -0
- package/skills/hallmark/references/craft.md +116 -0
- package/skills/hallmark/references/dark-mode.md +49 -0
- package/skills/hallmark/references/data-viz.md +48 -0
- package/skills/hallmark/references/design-md.md +53 -0
- package/skills/hallmark/references/direction.md +52 -0
- package/skills/hallmark/references/export-formats.md +80 -0
- package/skills/hallmark/references/flow.md +131 -0
- package/skills/hallmark/references/fonts.md +58 -0
- package/skills/hallmark/references/gates.md +243 -0
- package/skills/hallmark/references/genres/atmospheric.md +24 -0
- package/skills/hallmark/references/genres/editorial.md +24 -0
- package/skills/hallmark/references/genres/modern-minimal.md +24 -0
- package/skills/hallmark/references/genres/playful.md +24 -0
- package/skills/hallmark/references/imagery.md +99 -0
- package/skills/hallmark/references/interaction-and-states.md +89 -0
- package/skills/hallmark/references/layout.md +104 -0
- package/skills/hallmark/references/macrostructures/01-bento-grid.md +21 -0
- package/skills/hallmark/references/macrostructures/02-long-document.md +35 -0
- package/skills/hallmark/references/macrostructures/03-marquee-hero.md +21 -0
- package/skills/hallmark/references/macrostructures/04-stat-led.md +21 -0
- package/skills/hallmark/references/macrostructures/05-workbench.md +21 -0
- package/skills/hallmark/references/macrostructures/06-conversational-faq.md +28 -0
- package/skills/hallmark/references/macrostructures/07-manifesto.md +28 -0
- package/skills/hallmark/references/macrostructures/08-photographic.md +21 -0
- package/skills/hallmark/references/macrostructures/09-quote-led.md +21 -0
- package/skills/hallmark/references/macrostructures/10-specimen.md +21 -0
- package/skills/hallmark/references/macrostructures/11-catalogue.md +21 -0
- package/skills/hallmark/references/macrostructures/12-letter.md +21 -0
- package/skills/hallmark/references/macrostructures/13-index-first.md +21 -0
- package/skills/hallmark/references/macrostructures/14-narrative-workflow.md +21 -0
- package/skills/hallmark/references/macrostructures/15-split-studio.md +21 -0
- package/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
- package/skills/hallmark/references/macrostructures/17-type-specimen.md +19 -0
- package/skills/hallmark/references/macrostructures/18-portfolio-grid.md +21 -0
- package/skills/hallmark/references/macrostructures/19-map-diagram.md +21 -0
- package/skills/hallmark/references/macrostructures/20-ecosystem-index.md +19 -0
- package/skills/hallmark/references/macrostructures/21-component-playground.md +21 -0
- package/skills/hallmark/references/macrostructures.md +119 -0
- package/skills/hallmark/references/microinteractions.md +80 -0
- package/skills/hallmark/references/patterns.md +202 -0
- package/skills/hallmark/references/redesign.md +88 -0
- package/skills/hallmark/references/responsive.md +46 -0
- package/skills/hallmark/references/review-finish.md +19 -0
- package/skills/hallmark/references/review-hero.md +20 -0
- package/skills/hallmark/references/scroll-choreography.md +110 -0
- package/skills/hallmark/references/texture.md +90 -0
- package/skills/hallmark/references/themes/README.md +58 -0
- package/skills/hallmark/references/themes/almanac.jpg +0 -0
- package/skills/hallmark/references/themes/almanac.md +103 -0
- package/skills/hallmark/references/themes/arcade.jpg +0 -0
- package/skills/hallmark/references/themes/arcade.md +102 -0
- package/skills/hallmark/references/themes/atelier.jpg +0 -0
- package/skills/hallmark/references/themes/atelier.md +98 -0
- package/skills/hallmark/references/themes/bloom.jpg +0 -0
- package/skills/hallmark/references/themes/bloom.md +102 -0
- package/skills/hallmark/references/themes/blueprint.jpg +0 -0
- package/skills/hallmark/references/themes/blueprint.md +103 -0
- package/skills/hallmark/references/themes/broadside.jpg +0 -0
- package/skills/hallmark/references/themes/broadside.md +108 -0
- package/skills/hallmark/references/themes/brutal.jpg +0 -0
- package/skills/hallmark/references/themes/brutal.md +95 -0
- package/skills/hallmark/references/themes/carnival.jpg +0 -0
- package/skills/hallmark/references/themes/carnival.md +104 -0
- package/skills/hallmark/references/themes/cobalt.jpg +0 -0
- package/skills/hallmark/references/themes/cobalt.md +101 -0
- package/skills/hallmark/references/themes/console.jpg +0 -0
- package/skills/hallmark/references/themes/console.md +99 -0
- package/skills/hallmark/references/themes/coral.jpg +0 -0
- package/skills/hallmark/references/themes/coral.md +102 -0
- package/skills/hallmark/references/themes/darkroom.jpg +0 -0
- package/skills/hallmark/references/themes/darkroom.md +99 -0
- package/skills/hallmark/references/themes/dose.jpg +0 -0
- package/skills/hallmark/references/themes/dose.md +98 -0
- package/skills/hallmark/references/themes/dusk.jpg +0 -0
- package/skills/hallmark/references/themes/dusk.md +105 -0
- package/skills/hallmark/references/themes/editorial.jpg +0 -0
- package/skills/hallmark/references/themes/editorial.md +97 -0
- package/skills/hallmark/references/themes/formwork.jpg +0 -0
- package/skills/hallmark/references/themes/formwork.md +107 -0
- package/skills/hallmark/references/themes/furrow.jpg +0 -0
- package/skills/hallmark/references/themes/furrow.md +98 -0
- package/skills/hallmark/references/themes/garden.jpg +0 -0
- package/skills/hallmark/references/themes/garden.md +97 -0
- package/skills/hallmark/references/themes/grid.jpg +0 -0
- package/skills/hallmark/references/themes/grid.md +101 -0
- package/skills/hallmark/references/themes/halftone.jpg +0 -0
- package/skills/hallmark/references/themes/halftone.md +106 -0
- package/skills/hallmark/references/themes/hedgerow.jpg +0 -0
- package/skills/hallmark/references/themes/hedgerow.md +104 -0
- package/skills/hallmark/references/themes/hum.jpg +0 -0
- package/skills/hallmark/references/themes/hum.md +99 -0
- package/skills/hallmark/references/themes/index.json +2191 -0
- package/skills/hallmark/references/themes/ink.jpg +0 -0
- package/skills/hallmark/references/themes/ink.md +106 -0
- package/skills/hallmark/references/themes/kiln.jpg +0 -0
- package/skills/hallmark/references/themes/kiln.md +108 -0
- package/skills/hallmark/references/themes/loom.jpg +0 -0
- package/skills/hallmark/references/themes/loom.md +106 -0
- package/skills/hallmark/references/themes/lumen.jpg +0 -0
- package/skills/hallmark/references/themes/lumen.md +103 -0
- package/skills/hallmark/references/themes/manifesto.jpg +0 -0
- package/skills/hallmark/references/themes/manifesto.md +101 -0
- package/skills/hallmark/references/themes/midnight.jpg +0 -0
- package/skills/hallmark/references/themes/midnight.md +97 -0
- package/skills/hallmark/references/themes/newsprint.jpg +0 -0
- package/skills/hallmark/references/themes/newsprint.md +102 -0
- package/skills/hallmark/references/themes/overprint.jpg +0 -0
- package/skills/hallmark/references/themes/overprint.md +101 -0
- package/skills/hallmark/references/themes/pasteup.jpg +0 -0
- package/skills/hallmark/references/themes/pasteup.md +100 -0
- package/skills/hallmark/references/themes/piste.jpg +0 -0
- package/skills/hallmark/references/themes/piste.md +99 -0
- package/skills/hallmark/references/themes/ribbon.jpg +0 -0
- package/skills/hallmark/references/themes/ribbon.md +106 -0
- package/skills/hallmark/references/themes/rim.jpg +0 -0
- package/skills/hallmark/references/themes/rim.md +96 -0
- package/skills/hallmark/references/themes/riso.jpg +0 -0
- package/skills/hallmark/references/themes/riso.md +98 -0
- package/skills/hallmark/references/themes/solstice.jpg +0 -0
- package/skills/hallmark/references/themes/solstice.md +104 -0
- package/skills/hallmark/references/themes/specimen.jpg +0 -0
- package/skills/hallmark/references/themes/specimen.md +100 -0
- package/skills/hallmark/references/themes/spore.jpg +0 -0
- package/skills/hallmark/references/themes/spore.md +104 -0
- package/skills/hallmark/references/themes/sport.jpg +0 -0
- package/skills/hallmark/references/themes/sport.md +101 -0
- package/skills/hallmark/references/themes/squeeze.jpg +0 -0
- package/skills/hallmark/references/themes/squeeze.md +99 -0
- package/skills/hallmark/references/themes/sticker.jpg +0 -0
- package/skills/hallmark/references/themes/sticker.md +106 -0
- package/skills/hallmark/references/themes/studio.jpg +0 -0
- package/skills/hallmark/references/themes/studio.md +99 -0
- package/skills/hallmark/references/themes/terminal.jpg +0 -0
- package/skills/hallmark/references/themes/terminal.md +96 -0
- package/skills/hallmark/references/themes/terrazzo.jpg +0 -0
- package/skills/hallmark/references/themes/terrazzo.md +106 -0
- package/skills/hallmark/references/themes/ultraviolet.jpg +0 -0
- package/skills/hallmark/references/themes/ultraviolet.md +106 -0
- package/skills/hallmark/references/themes/vellum.jpg +0 -0
- package/skills/hallmark/references/themes/vellum.md +105 -0
- package/skills/hallmark/references/themes/wayfind.jpg +0 -0
- package/skills/hallmark/references/themes/wayfind.md +105 -0
- package/skills/hallmark/references/themes/werkstatt.jpg +0 -0
- package/skills/hallmark/references/themes/werkstatt.md +105 -0
- package/skills/hallmark/scripts/capture.mjs +125 -0
- package/skills/hallmark/scripts/fixtures/exemplars/atmospheric.html +324 -0
- package/skills/hallmark/scripts/fixtures/exemplars/editorial.html +363 -0
- package/skills/hallmark/scripts/fixtures/exemplars/modern-minimal.html +527 -0
- package/skills/hallmark/scripts/fixtures/exemplars/playful.html +353 -0
- package/skills/hallmark/scripts/fixtures/eyebrow-fail/index.html +118 -0
- package/skills/hallmark/scripts/fixtures/eyebrow-pass/index.html +137 -0
- package/skills/hallmark/scripts/fixtures/kept/after.html +27 -0
- package/skills/hallmark/scripts/fixtures/kept/before.html +26 -0
- package/skills/hallmark/scripts/fixtures/slopped.html +145 -0
- package/skills/hallmark/scripts/fixtures/tailwind-fail/Feature.vue +14 -0
- package/skills/hallmark/scripts/fixtures/tailwind-fail/Hero.tsx +33 -0
- package/skills/hallmark/scripts/fixtures/tailwind-fail/index.html +74 -0
- package/skills/hallmark/scripts/fixtures/tailwind-fail/style.css +6 -0
- package/skills/hallmark/scripts/fixtures/tailwind-pass/Hero.tsx +33 -0
- package/skills/hallmark/scripts/fixtures/tailwind-pass/index.html +57 -0
- package/skills/hallmark/scripts/fixtures/tailwind-pass/style.css +15 -0
- package/skills/hallmark/scripts/fixtures/waiver/index.html +54 -0
- package/skills/hallmark/scripts/imagegen.mjs +85 -0
- package/skills/hallmark/scripts/install-hook.mjs +70 -0
- package/skills/hallmark/scripts/install.mjs +141 -0
- package/skills/hallmark/scripts/lint-hook.mjs +49 -0
- package/skills/hallmark/scripts/photos.json +1 -0
- package/skills/hallmark/scripts/picture.mjs +71 -0
- package/skills/hallmark/scripts/refcheck.mjs +352 -0
- package/skills/hallmark/scripts/sloplint-render.mjs +1252 -0
- package/skills/hallmark/scripts/sloplint.mjs +4422 -0
- package/skills/hallmark/scripts/theme.mjs +650 -0
- package/skills/harness/SKILL.md +17 -8
- package/skills/i-have-adhd/SKILL.md +32 -12
- package/skills/no-ai-slop/SKILL.md +15 -1
- package/skills/plan-add/SKILL.md +38 -27
- package/skills/plan-status/SKILL.md +13 -7
- package/skills/plan-sync/SKILL.md +18 -12
- package/skills/pm-frameworks/SKILL.md +19 -10
- package/skills/pm-frameworks/SOURCE +7 -5
- package/skills/ponytail/SKILL.md +19 -14
- package/skills/product-analysis/SKILL.md +22 -18
- package/skills/product-flow/SKILL.md +14 -9
- package/skills/research/SKILL.md +97 -48
- package/skills/rl-env/SKILL.md +25 -13
- package/skills/rl-env/SOURCE +8 -6
- package/skills/tezgah-contract/SKILL.md +65 -47
- package/statusline.py +20 -9
- package/workflows/graph-review.js +14 -2
- package/bin/tezgah-skill.py +0 -59
- /package/skills/ai-research/0-autoresearch-skill/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/01-model-architecture/litgpt/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/01-model-architecture/mamba/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/01-model-architecture/nanogpt/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/01-model-architecture/rwkv/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/01-model-architecture/torchtitan/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/02-tokenization/huggingface-tokenizers/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/02-tokenization/sentencepiece/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/03-fine-tuning/axolotl/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/03-fine-tuning/llama-factory/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/03-fine-tuning/peft/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/03-fine-tuning/unsloth/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/04-mechanistic-interpretability/nnsight/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/04-mechanistic-interpretability/pyvene/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/04-mechanistic-interpretability/saelens/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/05-data-processing/nemo-curator/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/05-data-processing/ray-data/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/grpo-rl-training/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/miles/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/openrlhf/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/simpo/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/slime/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/torchforge/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/trl-fine-tuning/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/06-post-training/verl/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/07-safety-alignment/constitutional-ai/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/07-safety-alignment/llamaguard/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/07-safety-alignment/nemo-guardrails/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/07-safety-alignment/prompt-guard/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/accelerate/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/deepspeed/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/megatron-core/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/pytorch-fsdp2/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/pytorch-lightning/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/08-distributed-training/ray-train/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/09-infrastructure/lambda-labs/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/09-infrastructure/modal/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/09-infrastructure/skypilot/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/awq/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/bitsandbytes/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/flash-attention/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/gguf/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/gptq/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/hqq/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/10-optimization/ml-training-recipes/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/11-evaluation/lm-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/11-evaluation/nemo-evaluator/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/12-inference-serving/llama-cpp/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/12-inference-serving/sglang/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/12-inference-serving/tensorrt-llm/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/12-inference-serving/vllm/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/13-mlops/mlflow/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/13-mlops/swanlab/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/13-mlops/tensorboard/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/13-mlops/weights-and-biases/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/14-agents/a-evolve/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/14-agents/autogpt/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/14-agents/crewai/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/14-agents/langchain/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/14-agents/llamaindex/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/15-rag/chroma/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/15-rag/faiss/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/15-rag/pinecone/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/15-rag/qdrant/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/15-rag/sentence-transformers/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/16-prompt-engineering/dspy/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/16-prompt-engineering/guidance/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/16-prompt-engineering/instructor/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/16-prompt-engineering/outlines/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/17-observability/langsmith/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/17-observability/phoenix/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/audiocraft/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/blip-2/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/clip/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/cosmos-policy/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/llava/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/openpi/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/openvla-oft/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/segment-anything/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/stable-diffusion/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/18-multimodal/whisper/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/knowledge-distillation/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/long-context/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/model-merging/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/model-pruning/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/moe-training/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/19-emerging-techniques/speculative-decoding/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/20-ml-paper-writing/academic-plotting/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/21-research-ideation/brainstorming-research-ideas/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/21-research-ideation/creative-thinking-for-research/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/22-agent-native-research-artifact/compiler/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/22-agent-native-research-artifact/research-manager/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/colour-independence/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/flexible-typography/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/information-density/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/preference-audit/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/responsive-accessibility/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/responsive-review/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/simplified-views/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/specify/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/adaptive-interfaces/skills/user-preference-respect/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/adaptive-personalisation/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/assess-load/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/cognitive-load-assessment/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/contextual-help-design/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/error-prevention-recovery/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/focus-attention-design/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/memory-load-reduction/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/plain-language-design/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/review/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/simplify/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/cognitive-accessibility/skills/wayfinding-navigation/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/design-research/skills/behavioural-analytics/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/design-research/skills/qual-quant-triangulation/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/audit/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/design-flow/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/feedback-and-status/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/gesture-alternatives/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/keyboard-navigation/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/keyboard-review/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/motion-sensitivity/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/multi-modal-input/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/touch-target-design/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/inclusive-interaction/skills/voice-interaction/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-affordance/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-brand-consistency/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-color/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-composition/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-information-density/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-typography/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/design-library/visual-critique/skills/critique-visual-hierarchy/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/pm-frameworks/intended-vs-implemented/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/pm-frameworks/opportunity-solution-tree/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/rl-env/generate-nemo-gym-env/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/rl-env/generate-openenv-env/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/rl-env/generate-ors-env/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/rl-env/generate-verifiers-env/{SKILL.md → ENTRY.md} +0 -0
- /package/skills/rl-env/rl-env-from-description/{SKILL.md → ENTRY.md} +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Factory Droid lifecycle hook. Reads Droid's JSON on stdin and answers in
|
|
3
|
+
Droid's own output shape, onto the shared tezgah core (hooks/).
|
|
4
|
+
|
|
5
|
+
Droid 0.251.0 fires SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
6
|
+
SubagentStop and Stop here (`bin/tezgah-setup::DROID_HOOK_EVENTS`); anything
|
|
7
|
+
else is ignored. Inert outside the roots.
|
|
8
|
+
|
|
9
|
+
The always-on core is the managed block in `~/.factory/AGENTS.md`, which Droid
|
|
10
|
+
loads as coding guidelines in every session, so SessionStart repeats it only
|
|
11
|
+
when that block is missing. PreToolUse translates Droid's tool ids into the
|
|
12
|
+
gate's vocabulary and denies with `hookSpecificOutput.permissionDecision` on
|
|
13
|
+
exit 0 - never exit 2, whose stderr channel carries no reason the ledger and the
|
|
14
|
+
probe can read. Droid has no failure event: PostToolUse fires for every
|
|
15
|
+
finished call, so a shell call's outcome is read off its result
|
|
16
|
+
(`call_failed`).
|
|
17
|
+
|
|
18
|
+
Droid's Stop event carries no reply text (`stop_hook_active`,
|
|
19
|
+
`tool_execution_count`, `elapsed_time`), so Stop reads the last assistant text
|
|
20
|
+
from `transcript_path` (`last_reply`). When that read yields nothing, the turn
|
|
21
|
+
end is recorded as a `reply_unread` row and not judged: judging an empty reply
|
|
22
|
+
would let any claim through as "no claim". SubagentStop carries the sub-droid's
|
|
23
|
+
`task_result` and is judged record-only. Droid has no SubagentStart, so a
|
|
24
|
+
delegate gets no brief. The status line is `tezgah-statusline --droid`, Droid's
|
|
25
|
+
`statusLine` command, so no event here carries it.
|
|
26
|
+
"""
|
|
27
|
+
import json
|
|
28
|
+
import os
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
33
|
+
sys.path.insert(0, os.path.join(ROOT, "hooks"))
|
|
34
|
+
from tezgah_guard import attest_session, import_failed, safe # noqa: E402
|
|
35
|
+
try:
|
|
36
|
+
from tezgah_context import ( # noqa: E402
|
|
37
|
+
_tail_lines, command_text, context_for, record, shell_kind)
|
|
38
|
+
from tezgah_gate import cross_project_note, decision, graph_advice # noqa: E402
|
|
39
|
+
from tezgah_integrity import ( # noqa: E402
|
|
40
|
+
SUBAGENT_CHANNEL, bind_session, note, note_tool, ran_nothing, report_bytes,
|
|
41
|
+
stop_reason, untrusted_source)
|
|
42
|
+
from tezgah_paths import HOST_DIRS, off, root_for # noqa: E402
|
|
43
|
+
from tezgah_taste import write_note # noqa: E402
|
|
44
|
+
from tezgah_untrusted import marks # noqa: E402
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
import_failed(exc)
|
|
47
|
+
|
|
48
|
+
# Droid's tool ids (harness/hooks "Common tool matchers", harness/subagents tool
|
|
49
|
+
# categories) -> the shared gate's vocabulary. Read, LS, Glob, Grep, Task,
|
|
50
|
+
# TodoWrite and WebSearch already carry the gate's names; MCP tools arrive as
|
|
51
|
+
# `mcp__<server>__<tool>`, the shared shape, and pass through.
|
|
52
|
+
GATE_TOOLS = {
|
|
53
|
+
"Execute": "Bash",
|
|
54
|
+
"Create": "Write",
|
|
55
|
+
"ApplyPatch": "apply_patch",
|
|
56
|
+
"FetchUrl": "WebFetch",
|
|
57
|
+
}
|
|
58
|
+
# How Droid ends a shell result: the text its transcripts store for an Execute
|
|
59
|
+
# call (`[Process exited with code 1]`). The PostToolUse `tool_response` shape is
|
|
60
|
+
# undocumented, so this is read wherever the result carries text.
|
|
61
|
+
EXIT_LINE = re.compile(r"\[Process exited with code (-?\d+)\]")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def gate_name(name):
|
|
65
|
+
"""The tool name the gate denies under and the ledger row records.
|
|
66
|
+
|
|
67
|
+
`call_id` hashes the name it is handed, so both halves map through here: a
|
|
68
|
+
PostToolUse row under the raw id would never match the PreToolUse call and
|
|
69
|
+
the loop guard would never deny (`hosts/codex/hook.py::gate_name`)."""
|
|
70
|
+
return GATE_TOOLS.get(name, name)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def gate_input(name, inp):
|
|
74
|
+
"""The tool input in the gate's fields. Droid's ApplyPatch sends its patch as
|
|
75
|
+
`input` (the 0.251.0 bundle reads `o.input` as the patch text); the gate
|
|
76
|
+
reads `patch`."""
|
|
77
|
+
inp = inp if isinstance(inp, dict) else {}
|
|
78
|
+
if name == "ApplyPatch" and isinstance(inp.get("input"), str) and "patch" not in inp:
|
|
79
|
+
return dict(inp, patch=inp["input"])
|
|
80
|
+
return inp
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def classify(payload):
|
|
84
|
+
"""The used-tool kind for a tool payload, or None."""
|
|
85
|
+
name = payload.get("tool_name", "") or ""
|
|
86
|
+
if name.startswith("mcp__") and "codegraph" in name:
|
|
87
|
+
return "graph"
|
|
88
|
+
if name == "Task":
|
|
89
|
+
return "orch"
|
|
90
|
+
if name == "Execute":
|
|
91
|
+
return shell_kind(command_text(payload.get("tool_input")))
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _texts(value):
|
|
96
|
+
"""Every string in a result, depth first."""
|
|
97
|
+
if isinstance(value, str):
|
|
98
|
+
yield value
|
|
99
|
+
elif isinstance(value, dict):
|
|
100
|
+
for v in value.values():
|
|
101
|
+
yield from _texts(v)
|
|
102
|
+
elif isinstance(value, list):
|
|
103
|
+
for v in value:
|
|
104
|
+
yield from _texts(v)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def call_failed(payload):
|
|
108
|
+
"""The outcome a PostToolUse reports, as `note_tool`'s `failed`: True/False
|
|
109
|
+
from an Execute result's exit code - an integer `exit_code`/`exitCode`, else
|
|
110
|
+
the last `[Process exited with code N]` line - and None when no code is
|
|
111
|
+
read. A call with no code is recorded as one that RAN, never as a pass."""
|
|
112
|
+
if payload.get("tool_name") != "Execute":
|
|
113
|
+
return None
|
|
114
|
+
resp = payload.get("tool_response")
|
|
115
|
+
if isinstance(resp, dict):
|
|
116
|
+
for key in ("exit_code", "exitCode"):
|
|
117
|
+
code = resp.get(key)
|
|
118
|
+
if isinstance(code, int) and not isinstance(code, bool):
|
|
119
|
+
return code != 0
|
|
120
|
+
codes = [m for text in _texts(resp) for m in EXIT_LINE.findall(text)]
|
|
121
|
+
return int(codes[-1]) != 0 if codes else None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def last_reply(path):
|
|
125
|
+
"""The text of the assistant reply that ends the transcript, or "".
|
|
126
|
+
|
|
127
|
+
Droid writes one JSON row per message: `{"type": "message", "message":
|
|
128
|
+
{"role": "assistant", "content": [{"type": "text", "text": ...}]}}` (the
|
|
129
|
+
shape its session files carry). The reply is the text of the trailing
|
|
130
|
+
assistant rows; a transcript whose last message is the user's - a tool result
|
|
131
|
+
or a prompt - has no reply written yet, so it answers ""."""
|
|
132
|
+
parts = []
|
|
133
|
+
for raw in reversed(_tail_lines(path)):
|
|
134
|
+
try:
|
|
135
|
+
row = json.loads(raw)
|
|
136
|
+
except ValueError:
|
|
137
|
+
continue
|
|
138
|
+
msg = row.get("message") if isinstance(row, dict) else None
|
|
139
|
+
if not isinstance(msg, dict) or row.get("type") != "message":
|
|
140
|
+
continue
|
|
141
|
+
if msg.get("role") != "assistant":
|
|
142
|
+
break
|
|
143
|
+
content = msg.get("content")
|
|
144
|
+
texts = [b.get("text") for b in content if isinstance(b, dict)
|
|
145
|
+
and b.get("type") == "text" and isinstance(b.get("text"), str)] \
|
|
146
|
+
if isinstance(content, list) else []
|
|
147
|
+
parts[:0] = texts
|
|
148
|
+
return "\n".join(t for t in parts if t.strip())
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def rules_carry_core():
|
|
152
|
+
"""True when `AGENTS.md` in Droid's home holds tezgah-setup's managed block."""
|
|
153
|
+
try:
|
|
154
|
+
with open(os.path.join(HOST_DIRS["droid"], "AGENTS.md"), encoding="utf-8") as fh:
|
|
155
|
+
return "<!-- tezgah:start" in fh.read()
|
|
156
|
+
except OSError:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def result_size(result):
|
|
161
|
+
"""The result's top-level length, the Claude-family writer's measure; a value
|
|
162
|
+
with no length counts as non-empty and only a missing result is None."""
|
|
163
|
+
if isinstance(result, (str, bytes, bytearray, list, tuple, dict, set, frozenset)):
|
|
164
|
+
return len(result)
|
|
165
|
+
return 1 if result is not None else None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def post_tool(payload, cwd, session_id):
|
|
169
|
+
"""The ledger row for one finished call, and the notice for the model."""
|
|
170
|
+
name = payload.get("tool_name", "") or ""
|
|
171
|
+
tool = gate_name(name)
|
|
172
|
+
inp = gate_input(name, payload.get("tool_input"))
|
|
173
|
+
safe(session_id, record, session_id, classify(payload))
|
|
174
|
+
if not root_for(cwd):
|
|
175
|
+
return None
|
|
176
|
+
result = payload.get("tool_response")
|
|
177
|
+
failed = call_failed(payload)
|
|
178
|
+
# read before this call's row lands: `source` is the taint's own mark
|
|
179
|
+
got = safe(session_id, marks, tool, inp, session_id, result)
|
|
180
|
+
source, notice = got or (None, None)
|
|
181
|
+
safe(session_id, note_tool, session_id, tool, inp, failed=failed,
|
|
182
|
+
source=source, cwd=cwd,
|
|
183
|
+
out_bytes=(report_bytes(result)
|
|
184
|
+
if untrusted_source(tool, inp) == SUBAGENT_CHANNEL
|
|
185
|
+
else result_size(result)),
|
|
186
|
+
empty_run=ran_nothing(result))
|
|
187
|
+
advice = safe(session_id, graph_advice, tool, inp, cwd, "droid")
|
|
188
|
+
taste = cross = None
|
|
189
|
+
if not failed:
|
|
190
|
+
taste = safe(session_id, write_note, session_id, inp, cwd, tool)
|
|
191
|
+
cross = safe(session_id, cross_project_note, session_id, tool, inp, cwd)
|
|
192
|
+
return "\n".join(p for p in (notice, advice, taste, cross) if p)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def context(event, text):
|
|
196
|
+
return {"hookSpecificOutput": {"hookEventName": event,
|
|
197
|
+
"additionalContext": text}} if text else {}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def main():
|
|
201
|
+
try:
|
|
202
|
+
payload = json.load(sys.stdin)
|
|
203
|
+
except Exception:
|
|
204
|
+
return
|
|
205
|
+
if not isinstance(payload, dict):
|
|
206
|
+
return
|
|
207
|
+
event = payload.get("hook_event_name") or ""
|
|
208
|
+
cwd = payload.get("cwd") or os.getcwd()
|
|
209
|
+
session_id = payload.get("session_id")
|
|
210
|
+
safe(session_id, bind_session, session_id) # the switch latch (tezgah_paths.off)
|
|
211
|
+
out = {}
|
|
212
|
+
if event == "PreToolUse":
|
|
213
|
+
name = payload.get("tool_name", "") or ""
|
|
214
|
+
reason = safe(session_id, decision, gate_name(name),
|
|
215
|
+
gate_input(name, payload.get("tool_input")), cwd, session_id)
|
|
216
|
+
if reason:
|
|
217
|
+
out = {"hookSpecificOutput": {
|
|
218
|
+
"hookEventName": "PreToolUse",
|
|
219
|
+
"permissionDecision": "deny",
|
|
220
|
+
"permissionDecisionReason": reason,
|
|
221
|
+
}}
|
|
222
|
+
elif event == "PostToolUse":
|
|
223
|
+
out = context(event, post_tool(payload, cwd, session_id))
|
|
224
|
+
elif event == "SessionStart":
|
|
225
|
+
safe(session_id, attest_session, "droid", session_id, cwd)
|
|
226
|
+
out = context(event, safe(session_id, context_for, "session_start", cwd,
|
|
227
|
+
payload, with_core=not rules_carry_core()))
|
|
228
|
+
elif event == "UserPromptSubmit":
|
|
229
|
+
out = context(event, safe(session_id, context_for, "user_prompt", cwd, payload))
|
|
230
|
+
elif event == "SubagentStop":
|
|
231
|
+
# a sub-droid's end is judged record-only (ADR 011): one `subagent_end`
|
|
232
|
+
# row from its `task_result`, never a block
|
|
233
|
+
safe(session_id, record, session_id, "orch")
|
|
234
|
+
if not off("verify-off") and root_for(cwd):
|
|
235
|
+
text = payload.get("task_result")
|
|
236
|
+
safe(session_id, stop_reason, text if isinstance(text, str) else "",
|
|
237
|
+
session_id, cwd=cwd, subagent=True)
|
|
238
|
+
elif event == "Stop":
|
|
239
|
+
# Droid's Stop takes {"decision": "block", "reason": ...}. The reply
|
|
240
|
+
# after a block is judged again and refused at most once more
|
|
241
|
+
# (tezgah_integrity).
|
|
242
|
+
if not off("verify-off") and root_for(cwd):
|
|
243
|
+
path = payload.get("transcript_path")
|
|
244
|
+
text = safe(session_id, last_reply, path)
|
|
245
|
+
if text:
|
|
246
|
+
reason = safe(session_id, stop_reason, text, session_id, cwd=cwd,
|
|
247
|
+
after_block=bool(payload.get("stop_hook_active")),
|
|
248
|
+
mode=payload.get("permission_mode"))
|
|
249
|
+
if reason:
|
|
250
|
+
out = {"decision": "block", "reason": reason}
|
|
251
|
+
else:
|
|
252
|
+
safe(session_id, note, session_id, "reply_unread",
|
|
253
|
+
"no assistant text at the end of %s" % path if path
|
|
254
|
+
else "no transcript_path")
|
|
255
|
+
if out:
|
|
256
|
+
print(json.dumps(out))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
main()
|
package/hosts/dsh/hooks.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{ "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-auto-init.py\"", "timeout": 10 } ] }
|
|
11
11
|
],
|
|
12
12
|
"PreToolUse": [
|
|
13
|
-
{ "matcher": "Agent|agent|Task|task|Grep|grep|Bash|bash|PowerShell|pwsh|Shell|shell|subagent|Edit|Write|MultiEdit|NotebookEdit|AskUserQuestion", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] },
|
|
13
|
+
{ "matcher": "Agent|agent|Task|task|Grep|grep|Bash|bash|PowerShell|pwsh|Shell|shell|Monitor|monitor|subagent|Edit|Write|MultiEdit|NotebookEdit|AskUserQuestion", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] },
|
|
14
14
|
{ "matcher": "^mcp__(?:.*[^A-Za-z0-9])?(?:append|cancel|clear|click|close|comment|commit|copy|create|delete|deploy|drag|edit|email|evaluate|exec|execute|fill|insert|install|kill|launch|merge|message|mkdir|move|navigate|notify|patch|pause|post|press|print|publish|push|put|release|remove|rename|replace|reply|resume|review|run|save|send|set|share|skip|start|stop|submit|swipe|tag|tap|terminate|type|uninstall|update|upload|write)(?:[^A-Za-z0-9].*)?$", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] }
|
|
15
15
|
],
|
|
16
16
|
"PostToolUse": [
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Gemini CLI lifecycle hook. Reads Gemini's JSON on stdin and answers in its own
|
|
3
|
+
output shape, onto the shared tezgah core (hooks/).
|
|
4
|
+
|
|
5
|
+
tezgah's entries live in its own extension, `$GEMINI_CLI_HOME/.gemini/extensions/
|
|
6
|
+
tezgah/hooks/hooks.json` (`bin/tezgah-setup::GEMINI_HOOK_EVENTS`): SessionStart,
|
|
7
|
+
BeforeAgent, BeforeTool, AfterTool and AfterAgent (geminicli hooks reference);
|
|
8
|
+
anything else is ignored. Inert outside the roots. A bad exit or bad stdout is a
|
|
9
|
+
warning there and the call goes on (fail open).
|
|
10
|
+
|
|
11
|
+
The always-on core is the managed block in `~/.gemini/GEMINI.md`, the global
|
|
12
|
+
context file, so SessionStart repeats it only when that block is missing.
|
|
13
|
+
BeforeAgent writes the turn row and carries the per-turn reminder as
|
|
14
|
+
`hookSpecificOutput.additionalContext`. BeforeTool translates the tool names
|
|
15
|
+
into the gate's vocabulary and denies with a top-level
|
|
16
|
+
`{"decision": "deny", "reason": ...}` on exit 0. AfterTool's `tool_response`
|
|
17
|
+
holds `llmContent` and an optional `error`; the outcome is read off both
|
|
18
|
+
(`call_failed`). AfterAgent carries the reply as `prompt_response` and
|
|
19
|
+
`stop_hook_active`, and its deny sends the reason back as a new prompt. Gemini
|
|
20
|
+
has no command status line (`ui.footer` is a closed list), so the status
|
|
21
|
+
segment rides `systemMessage` at SessionStart and AfterAgent.
|
|
22
|
+
"""
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
29
|
+
sys.path.insert(0, os.path.join(ROOT, "hooks"))
|
|
30
|
+
from tezgah_guard import attest_session, import_failed, safe # noqa: E402
|
|
31
|
+
try:
|
|
32
|
+
from tezgah_context import ( # noqa: E402
|
|
33
|
+
TOOL_USE_MEASURES, command_text, context_for, health_lines, record, shell_kind)
|
|
34
|
+
from tezgah_gate import cross_project_note, decision, graph_advice # noqa: E402
|
|
35
|
+
from tezgah_integrity import ( # noqa: E402
|
|
36
|
+
SUBAGENT_CHANNEL, bind_session, note_tool, ran_nothing, report_bytes,
|
|
37
|
+
stop_reason, untrusted_source)
|
|
38
|
+
from tezgah_paths import HOST_DIRS, off, root_for # noqa: E402
|
|
39
|
+
from tezgah_taste import write_note # noqa: E402
|
|
40
|
+
from tezgah_untrusted import marks # noqa: E402
|
|
41
|
+
except Exception as exc:
|
|
42
|
+
import_failed(exc)
|
|
43
|
+
|
|
44
|
+
# Gemini's tool names (docs reference/tools.md, core/subagents.md) -> the gate's
|
|
45
|
+
# vocabulary. A subagent is a tool of its own name; `invoke_agent` is the
|
|
46
|
+
# generic one in the source's tool-names.ts. A user's own agents are not mapped.
|
|
47
|
+
GATE_TOOLS = {
|
|
48
|
+
"run_shell_command": "Bash",
|
|
49
|
+
"write_file": "Write",
|
|
50
|
+
"replace": "Edit",
|
|
51
|
+
"grep_search": "Grep", "search_file_content": "Grep",
|
|
52
|
+
"codebase_investigator": "Task", "generalist": "Task", "invoke_agent": "Task",
|
|
53
|
+
"read_file": "Read",
|
|
54
|
+
}
|
|
55
|
+
# The shell tool writes `Output: ...` first, then `Exit Code: N` only for a
|
|
56
|
+
# non-zero code, or `Signal: NAME` for a killed process
|
|
57
|
+
# (packages/core/src/tools/shell.ts), inside an `<untrusted_context>` wrapper;
|
|
58
|
+
# a background or cancelled run has no Output.
|
|
59
|
+
RAN = re.compile(r"\A(?:<untrusted_context[^>]*>\n)?Output: ")
|
|
60
|
+
EXIT_CODE = re.compile(r"(?m)^Exit Code: (-?\d+)$")
|
|
61
|
+
SIGNAL = re.compile(r"(?m)^Signal: ")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def gate_name(name):
|
|
65
|
+
"""The tool name the gate denies under and the ledger row records; both
|
|
66
|
+
halves map through here so a call hashes to one id. An MCP tool arrives as
|
|
67
|
+
`mcp_<server>_<tool>`, where nothing marks the boundary, and leaves as
|
|
68
|
+
`mcp__<server>_<tool>`."""
|
|
69
|
+
if name in GATE_TOOLS:
|
|
70
|
+
return GATE_TOOLS[name]
|
|
71
|
+
if name.startswith("mcp_") and not name.startswith("mcp__"):
|
|
72
|
+
return "mcp__" + name[4:]
|
|
73
|
+
return name
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def inp_of(payload):
|
|
77
|
+
inp = payload.get("tool_input")
|
|
78
|
+
return inp if isinstance(inp, dict) else {}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def classify(tool, inp):
|
|
82
|
+
"""The used-tool kind for a mapped call, or None."""
|
|
83
|
+
if tool.startswith("mcp__") and "codegraph" in tool:
|
|
84
|
+
return "graph"
|
|
85
|
+
if tool == "Task":
|
|
86
|
+
return "orch"
|
|
87
|
+
if tool == "Bash":
|
|
88
|
+
return shell_kind(command_text(inp))
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def result_text(resp):
|
|
93
|
+
"""`tool_response.llmContent` as text: a string, or a list of parts."""
|
|
94
|
+
content = resp.get("llmContent") if isinstance(resp, dict) else None
|
|
95
|
+
if isinstance(content, str):
|
|
96
|
+
return content
|
|
97
|
+
if isinstance(content, list):
|
|
98
|
+
return "\n".join(p.get("text") if isinstance(p, dict) and isinstance(p.get("text"), str)
|
|
99
|
+
else p if isinstance(p, str) else "" for p in content)
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def call_failed(tool, resp, text):
|
|
104
|
+
"""`note_tool`'s `failed`: an `error` in the response is a failure; a shell
|
|
105
|
+
result names a non-zero exit code or a signal (a killed runner), or ran to
|
|
106
|
+
its end with neither (a pass). None when the response says none of these -
|
|
107
|
+
a call that RAN, never a pass."""
|
|
108
|
+
if isinstance(resp, dict) and resp.get("error"):
|
|
109
|
+
return True
|
|
110
|
+
if tool != "Bash" or not isinstance(text, str):
|
|
111
|
+
return None
|
|
112
|
+
# a signal first: the command's own output may hold an `Exit Code: 0` line
|
|
113
|
+
if SIGNAL.search(text):
|
|
114
|
+
return True
|
|
115
|
+
codes = EXIT_CODE.findall(text)
|
|
116
|
+
if codes:
|
|
117
|
+
return int(codes[-1]) != 0
|
|
118
|
+
return False if RAN.match(text) else None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def rules_carry_core():
|
|
122
|
+
"""True when Gemini's global GEMINI.md holds tezgah-setup's managed block."""
|
|
123
|
+
try:
|
|
124
|
+
with open(os.path.join(HOST_DIRS["gemini"], "GEMINI.md"), encoding="utf-8") as fh:
|
|
125
|
+
return "<!-- tezgah:start" in fh.read()
|
|
126
|
+
except OSError:
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def post_tool(payload, cwd, session_id):
|
|
131
|
+
"""The ledger row for one finished call, and the notice for the model."""
|
|
132
|
+
tool, inp = gate_name(payload.get("tool_name", "") or ""), inp_of(payload)
|
|
133
|
+
safe(session_id, record, session_id, classify(tool, inp))
|
|
134
|
+
if not root_for(cwd):
|
|
135
|
+
return None
|
|
136
|
+
resp = payload.get("tool_response")
|
|
137
|
+
result = result_text(resp)
|
|
138
|
+
failed = call_failed(tool, resp, result)
|
|
139
|
+
# read before this call's row lands: `source` is the taint's own mark
|
|
140
|
+
got = safe(session_id, marks, tool, inp, session_id, result)
|
|
141
|
+
source, notice = got or (None, None)
|
|
142
|
+
safe(session_id, note_tool, session_id, tool, inp, failed=failed,
|
|
143
|
+
source=source, cwd=cwd,
|
|
144
|
+
out_bytes=(report_bytes(result)
|
|
145
|
+
if untrusted_source(tool, inp) == SUBAGENT_CHANNEL
|
|
146
|
+
else len(result) if isinstance(result, str) else None),
|
|
147
|
+
empty_run=ran_nothing(result))
|
|
148
|
+
advice = safe(session_id, graph_advice, tool, inp, cwd, "gemini")
|
|
149
|
+
taste = cross = None
|
|
150
|
+
if not failed:
|
|
151
|
+
taste = safe(session_id, write_note, session_id, inp, cwd, tool)
|
|
152
|
+
cross = safe(session_id, cross_project_note, session_id, tool, inp, cwd)
|
|
153
|
+
return "\n".join(p for p in (notice, advice, taste, cross) if p)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def context(event, text):
|
|
157
|
+
return {"hookSpecificOutput": {"hookEventName": event,
|
|
158
|
+
"additionalContext": text}} if text else {}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def status(out, cwd, session_id):
|
|
162
|
+
"""Add the status segment as `systemMessage`, shown to the user only."""
|
|
163
|
+
seg = safe(session_id, health_lines, cwd, session_id, observable=TOOL_USE_MEASURES)
|
|
164
|
+
if seg:
|
|
165
|
+
out["systemMessage"] = "tezgah " + seg
|
|
166
|
+
return out
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def main():
|
|
170
|
+
try:
|
|
171
|
+
payload = json.load(sys.stdin)
|
|
172
|
+
except Exception:
|
|
173
|
+
return
|
|
174
|
+
if not isinstance(payload, dict):
|
|
175
|
+
return
|
|
176
|
+
event = payload.get("hook_event_name") or ""
|
|
177
|
+
cwd = payload.get("cwd") or os.getcwd()
|
|
178
|
+
session_id = payload.get("session_id")
|
|
179
|
+
safe(session_id, bind_session, session_id) # the switch latch (tezgah_paths.off)
|
|
180
|
+
out = {}
|
|
181
|
+
if event == "BeforeTool":
|
|
182
|
+
reason = safe(session_id, decision, gate_name(payload.get("tool_name", "") or ""),
|
|
183
|
+
inp_of(payload), cwd, session_id)
|
|
184
|
+
if reason:
|
|
185
|
+
# the reason reaches the agent as the tool's error; the turn goes on
|
|
186
|
+
out = {"decision": "deny", "reason": reason}
|
|
187
|
+
elif event == "AfterTool":
|
|
188
|
+
out = context(event, post_tool(payload, cwd, session_id))
|
|
189
|
+
elif event == "SessionStart":
|
|
190
|
+
safe(session_id, attest_session, "gemini", session_id, cwd)
|
|
191
|
+
out = status(context(event, safe(session_id, context_for, "session_start", cwd,
|
|
192
|
+
payload, with_core=not rules_carry_core())),
|
|
193
|
+
cwd, session_id)
|
|
194
|
+
elif event == "BeforeAgent":
|
|
195
|
+
out = context(event, safe(session_id, context_for, "user_prompt", cwd, payload))
|
|
196
|
+
elif event == "AfterAgent":
|
|
197
|
+
# {"decision": "deny", "reason": ...} rejects the reply and sends the
|
|
198
|
+
# reason back as a new prompt; the reply after a block is judged again
|
|
199
|
+
# and refused at most once more (tezgah_integrity).
|
|
200
|
+
if not off("verify-off") and root_for(cwd):
|
|
201
|
+
text = payload.get("prompt_response")
|
|
202
|
+
reason = safe(session_id, stop_reason, text if isinstance(text, str) else "",
|
|
203
|
+
session_id, cwd=cwd,
|
|
204
|
+
after_block=bool(payload.get("stop_hook_active")),
|
|
205
|
+
mode=payload.get("permission_mode"))
|
|
206
|
+
if reason:
|
|
207
|
+
out = {"decision": "deny", "reason": reason}
|
|
208
|
+
out = status(out, cwd, session_id)
|
|
209
|
+
if out:
|
|
210
|
+
print(json.dumps(out))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
main()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Goose lifecycle hook. Reads Goose's JSON on stdin and answers in its own
|
|
3
|
+
output shape, onto the shared tezgah core (hooks/).
|
|
4
|
+
|
|
5
|
+
tezgah's entries live in its own plugin, `~/.agents/plugins/tezgah/hooks/
|
|
6
|
+
hooks.json` (`bin/tezgah-setup::GOOSE_HOOK_EVENTS`): SessionStart,
|
|
7
|
+
UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure and Stop (goose
|
|
8
|
+
docs guides/context-engineering/hooks); anything else is ignored. The event
|
|
9
|
+
name is the payload's `event`, the directory its `working_dir` (sent on tool
|
|
10
|
+
events only), else the hook's own cwd. Inert outside the roots. A hook fault is
|
|
11
|
+
logged by Goose and the call goes on: `on_failure` is left at its default
|
|
12
|
+
`allow`, so the gate fails open (X-D2).
|
|
13
|
+
|
|
14
|
+
Only PreToolUse and Stop can block. Both deny with `{"decision": "block",
|
|
15
|
+
"reason": ...}` on stdout, which must start with `{`, on exit 0. Every other
|
|
16
|
+
event is observation only: SessionStart writes the attestation, and
|
|
17
|
+
UserPromptSubmit the turn row; no output of theirs reaches the model, so the
|
|
18
|
+
always-on core is the managed block in `~/.config/goose/.goosehints` alone.
|
|
19
|
+
PostToolUse fires for a call that succeeded and PostToolUseFailure for one that
|
|
20
|
+
failed; neither carries the tool's output, so the event is the outcome. Stop
|
|
21
|
+
carries the reply as `last_assistant_message` and no count of earlier blocks,
|
|
22
|
+
so the turn's own rows bound the re-asks. No status line and no subagent events
|
|
23
|
+
(SubagentStart/SubagentStop are not emitted).
|
|
24
|
+
"""
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
30
|
+
sys.path.insert(0, os.path.join(ROOT, "hooks"))
|
|
31
|
+
from tezgah_guard import attest_session, import_failed, safe # noqa: E402
|
|
32
|
+
try:
|
|
33
|
+
from tezgah_context import command_text, context_for, record, shell_kind # noqa: E402
|
|
34
|
+
from tezgah_gate import cross_project_note, decision, graph_advice # noqa: E402
|
|
35
|
+
from tezgah_integrity import bind_session, note, note_tool, stop_reason # noqa: E402
|
|
36
|
+
from tezgah_paths import off, root_for # noqa: E402
|
|
37
|
+
from tezgah_taste import write_note # noqa: E402
|
|
38
|
+
from tezgah_untrusted import marks # noqa: E402
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
import_failed(exc)
|
|
41
|
+
|
|
42
|
+
# Goose's built-in developer tools arrive under their bare name (hooks "Tool
|
|
43
|
+
# Input Keys"); a subagent tool is not mapped.
|
|
44
|
+
GATE_TOOLS = {"shell": "Bash", "write": "Write", "edit": "Edit"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def gate_name(name):
|
|
48
|
+
"""The tool name the gate denies under and the ledger row records; both
|
|
49
|
+
halves map through here so a call hashes to one id. An extension's tool
|
|
50
|
+
arrives as `<extension>__<tool>` and leaves in the shared
|
|
51
|
+
`mcp__<extension>__<tool>` form."""
|
|
52
|
+
if name in GATE_TOOLS:
|
|
53
|
+
return GATE_TOOLS[name]
|
|
54
|
+
if "__" in name.strip("_") and not name.startswith("mcp__"):
|
|
55
|
+
return "mcp__" + name
|
|
56
|
+
return name
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def gate_input(name, inp):
|
|
60
|
+
"""The tool input in the gate's fields: `write` and `edit` name the file
|
|
61
|
+
`path`, and `edit` its strings `before`/`after`."""
|
|
62
|
+
inp = dict(inp) if isinstance(inp, dict) else {}
|
|
63
|
+
if name in ("write", "edit") and "path" in inp:
|
|
64
|
+
inp.setdefault("file_path", inp["path"])
|
|
65
|
+
if name == "edit":
|
|
66
|
+
for src, dst in (("before", "old_string"), ("after", "new_string")):
|
|
67
|
+
if src in inp:
|
|
68
|
+
inp.setdefault(dst, inp[src])
|
|
69
|
+
return inp
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def classify(tool, inp):
|
|
73
|
+
"""The used-tool kind for a mapped call, or None."""
|
|
74
|
+
if tool.startswith("mcp__") and "codegraph" in tool:
|
|
75
|
+
return "graph"
|
|
76
|
+
if tool == "Bash":
|
|
77
|
+
return shell_kind(command_text(inp))
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def post_tool(payload, cwd, session_id, failed):
|
|
82
|
+
"""The ledger row for one finished call. Goose reads no output of this
|
|
83
|
+
event, so nothing is returned to the model."""
|
|
84
|
+
name = payload.get("tool_name", "") or ""
|
|
85
|
+
tool, inp = gate_name(name), gate_input(name, payload.get("tool_input"))
|
|
86
|
+
safe(session_id, record, session_id, classify(tool, inp))
|
|
87
|
+
if not root_for(cwd):
|
|
88
|
+
return
|
|
89
|
+
# read before this call's row lands: `source` is the taint's own mark
|
|
90
|
+
got = safe(session_id, marks, tool, inp, session_id, None)
|
|
91
|
+
source = (got or (None, None))[0]
|
|
92
|
+
safe(session_id, note_tool, session_id, tool, inp, failed=failed,
|
|
93
|
+
source=source, cwd=cwd)
|
|
94
|
+
safe(session_id, graph_advice, tool, inp, cwd, "goose")
|
|
95
|
+
if not failed:
|
|
96
|
+
safe(session_id, write_note, session_id, inp, cwd, tool)
|
|
97
|
+
safe(session_id, cross_project_note, session_id, tool, inp, cwd)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def main():
|
|
101
|
+
try:
|
|
102
|
+
payload = json.load(sys.stdin)
|
|
103
|
+
except Exception:
|
|
104
|
+
return
|
|
105
|
+
if not isinstance(payload, dict):
|
|
106
|
+
return
|
|
107
|
+
event = payload.get("event") or ""
|
|
108
|
+
cwd = payload.get("working_dir") or os.getcwd()
|
|
109
|
+
session_id = payload.get("session_id")
|
|
110
|
+
safe(session_id, bind_session, session_id) # the switch latch (tezgah_paths.off)
|
|
111
|
+
out = {}
|
|
112
|
+
if event == "PreToolUse":
|
|
113
|
+
name = payload.get("tool_name", "") or ""
|
|
114
|
+
reason = safe(session_id, decision, gate_name(name),
|
|
115
|
+
gate_input(name, payload.get("tool_input")), cwd, session_id)
|
|
116
|
+
if reason:
|
|
117
|
+
# Goose returns the reason to the model as a policy denial
|
|
118
|
+
out = {"decision": "block", "reason": reason}
|
|
119
|
+
elif event in ("PostToolUse", "PostToolUseFailure"):
|
|
120
|
+
post_tool(payload, cwd, session_id, event == "PostToolUseFailure")
|
|
121
|
+
elif event == "SessionStart":
|
|
122
|
+
safe(session_id, attest_session, "goose", session_id, cwd)
|
|
123
|
+
elif event == "UserPromptSubmit":
|
|
124
|
+
# the turn row only: Goose ignores this event's output
|
|
125
|
+
safe(session_id, context_for, "user_prompt", cwd, payload)
|
|
126
|
+
elif event == "Stop":
|
|
127
|
+
# a block forces the turn to keep going; Goose caps consecutive blocks
|
|
128
|
+
# (GOOSE_STOP_HOOK_BLOCK_CAP). The payload counts no earlier block, so
|
|
129
|
+
# the turn's refusal rows decide whether the reply may be refused again.
|
|
130
|
+
# The reply is sent only when there is assistant output (X-D5).
|
|
131
|
+
if not off("verify-off") and root_for(cwd):
|
|
132
|
+
text = payload.get("last_assistant_message")
|
|
133
|
+
if isinstance(text, str) and text.strip():
|
|
134
|
+
reason = safe(session_id, stop_reason, text, session_id, cwd=cwd,
|
|
135
|
+
mode=payload.get("permission_mode"))
|
|
136
|
+
if reason:
|
|
137
|
+
out = {"decision": "block", "reason": reason}
|
|
138
|
+
else:
|
|
139
|
+
safe(session_id, note, session_id, "reply_unread",
|
|
140
|
+
"no last_assistant_message")
|
|
141
|
+
if out:
|
|
142
|
+
print(json.dumps(out))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
main()
|
package/hosts/omp/hook.py
CHANGED
|
@@ -276,9 +276,12 @@ def handle(payload):
|
|
|
276
276
|
# the reply after a block is judged again; the core decides from the
|
|
277
277
|
# turn's ledger whether it may be refused once more (plan 083), and a
|
|
278
278
|
# core that raises has refused nothing
|
|
279
|
+
# an older installed bridge still sends omp's message object here; the
|
|
280
|
+
# core reads the prose either way (tezgah_integrity.reply_prose, 143)
|
|
279
281
|
reason = safe(session_id, stop_reason,
|
|
280
282
|
payload.get("last_assistant_message"), session_id,
|
|
281
|
-
cwd=cwd, after_block=bool(payload.get("stop_hook_active"))
|
|
283
|
+
cwd=cwd, after_block=bool(payload.get("stop_hook_active")),
|
|
284
|
+
mode=payload.get("permission_mode"))
|
|
282
285
|
return {"decision": "block", "reason": reason} if reason else {}
|
|
283
286
|
if event == "subagent_stop":
|
|
284
287
|
# record-only (ADR 011): judged on the child's own rows, the row lands
|