@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.
Files changed (445) hide show
  1. package/CHANGELOG.md +415 -1
  2. package/MANIFEST +404 -144
  3. package/README.de.md +1 -1
  4. package/README.es.md +1 -1
  5. package/README.fr.md +1 -1
  6. package/README.md +5 -4
  7. package/README.tr.md +1 -1
  8. package/bin/codegen +5 -2
  9. package/bin/tezgah-design +288 -1
  10. package/bin/tezgah-docs +1 -1
  11. package/bin/tezgah-doctor +39 -2
  12. package/bin/tezgah-import-ai-research +26 -12
  13. package/bin/tezgah-ocr-review +296 -0
  14. package/bin/tezgah-research +255 -44
  15. package/bin/tezgah-review-quote +185 -0
  16. package/bin/tezgah-setup +1365 -55
  17. package/bin/tezgah-skill +1 -1
  18. package/bin/tezgah-status +201 -3
  19. package/bin/tezgah-task +258 -20
  20. package/commands/adhd.md +2 -2
  21. package/commands/ponytail.md +4 -4
  22. package/docs/architecture.md +2 -2
  23. package/docs/citations-baseline.json +3 -3
  24. package/docs/clarity-baseline.json +11 -5
  25. package/docs/contract.md +91 -43
  26. package/docs/environment.md +5 -2
  27. package/docs/evidence.md +109 -17
  28. package/docs/feature-audit.md +2 -2
  29. package/docs/gate.md +60 -33
  30. package/docs/glossary.md +6 -3
  31. package/docs/hosts.md +313 -17
  32. package/docs/index.json +34 -8
  33. package/docs/judge.md +4 -2
  34. package/docs/layers.md +4 -4
  35. package/docs/models.md +37 -9
  36. package/docs/operations.md +141 -5
  37. package/docs/research.md +815 -8
  38. package/docs/skills.md +137 -13
  39. package/docs/status-line.md +121 -1
  40. package/docs/testing.md +3 -1
  41. package/hooks/hooks.json +2 -2
  42. package/hooks/lang/en.json +21 -0
  43. package/hooks/lang/tr.json +22 -1
  44. package/hooks/projects-stop.py +2 -1
  45. package/hooks/tezgah_agents.py +65 -25
  46. package/hooks/tezgah_apps.py +18 -0
  47. package/hooks/tezgah_attest.py +79 -10
  48. package/hooks/tezgah_components.py +4 -0
  49. package/hooks/tezgah_context.py +207 -55
  50. package/hooks/tezgah_extension.py +337 -0
  51. package/hooks/tezgah_gate.py +267 -79
  52. package/hooks/tezgah_integrity.py +864 -143
  53. package/hooks/tezgah_judge.py +6 -4
  54. package/hooks/tezgah_models.py +59 -19
  55. package/hooks/tezgah_orca.py +1 -1
  56. package/hooks/tezgah_paths.py +145 -8
  57. package/hooks/tezgah_policy.py +128 -57
  58. package/hooks/tezgah_rank.py +9 -6
  59. package/hooks/tezgah_replay.py +12 -6
  60. package/hooks/tezgah_research.py +1414 -76
  61. package/hooks/tezgah_research_capture.py +1544 -0
  62. package/hooks/tezgah_research_evidence.py +842 -0
  63. package/hooks/tezgah_research_stats.py +258 -0
  64. package/hooks/tezgah_skill_pick.py +408 -106
  65. package/hooks/tezgah_stack.py +236 -1
  66. package/hooks/tezgah_stopspec.py +8 -1
  67. package/hooks/tezgah_task.py +14 -5
  68. package/hooks/tezgah_taste_ledger.py +1 -1
  69. package/hosts/amp/hook.py +217 -0
  70. package/hosts/amp/tezgah-plugin.ts.in +386 -0
  71. package/hosts/antigravity/hook.py +255 -0
  72. package/hosts/codex/hook.py +2 -1
  73. package/hosts/commandcode/hook.py +239 -0
  74. package/hosts/copilot/hook.py +284 -0
  75. package/hosts/cursor/hook.py +31 -3
  76. package/hosts/droid/hook.py +259 -0
  77. package/hosts/dsh/hooks.json +1 -1
  78. package/hosts/gemini/hook.py +213 -0
  79. package/hosts/goose/hook.py +145 -0
  80. package/hosts/omp/hook.py +4 -1
  81. package/hosts/omp/tezgah-hook.ts.in +30 -7
  82. package/hosts/opencode/plugins/tezgah.js +309 -48
  83. package/hosts/opencode/tui/cli/tui.tsx +100 -0
  84. package/hosts/opencode/tui/tezgah-tui.tsx +2 -1
  85. package/hosts/qwen/hook.py +206 -0
  86. package/output-styles/tezgah.md +20 -15
  87. package/package.json +1 -1
  88. package/skills/ai-research/SKILL.md +21 -14
  89. package/skills/ai-research/SOURCE +7 -5
  90. package/skills/ai-research/index/1-frame.md +6 -6
  91. package/skills/ai-research/index/2-data.md +9 -9
  92. package/skills/ai-research/index/3-train.md +36 -36
  93. package/skills/ai-research/index/4-measure.md +13 -13
  94. package/skills/ai-research/index/5-run.md +11 -11
  95. package/skills/ai-research/index/6-write.md +23 -23
  96. package/skills/ai-research/library.json +104 -104
  97. package/skills/analyze-app/SKILL.md +17 -12
  98. package/skills/design-contract/SKILL.md +42 -7
  99. package/skills/design-contract/data-views.md +2 -2
  100. package/skills/design-library/EVALS.md +1 -1
  101. package/skills/design-library/INDEX.md +39 -39
  102. package/skills/design-library/SKILL.md +19 -13
  103. package/skills/design-library/SOURCE +45 -43
  104. package/skills/feature-audit/SKILL.md +20 -14
  105. package/skills/hallmark/LICENSE +21 -0
  106. package/skills/hallmark/SKILL.md +154 -0
  107. package/skills/hallmark/SOURCE +207 -0
  108. package/skills/hallmark/agents/openai.yaml +13 -0
  109. package/skills/hallmark/references/anatomy.md +71 -0
  110. package/skills/hallmark/references/brand-truth.md +40 -0
  111. package/skills/hallmark/references/comp.md +104 -0
  112. package/skills/hallmark/references/craft.md +116 -0
  113. package/skills/hallmark/references/dark-mode.md +49 -0
  114. package/skills/hallmark/references/data-viz.md +48 -0
  115. package/skills/hallmark/references/design-md.md +53 -0
  116. package/skills/hallmark/references/direction.md +52 -0
  117. package/skills/hallmark/references/export-formats.md +80 -0
  118. package/skills/hallmark/references/flow.md +131 -0
  119. package/skills/hallmark/references/fonts.md +58 -0
  120. package/skills/hallmark/references/gates.md +243 -0
  121. package/skills/hallmark/references/genres/atmospheric.md +24 -0
  122. package/skills/hallmark/references/genres/editorial.md +24 -0
  123. package/skills/hallmark/references/genres/modern-minimal.md +24 -0
  124. package/skills/hallmark/references/genres/playful.md +24 -0
  125. package/skills/hallmark/references/imagery.md +99 -0
  126. package/skills/hallmark/references/interaction-and-states.md +89 -0
  127. package/skills/hallmark/references/layout.md +104 -0
  128. package/skills/hallmark/references/macrostructures/01-bento-grid.md +21 -0
  129. package/skills/hallmark/references/macrostructures/02-long-document.md +35 -0
  130. package/skills/hallmark/references/macrostructures/03-marquee-hero.md +21 -0
  131. package/skills/hallmark/references/macrostructures/04-stat-led.md +21 -0
  132. package/skills/hallmark/references/macrostructures/05-workbench.md +21 -0
  133. package/skills/hallmark/references/macrostructures/06-conversational-faq.md +28 -0
  134. package/skills/hallmark/references/macrostructures/07-manifesto.md +28 -0
  135. package/skills/hallmark/references/macrostructures/08-photographic.md +21 -0
  136. package/skills/hallmark/references/macrostructures/09-quote-led.md +21 -0
  137. package/skills/hallmark/references/macrostructures/10-specimen.md +21 -0
  138. package/skills/hallmark/references/macrostructures/11-catalogue.md +21 -0
  139. package/skills/hallmark/references/macrostructures/12-letter.md +21 -0
  140. package/skills/hallmark/references/macrostructures/13-index-first.md +21 -0
  141. package/skills/hallmark/references/macrostructures/14-narrative-workflow.md +21 -0
  142. package/skills/hallmark/references/macrostructures/15-split-studio.md +21 -0
  143. package/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
  144. package/skills/hallmark/references/macrostructures/17-type-specimen.md +19 -0
  145. package/skills/hallmark/references/macrostructures/18-portfolio-grid.md +21 -0
  146. package/skills/hallmark/references/macrostructures/19-map-diagram.md +21 -0
  147. package/skills/hallmark/references/macrostructures/20-ecosystem-index.md +19 -0
  148. package/skills/hallmark/references/macrostructures/21-component-playground.md +21 -0
  149. package/skills/hallmark/references/macrostructures.md +119 -0
  150. package/skills/hallmark/references/microinteractions.md +80 -0
  151. package/skills/hallmark/references/patterns.md +202 -0
  152. package/skills/hallmark/references/redesign.md +88 -0
  153. package/skills/hallmark/references/responsive.md +46 -0
  154. package/skills/hallmark/references/review-finish.md +19 -0
  155. package/skills/hallmark/references/review-hero.md +20 -0
  156. package/skills/hallmark/references/scroll-choreography.md +110 -0
  157. package/skills/hallmark/references/texture.md +90 -0
  158. package/skills/hallmark/references/themes/README.md +58 -0
  159. package/skills/hallmark/references/themes/almanac.jpg +0 -0
  160. package/skills/hallmark/references/themes/almanac.md +103 -0
  161. package/skills/hallmark/references/themes/arcade.jpg +0 -0
  162. package/skills/hallmark/references/themes/arcade.md +102 -0
  163. package/skills/hallmark/references/themes/atelier.jpg +0 -0
  164. package/skills/hallmark/references/themes/atelier.md +98 -0
  165. package/skills/hallmark/references/themes/bloom.jpg +0 -0
  166. package/skills/hallmark/references/themes/bloom.md +102 -0
  167. package/skills/hallmark/references/themes/blueprint.jpg +0 -0
  168. package/skills/hallmark/references/themes/blueprint.md +103 -0
  169. package/skills/hallmark/references/themes/broadside.jpg +0 -0
  170. package/skills/hallmark/references/themes/broadside.md +108 -0
  171. package/skills/hallmark/references/themes/brutal.jpg +0 -0
  172. package/skills/hallmark/references/themes/brutal.md +95 -0
  173. package/skills/hallmark/references/themes/carnival.jpg +0 -0
  174. package/skills/hallmark/references/themes/carnival.md +104 -0
  175. package/skills/hallmark/references/themes/cobalt.jpg +0 -0
  176. package/skills/hallmark/references/themes/cobalt.md +101 -0
  177. package/skills/hallmark/references/themes/console.jpg +0 -0
  178. package/skills/hallmark/references/themes/console.md +99 -0
  179. package/skills/hallmark/references/themes/coral.jpg +0 -0
  180. package/skills/hallmark/references/themes/coral.md +102 -0
  181. package/skills/hallmark/references/themes/darkroom.jpg +0 -0
  182. package/skills/hallmark/references/themes/darkroom.md +99 -0
  183. package/skills/hallmark/references/themes/dose.jpg +0 -0
  184. package/skills/hallmark/references/themes/dose.md +98 -0
  185. package/skills/hallmark/references/themes/dusk.jpg +0 -0
  186. package/skills/hallmark/references/themes/dusk.md +105 -0
  187. package/skills/hallmark/references/themes/editorial.jpg +0 -0
  188. package/skills/hallmark/references/themes/editorial.md +97 -0
  189. package/skills/hallmark/references/themes/formwork.jpg +0 -0
  190. package/skills/hallmark/references/themes/formwork.md +107 -0
  191. package/skills/hallmark/references/themes/furrow.jpg +0 -0
  192. package/skills/hallmark/references/themes/furrow.md +98 -0
  193. package/skills/hallmark/references/themes/garden.jpg +0 -0
  194. package/skills/hallmark/references/themes/garden.md +97 -0
  195. package/skills/hallmark/references/themes/grid.jpg +0 -0
  196. package/skills/hallmark/references/themes/grid.md +101 -0
  197. package/skills/hallmark/references/themes/halftone.jpg +0 -0
  198. package/skills/hallmark/references/themes/halftone.md +106 -0
  199. package/skills/hallmark/references/themes/hedgerow.jpg +0 -0
  200. package/skills/hallmark/references/themes/hedgerow.md +104 -0
  201. package/skills/hallmark/references/themes/hum.jpg +0 -0
  202. package/skills/hallmark/references/themes/hum.md +99 -0
  203. package/skills/hallmark/references/themes/index.json +2191 -0
  204. package/skills/hallmark/references/themes/ink.jpg +0 -0
  205. package/skills/hallmark/references/themes/ink.md +106 -0
  206. package/skills/hallmark/references/themes/kiln.jpg +0 -0
  207. package/skills/hallmark/references/themes/kiln.md +108 -0
  208. package/skills/hallmark/references/themes/loom.jpg +0 -0
  209. package/skills/hallmark/references/themes/loom.md +106 -0
  210. package/skills/hallmark/references/themes/lumen.jpg +0 -0
  211. package/skills/hallmark/references/themes/lumen.md +103 -0
  212. package/skills/hallmark/references/themes/manifesto.jpg +0 -0
  213. package/skills/hallmark/references/themes/manifesto.md +101 -0
  214. package/skills/hallmark/references/themes/midnight.jpg +0 -0
  215. package/skills/hallmark/references/themes/midnight.md +97 -0
  216. package/skills/hallmark/references/themes/newsprint.jpg +0 -0
  217. package/skills/hallmark/references/themes/newsprint.md +102 -0
  218. package/skills/hallmark/references/themes/overprint.jpg +0 -0
  219. package/skills/hallmark/references/themes/overprint.md +101 -0
  220. package/skills/hallmark/references/themes/pasteup.jpg +0 -0
  221. package/skills/hallmark/references/themes/pasteup.md +100 -0
  222. package/skills/hallmark/references/themes/piste.jpg +0 -0
  223. package/skills/hallmark/references/themes/piste.md +99 -0
  224. package/skills/hallmark/references/themes/ribbon.jpg +0 -0
  225. package/skills/hallmark/references/themes/ribbon.md +106 -0
  226. package/skills/hallmark/references/themes/rim.jpg +0 -0
  227. package/skills/hallmark/references/themes/rim.md +96 -0
  228. package/skills/hallmark/references/themes/riso.jpg +0 -0
  229. package/skills/hallmark/references/themes/riso.md +98 -0
  230. package/skills/hallmark/references/themes/solstice.jpg +0 -0
  231. package/skills/hallmark/references/themes/solstice.md +104 -0
  232. package/skills/hallmark/references/themes/specimen.jpg +0 -0
  233. package/skills/hallmark/references/themes/specimen.md +100 -0
  234. package/skills/hallmark/references/themes/spore.jpg +0 -0
  235. package/skills/hallmark/references/themes/spore.md +104 -0
  236. package/skills/hallmark/references/themes/sport.jpg +0 -0
  237. package/skills/hallmark/references/themes/sport.md +101 -0
  238. package/skills/hallmark/references/themes/squeeze.jpg +0 -0
  239. package/skills/hallmark/references/themes/squeeze.md +99 -0
  240. package/skills/hallmark/references/themes/sticker.jpg +0 -0
  241. package/skills/hallmark/references/themes/sticker.md +106 -0
  242. package/skills/hallmark/references/themes/studio.jpg +0 -0
  243. package/skills/hallmark/references/themes/studio.md +99 -0
  244. package/skills/hallmark/references/themes/terminal.jpg +0 -0
  245. package/skills/hallmark/references/themes/terminal.md +96 -0
  246. package/skills/hallmark/references/themes/terrazzo.jpg +0 -0
  247. package/skills/hallmark/references/themes/terrazzo.md +106 -0
  248. package/skills/hallmark/references/themes/ultraviolet.jpg +0 -0
  249. package/skills/hallmark/references/themes/ultraviolet.md +106 -0
  250. package/skills/hallmark/references/themes/vellum.jpg +0 -0
  251. package/skills/hallmark/references/themes/vellum.md +105 -0
  252. package/skills/hallmark/references/themes/wayfind.jpg +0 -0
  253. package/skills/hallmark/references/themes/wayfind.md +105 -0
  254. package/skills/hallmark/references/themes/werkstatt.jpg +0 -0
  255. package/skills/hallmark/references/themes/werkstatt.md +105 -0
  256. package/skills/hallmark/scripts/capture.mjs +125 -0
  257. package/skills/hallmark/scripts/fixtures/exemplars/atmospheric.html +324 -0
  258. package/skills/hallmark/scripts/fixtures/exemplars/editorial.html +363 -0
  259. package/skills/hallmark/scripts/fixtures/exemplars/modern-minimal.html +527 -0
  260. package/skills/hallmark/scripts/fixtures/exemplars/playful.html +353 -0
  261. package/skills/hallmark/scripts/fixtures/eyebrow-fail/index.html +118 -0
  262. package/skills/hallmark/scripts/fixtures/eyebrow-pass/index.html +137 -0
  263. package/skills/hallmark/scripts/fixtures/kept/after.html +27 -0
  264. package/skills/hallmark/scripts/fixtures/kept/before.html +26 -0
  265. package/skills/hallmark/scripts/fixtures/slopped.html +145 -0
  266. package/skills/hallmark/scripts/fixtures/tailwind-fail/Feature.vue +14 -0
  267. package/skills/hallmark/scripts/fixtures/tailwind-fail/Hero.tsx +33 -0
  268. package/skills/hallmark/scripts/fixtures/tailwind-fail/index.html +74 -0
  269. package/skills/hallmark/scripts/fixtures/tailwind-fail/style.css +6 -0
  270. package/skills/hallmark/scripts/fixtures/tailwind-pass/Hero.tsx +33 -0
  271. package/skills/hallmark/scripts/fixtures/tailwind-pass/index.html +57 -0
  272. package/skills/hallmark/scripts/fixtures/tailwind-pass/style.css +15 -0
  273. package/skills/hallmark/scripts/fixtures/waiver/index.html +54 -0
  274. package/skills/hallmark/scripts/imagegen.mjs +85 -0
  275. package/skills/hallmark/scripts/install-hook.mjs +70 -0
  276. package/skills/hallmark/scripts/install.mjs +141 -0
  277. package/skills/hallmark/scripts/lint-hook.mjs +49 -0
  278. package/skills/hallmark/scripts/photos.json +1 -0
  279. package/skills/hallmark/scripts/picture.mjs +71 -0
  280. package/skills/hallmark/scripts/refcheck.mjs +352 -0
  281. package/skills/hallmark/scripts/sloplint-render.mjs +1252 -0
  282. package/skills/hallmark/scripts/sloplint.mjs +4422 -0
  283. package/skills/hallmark/scripts/theme.mjs +650 -0
  284. package/skills/harness/SKILL.md +17 -8
  285. package/skills/i-have-adhd/SKILL.md +32 -12
  286. package/skills/no-ai-slop/SKILL.md +15 -1
  287. package/skills/plan-add/SKILL.md +38 -27
  288. package/skills/plan-status/SKILL.md +13 -7
  289. package/skills/plan-sync/SKILL.md +18 -12
  290. package/skills/pm-frameworks/SKILL.md +19 -10
  291. package/skills/pm-frameworks/SOURCE +7 -5
  292. package/skills/ponytail/SKILL.md +19 -14
  293. package/skills/product-analysis/SKILL.md +22 -18
  294. package/skills/product-flow/SKILL.md +14 -9
  295. package/skills/research/SKILL.md +97 -48
  296. package/skills/rl-env/SKILL.md +25 -13
  297. package/skills/rl-env/SOURCE +8 -6
  298. package/skills/tezgah-contract/SKILL.md +65 -47
  299. package/statusline.py +20 -9
  300. package/workflows/graph-review.js +14 -2
  301. package/bin/tezgah-skill.py +0 -59
  302. /package/skills/ai-research/0-autoresearch-skill/{SKILL.md → ENTRY.md} +0 -0
  303. /package/skills/ai-research/01-model-architecture/litgpt/{SKILL.md → ENTRY.md} +0 -0
  304. /package/skills/ai-research/01-model-architecture/mamba/{SKILL.md → ENTRY.md} +0 -0
  305. /package/skills/ai-research/01-model-architecture/nanogpt/{SKILL.md → ENTRY.md} +0 -0
  306. /package/skills/ai-research/01-model-architecture/rwkv/{SKILL.md → ENTRY.md} +0 -0
  307. /package/skills/ai-research/01-model-architecture/torchtitan/{SKILL.md → ENTRY.md} +0 -0
  308. /package/skills/ai-research/02-tokenization/huggingface-tokenizers/{SKILL.md → ENTRY.md} +0 -0
  309. /package/skills/ai-research/02-tokenization/sentencepiece/{SKILL.md → ENTRY.md} +0 -0
  310. /package/skills/ai-research/03-fine-tuning/axolotl/{SKILL.md → ENTRY.md} +0 -0
  311. /package/skills/ai-research/03-fine-tuning/llama-factory/{SKILL.md → ENTRY.md} +0 -0
  312. /package/skills/ai-research/03-fine-tuning/peft/{SKILL.md → ENTRY.md} +0 -0
  313. /package/skills/ai-research/03-fine-tuning/unsloth/{SKILL.md → ENTRY.md} +0 -0
  314. /package/skills/ai-research/04-mechanistic-interpretability/nnsight/{SKILL.md → ENTRY.md} +0 -0
  315. /package/skills/ai-research/04-mechanistic-interpretability/pyvene/{SKILL.md → ENTRY.md} +0 -0
  316. /package/skills/ai-research/04-mechanistic-interpretability/saelens/{SKILL.md → ENTRY.md} +0 -0
  317. /package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/{SKILL.md → ENTRY.md} +0 -0
  318. /package/skills/ai-research/05-data-processing/nemo-curator/{SKILL.md → ENTRY.md} +0 -0
  319. /package/skills/ai-research/05-data-processing/ray-data/{SKILL.md → ENTRY.md} +0 -0
  320. /package/skills/ai-research/06-post-training/grpo-rl-training/{SKILL.md → ENTRY.md} +0 -0
  321. /package/skills/ai-research/06-post-training/miles/{SKILL.md → ENTRY.md} +0 -0
  322. /package/skills/ai-research/06-post-training/openrlhf/{SKILL.md → ENTRY.md} +0 -0
  323. /package/skills/ai-research/06-post-training/simpo/{SKILL.md → ENTRY.md} +0 -0
  324. /package/skills/ai-research/06-post-training/slime/{SKILL.md → ENTRY.md} +0 -0
  325. /package/skills/ai-research/06-post-training/torchforge/{SKILL.md → ENTRY.md} +0 -0
  326. /package/skills/ai-research/06-post-training/trl-fine-tuning/{SKILL.md → ENTRY.md} +0 -0
  327. /package/skills/ai-research/06-post-training/verl/{SKILL.md → ENTRY.md} +0 -0
  328. /package/skills/ai-research/07-safety-alignment/constitutional-ai/{SKILL.md → ENTRY.md} +0 -0
  329. /package/skills/ai-research/07-safety-alignment/llamaguard/{SKILL.md → ENTRY.md} +0 -0
  330. /package/skills/ai-research/07-safety-alignment/nemo-guardrails/{SKILL.md → ENTRY.md} +0 -0
  331. /package/skills/ai-research/07-safety-alignment/prompt-guard/{SKILL.md → ENTRY.md} +0 -0
  332. /package/skills/ai-research/08-distributed-training/accelerate/{SKILL.md → ENTRY.md} +0 -0
  333. /package/skills/ai-research/08-distributed-training/deepspeed/{SKILL.md → ENTRY.md} +0 -0
  334. /package/skills/ai-research/08-distributed-training/megatron-core/{SKILL.md → ENTRY.md} +0 -0
  335. /package/skills/ai-research/08-distributed-training/pytorch-fsdp2/{SKILL.md → ENTRY.md} +0 -0
  336. /package/skills/ai-research/08-distributed-training/pytorch-lightning/{SKILL.md → ENTRY.md} +0 -0
  337. /package/skills/ai-research/08-distributed-training/ray-train/{SKILL.md → ENTRY.md} +0 -0
  338. /package/skills/ai-research/09-infrastructure/lambda-labs/{SKILL.md → ENTRY.md} +0 -0
  339. /package/skills/ai-research/09-infrastructure/modal/{SKILL.md → ENTRY.md} +0 -0
  340. /package/skills/ai-research/09-infrastructure/skypilot/{SKILL.md → ENTRY.md} +0 -0
  341. /package/skills/ai-research/10-optimization/awq/{SKILL.md → ENTRY.md} +0 -0
  342. /package/skills/ai-research/10-optimization/bitsandbytes/{SKILL.md → ENTRY.md} +0 -0
  343. /package/skills/ai-research/10-optimization/flash-attention/{SKILL.md → ENTRY.md} +0 -0
  344. /package/skills/ai-research/10-optimization/gguf/{SKILL.md → ENTRY.md} +0 -0
  345. /package/skills/ai-research/10-optimization/gptq/{SKILL.md → ENTRY.md} +0 -0
  346. /package/skills/ai-research/10-optimization/hqq/{SKILL.md → ENTRY.md} +0 -0
  347. /package/skills/ai-research/10-optimization/ml-training-recipes/{SKILL.md → ENTRY.md} +0 -0
  348. /package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
  349. /package/skills/ai-research/11-evaluation/lm-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
  350. /package/skills/ai-research/11-evaluation/nemo-evaluator/{SKILL.md → ENTRY.md} +0 -0
  351. /package/skills/ai-research/12-inference-serving/llama-cpp/{SKILL.md → ENTRY.md} +0 -0
  352. /package/skills/ai-research/12-inference-serving/sglang/{SKILL.md → ENTRY.md} +0 -0
  353. /package/skills/ai-research/12-inference-serving/tensorrt-llm/{SKILL.md → ENTRY.md} +0 -0
  354. /package/skills/ai-research/12-inference-serving/vllm/{SKILL.md → ENTRY.md} +0 -0
  355. /package/skills/ai-research/13-mlops/mlflow/{SKILL.md → ENTRY.md} +0 -0
  356. /package/skills/ai-research/13-mlops/swanlab/{SKILL.md → ENTRY.md} +0 -0
  357. /package/skills/ai-research/13-mlops/tensorboard/{SKILL.md → ENTRY.md} +0 -0
  358. /package/skills/ai-research/13-mlops/weights-and-biases/{SKILL.md → ENTRY.md} +0 -0
  359. /package/skills/ai-research/14-agents/a-evolve/{SKILL.md → ENTRY.md} +0 -0
  360. /package/skills/ai-research/14-agents/autogpt/{SKILL.md → ENTRY.md} +0 -0
  361. /package/skills/ai-research/14-agents/crewai/{SKILL.md → ENTRY.md} +0 -0
  362. /package/skills/ai-research/14-agents/langchain/{SKILL.md → ENTRY.md} +0 -0
  363. /package/skills/ai-research/14-agents/llamaindex/{SKILL.md → ENTRY.md} +0 -0
  364. /package/skills/ai-research/15-rag/chroma/{SKILL.md → ENTRY.md} +0 -0
  365. /package/skills/ai-research/15-rag/faiss/{SKILL.md → ENTRY.md} +0 -0
  366. /package/skills/ai-research/15-rag/pinecone/{SKILL.md → ENTRY.md} +0 -0
  367. /package/skills/ai-research/15-rag/qdrant/{SKILL.md → ENTRY.md} +0 -0
  368. /package/skills/ai-research/15-rag/sentence-transformers/{SKILL.md → ENTRY.md} +0 -0
  369. /package/skills/ai-research/16-prompt-engineering/dspy/{SKILL.md → ENTRY.md} +0 -0
  370. /package/skills/ai-research/16-prompt-engineering/guidance/{SKILL.md → ENTRY.md} +0 -0
  371. /package/skills/ai-research/16-prompt-engineering/instructor/{SKILL.md → ENTRY.md} +0 -0
  372. /package/skills/ai-research/16-prompt-engineering/outlines/{SKILL.md → ENTRY.md} +0 -0
  373. /package/skills/ai-research/17-observability/langsmith/{SKILL.md → ENTRY.md} +0 -0
  374. /package/skills/ai-research/17-observability/phoenix/{SKILL.md → ENTRY.md} +0 -0
  375. /package/skills/ai-research/18-multimodal/audiocraft/{SKILL.md → ENTRY.md} +0 -0
  376. /package/skills/ai-research/18-multimodal/blip-2/{SKILL.md → ENTRY.md} +0 -0
  377. /package/skills/ai-research/18-multimodal/clip/{SKILL.md → ENTRY.md} +0 -0
  378. /package/skills/ai-research/18-multimodal/cosmos-policy/{SKILL.md → ENTRY.md} +0 -0
  379. /package/skills/ai-research/18-multimodal/llava/{SKILL.md → ENTRY.md} +0 -0
  380. /package/skills/ai-research/18-multimodal/openpi/{SKILL.md → ENTRY.md} +0 -0
  381. /package/skills/ai-research/18-multimodal/openvla-oft/{SKILL.md → ENTRY.md} +0 -0
  382. /package/skills/ai-research/18-multimodal/segment-anything/{SKILL.md → ENTRY.md} +0 -0
  383. /package/skills/ai-research/18-multimodal/stable-diffusion/{SKILL.md → ENTRY.md} +0 -0
  384. /package/skills/ai-research/18-multimodal/whisper/{SKILL.md → ENTRY.md} +0 -0
  385. /package/skills/ai-research/19-emerging-techniques/knowledge-distillation/{SKILL.md → ENTRY.md} +0 -0
  386. /package/skills/ai-research/19-emerging-techniques/long-context/{SKILL.md → ENTRY.md} +0 -0
  387. /package/skills/ai-research/19-emerging-techniques/model-merging/{SKILL.md → ENTRY.md} +0 -0
  388. /package/skills/ai-research/19-emerging-techniques/model-pruning/{SKILL.md → ENTRY.md} +0 -0
  389. /package/skills/ai-research/19-emerging-techniques/moe-training/{SKILL.md → ENTRY.md} +0 -0
  390. /package/skills/ai-research/19-emerging-techniques/speculative-decoding/{SKILL.md → ENTRY.md} +0 -0
  391. /package/skills/ai-research/20-ml-paper-writing/academic-plotting/{SKILL.md → ENTRY.md} +0 -0
  392. /package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/{SKILL.md → ENTRY.md} +0 -0
  393. /package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/{SKILL.md → ENTRY.md} +0 -0
  394. /package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/{SKILL.md → ENTRY.md} +0 -0
  395. /package/skills/ai-research/21-research-ideation/brainstorming-research-ideas/{SKILL.md → ENTRY.md} +0 -0
  396. /package/skills/ai-research/21-research-ideation/creative-thinking-for-research/{SKILL.md → ENTRY.md} +0 -0
  397. /package/skills/ai-research/22-agent-native-research-artifact/compiler/{SKILL.md → ENTRY.md} +0 -0
  398. /package/skills/ai-research/22-agent-native-research-artifact/research-manager/{SKILL.md → ENTRY.md} +0 -0
  399. /package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/{SKILL.md → ENTRY.md} +0 -0
  400. /package/skills/design-library/adaptive-interfaces/skills/colour-independence/{SKILL.md → ENTRY.md} +0 -0
  401. /package/skills/design-library/adaptive-interfaces/skills/flexible-typography/{SKILL.md → ENTRY.md} +0 -0
  402. /package/skills/design-library/adaptive-interfaces/skills/information-density/{SKILL.md → ENTRY.md} +0 -0
  403. /package/skills/design-library/adaptive-interfaces/skills/preference-audit/{SKILL.md → ENTRY.md} +0 -0
  404. /package/skills/design-library/adaptive-interfaces/skills/responsive-accessibility/{SKILL.md → ENTRY.md} +0 -0
  405. /package/skills/design-library/adaptive-interfaces/skills/responsive-review/{SKILL.md → ENTRY.md} +0 -0
  406. /package/skills/design-library/adaptive-interfaces/skills/simplified-views/{SKILL.md → ENTRY.md} +0 -0
  407. /package/skills/design-library/adaptive-interfaces/skills/specify/{SKILL.md → ENTRY.md} +0 -0
  408. /package/skills/design-library/adaptive-interfaces/skills/user-preference-respect/{SKILL.md → ENTRY.md} +0 -0
  409. /package/skills/design-library/cognitive-accessibility/skills/adaptive-personalisation/{SKILL.md → ENTRY.md} +0 -0
  410. /package/skills/design-library/cognitive-accessibility/skills/assess-load/{SKILL.md → ENTRY.md} +0 -0
  411. /package/skills/design-library/cognitive-accessibility/skills/cognitive-load-assessment/{SKILL.md → ENTRY.md} +0 -0
  412. /package/skills/design-library/cognitive-accessibility/skills/contextual-help-design/{SKILL.md → ENTRY.md} +0 -0
  413. /package/skills/design-library/cognitive-accessibility/skills/error-prevention-recovery/{SKILL.md → ENTRY.md} +0 -0
  414. /package/skills/design-library/cognitive-accessibility/skills/focus-attention-design/{SKILL.md → ENTRY.md} +0 -0
  415. /package/skills/design-library/cognitive-accessibility/skills/memory-load-reduction/{SKILL.md → ENTRY.md} +0 -0
  416. /package/skills/design-library/cognitive-accessibility/skills/plain-language-design/{SKILL.md → ENTRY.md} +0 -0
  417. /package/skills/design-library/cognitive-accessibility/skills/review/{SKILL.md → ENTRY.md} +0 -0
  418. /package/skills/design-library/cognitive-accessibility/skills/simplify/{SKILL.md → ENTRY.md} +0 -0
  419. /package/skills/design-library/cognitive-accessibility/skills/wayfinding-navigation/{SKILL.md → ENTRY.md} +0 -0
  420. /package/skills/design-library/design-research/skills/behavioural-analytics/{SKILL.md → ENTRY.md} +0 -0
  421. /package/skills/design-library/design-research/skills/qual-quant-triangulation/{SKILL.md → ENTRY.md} +0 -0
  422. /package/skills/design-library/inclusive-interaction/skills/audit/{SKILL.md → ENTRY.md} +0 -0
  423. /package/skills/design-library/inclusive-interaction/skills/design-flow/{SKILL.md → ENTRY.md} +0 -0
  424. /package/skills/design-library/inclusive-interaction/skills/feedback-and-status/{SKILL.md → ENTRY.md} +0 -0
  425. /package/skills/design-library/inclusive-interaction/skills/gesture-alternatives/{SKILL.md → ENTRY.md} +0 -0
  426. /package/skills/design-library/inclusive-interaction/skills/keyboard-navigation/{SKILL.md → ENTRY.md} +0 -0
  427. /package/skills/design-library/inclusive-interaction/skills/keyboard-review/{SKILL.md → ENTRY.md} +0 -0
  428. /package/skills/design-library/inclusive-interaction/skills/motion-sensitivity/{SKILL.md → ENTRY.md} +0 -0
  429. /package/skills/design-library/inclusive-interaction/skills/multi-modal-input/{SKILL.md → ENTRY.md} +0 -0
  430. /package/skills/design-library/inclusive-interaction/skills/touch-target-design/{SKILL.md → ENTRY.md} +0 -0
  431. /package/skills/design-library/inclusive-interaction/skills/voice-interaction/{SKILL.md → ENTRY.md} +0 -0
  432. /package/skills/design-library/visual-critique/skills/critique-affordance/{SKILL.md → ENTRY.md} +0 -0
  433. /package/skills/design-library/visual-critique/skills/critique-brand-consistency/{SKILL.md → ENTRY.md} +0 -0
  434. /package/skills/design-library/visual-critique/skills/critique-color/{SKILL.md → ENTRY.md} +0 -0
  435. /package/skills/design-library/visual-critique/skills/critique-composition/{SKILL.md → ENTRY.md} +0 -0
  436. /package/skills/design-library/visual-critique/skills/critique-information-density/{SKILL.md → ENTRY.md} +0 -0
  437. /package/skills/design-library/visual-critique/skills/critique-typography/{SKILL.md → ENTRY.md} +0 -0
  438. /package/skills/design-library/visual-critique/skills/critique-visual-hierarchy/{SKILL.md → ENTRY.md} +0 -0
  439. /package/skills/pm-frameworks/intended-vs-implemented/{SKILL.md → ENTRY.md} +0 -0
  440. /package/skills/pm-frameworks/opportunity-solution-tree/{SKILL.md → ENTRY.md} +0 -0
  441. /package/skills/rl-env/generate-nemo-gym-env/{SKILL.md → ENTRY.md} +0 -0
  442. /package/skills/rl-env/generate-openenv-env/{SKILL.md → ENTRY.md} +0 -0
  443. /package/skills/rl-env/generate-ors-env/{SKILL.md → ENTRY.md} +0 -0
  444. /package/skills/rl-env/generate-verifiers-env/{SKILL.md → ENTRY.md} +0 -0
  445. /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()
@@ -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