@r1z4x/tezgah 1.9.0 → 1.11.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 (429) hide show
  1. package/CHANGELOG.md +98 -1
  2. package/MANIFEST +382 -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 +1 -1
  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 +216 -40
  15. package/bin/tezgah-review-quote +185 -0
  16. package/bin/tezgah-setup +136 -33
  17. package/bin/tezgah-skill +1 -1
  18. package/bin/tezgah-status +201 -3
  19. package/bin/tezgah-task +203 -3
  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 +2 -2
  24. package/docs/clarity-baseline.json +2 -2
  25. package/docs/contract.md +87 -42
  26. package/docs/environment.md +5 -3
  27. package/docs/evidence.md +28 -9
  28. package/docs/feature-audit.md +2 -2
  29. package/docs/gate.md +54 -31
  30. package/docs/glossary.md +5 -2
  31. package/docs/hosts.md +17 -3
  32. package/docs/index.json +34 -8
  33. package/docs/judge.md +4 -2
  34. package/docs/layers.md +3 -3
  35. package/docs/models.md +31 -9
  36. package/docs/operations.md +148 -8
  37. package/docs/research.md +786 -5
  38. package/docs/skills.md +137 -13
  39. package/docs/status-line.md +115 -1
  40. package/hooks/hooks.json +1 -1
  41. package/hooks/lang/en.json +10 -0
  42. package/hooks/lang/tr.json +11 -1
  43. package/hooks/tezgah_agents.py +38 -22
  44. package/hooks/tezgah_apps.py +18 -0
  45. package/hooks/tezgah_components.py +4 -0
  46. package/hooks/tezgah_context.py +189 -50
  47. package/hooks/tezgah_extension.py +337 -0
  48. package/hooks/tezgah_gate.py +188 -56
  49. package/hooks/tezgah_integrity.py +652 -105
  50. package/hooks/tezgah_judge.py +6 -4
  51. package/hooks/tezgah_models.py +59 -19
  52. package/hooks/tezgah_orca.py +1 -1
  53. package/hooks/tezgah_paths.py +102 -6
  54. package/hooks/tezgah_policy.py +94 -45
  55. package/hooks/tezgah_rank.py +9 -6
  56. package/hooks/tezgah_replay.py +4 -4
  57. package/hooks/tezgah_research.py +1260 -65
  58. package/hooks/tezgah_research_capture.py +1544 -0
  59. package/hooks/tezgah_research_evidence.py +842 -0
  60. package/hooks/tezgah_research_stats.py +258 -0
  61. package/hooks/tezgah_skill_pick.py +408 -106
  62. package/hooks/tezgah_stopspec.py +8 -1
  63. package/hooks/tezgah_task.py +12 -4
  64. package/hooks/tezgah_taste_ledger.py +1 -1
  65. package/hosts/cursor/hook.py +29 -2
  66. package/hosts/opencode/plugins/tezgah.js +307 -46
  67. package/hosts/opencode/tui/cli/tui.tsx +100 -0
  68. package/hosts/opencode/tui/tezgah-tui.tsx +2 -1
  69. package/output-styles/tezgah.md +10 -10
  70. package/package.json +1 -1
  71. package/packaging/install.ps1 +36 -3
  72. package/packaging/install.sh +13 -3
  73. package/packaging/upgrade.sh +61 -7
  74. package/skills/ai-research/SKILL.md +21 -14
  75. package/skills/ai-research/SOURCE +7 -5
  76. package/skills/ai-research/index/1-frame.md +6 -6
  77. package/skills/ai-research/index/2-data.md +9 -9
  78. package/skills/ai-research/index/3-train.md +36 -36
  79. package/skills/ai-research/index/4-measure.md +13 -13
  80. package/skills/ai-research/index/5-run.md +11 -11
  81. package/skills/ai-research/index/6-write.md +23 -23
  82. package/skills/ai-research/library.json +104 -104
  83. package/skills/analyze-app/SKILL.md +17 -12
  84. package/skills/design-contract/SKILL.md +42 -7
  85. package/skills/design-contract/data-views.md +2 -2
  86. package/skills/design-library/EVALS.md +1 -1
  87. package/skills/design-library/INDEX.md +39 -39
  88. package/skills/design-library/SKILL.md +19 -13
  89. package/skills/design-library/SOURCE +45 -43
  90. package/skills/feature-audit/SKILL.md +20 -14
  91. package/skills/hallmark/LICENSE +21 -0
  92. package/skills/hallmark/SKILL.md +154 -0
  93. package/skills/hallmark/SOURCE +207 -0
  94. package/skills/hallmark/agents/openai.yaml +13 -0
  95. package/skills/hallmark/references/anatomy.md +71 -0
  96. package/skills/hallmark/references/brand-truth.md +40 -0
  97. package/skills/hallmark/references/comp.md +104 -0
  98. package/skills/hallmark/references/craft.md +116 -0
  99. package/skills/hallmark/references/dark-mode.md +49 -0
  100. package/skills/hallmark/references/data-viz.md +48 -0
  101. package/skills/hallmark/references/design-md.md +53 -0
  102. package/skills/hallmark/references/direction.md +52 -0
  103. package/skills/hallmark/references/export-formats.md +80 -0
  104. package/skills/hallmark/references/flow.md +131 -0
  105. package/skills/hallmark/references/fonts.md +58 -0
  106. package/skills/hallmark/references/gates.md +243 -0
  107. package/skills/hallmark/references/genres/atmospheric.md +24 -0
  108. package/skills/hallmark/references/genres/editorial.md +24 -0
  109. package/skills/hallmark/references/genres/modern-minimal.md +24 -0
  110. package/skills/hallmark/references/genres/playful.md +24 -0
  111. package/skills/hallmark/references/imagery.md +99 -0
  112. package/skills/hallmark/references/interaction-and-states.md +89 -0
  113. package/skills/hallmark/references/layout.md +104 -0
  114. package/skills/hallmark/references/macrostructures/01-bento-grid.md +21 -0
  115. package/skills/hallmark/references/macrostructures/02-long-document.md +35 -0
  116. package/skills/hallmark/references/macrostructures/03-marquee-hero.md +21 -0
  117. package/skills/hallmark/references/macrostructures/04-stat-led.md +21 -0
  118. package/skills/hallmark/references/macrostructures/05-workbench.md +21 -0
  119. package/skills/hallmark/references/macrostructures/06-conversational-faq.md +28 -0
  120. package/skills/hallmark/references/macrostructures/07-manifesto.md +28 -0
  121. package/skills/hallmark/references/macrostructures/08-photographic.md +21 -0
  122. package/skills/hallmark/references/macrostructures/09-quote-led.md +21 -0
  123. package/skills/hallmark/references/macrostructures/10-specimen.md +21 -0
  124. package/skills/hallmark/references/macrostructures/11-catalogue.md +21 -0
  125. package/skills/hallmark/references/macrostructures/12-letter.md +21 -0
  126. package/skills/hallmark/references/macrostructures/13-index-first.md +21 -0
  127. package/skills/hallmark/references/macrostructures/14-narrative-workflow.md +21 -0
  128. package/skills/hallmark/references/macrostructures/15-split-studio.md +21 -0
  129. package/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
  130. package/skills/hallmark/references/macrostructures/17-type-specimen.md +19 -0
  131. package/skills/hallmark/references/macrostructures/18-portfolio-grid.md +21 -0
  132. package/skills/hallmark/references/macrostructures/19-map-diagram.md +21 -0
  133. package/skills/hallmark/references/macrostructures/20-ecosystem-index.md +19 -0
  134. package/skills/hallmark/references/macrostructures/21-component-playground.md +21 -0
  135. package/skills/hallmark/references/macrostructures.md +119 -0
  136. package/skills/hallmark/references/microinteractions.md +80 -0
  137. package/skills/hallmark/references/patterns.md +202 -0
  138. package/skills/hallmark/references/redesign.md +88 -0
  139. package/skills/hallmark/references/responsive.md +46 -0
  140. package/skills/hallmark/references/review-finish.md +19 -0
  141. package/skills/hallmark/references/review-hero.md +20 -0
  142. package/skills/hallmark/references/scroll-choreography.md +110 -0
  143. package/skills/hallmark/references/texture.md +90 -0
  144. package/skills/hallmark/references/themes/README.md +58 -0
  145. package/skills/hallmark/references/themes/almanac.jpg +0 -0
  146. package/skills/hallmark/references/themes/almanac.md +103 -0
  147. package/skills/hallmark/references/themes/arcade.jpg +0 -0
  148. package/skills/hallmark/references/themes/arcade.md +102 -0
  149. package/skills/hallmark/references/themes/atelier.jpg +0 -0
  150. package/skills/hallmark/references/themes/atelier.md +98 -0
  151. package/skills/hallmark/references/themes/bloom.jpg +0 -0
  152. package/skills/hallmark/references/themes/bloom.md +102 -0
  153. package/skills/hallmark/references/themes/blueprint.jpg +0 -0
  154. package/skills/hallmark/references/themes/blueprint.md +103 -0
  155. package/skills/hallmark/references/themes/broadside.jpg +0 -0
  156. package/skills/hallmark/references/themes/broadside.md +108 -0
  157. package/skills/hallmark/references/themes/brutal.jpg +0 -0
  158. package/skills/hallmark/references/themes/brutal.md +95 -0
  159. package/skills/hallmark/references/themes/carnival.jpg +0 -0
  160. package/skills/hallmark/references/themes/carnival.md +104 -0
  161. package/skills/hallmark/references/themes/cobalt.jpg +0 -0
  162. package/skills/hallmark/references/themes/cobalt.md +101 -0
  163. package/skills/hallmark/references/themes/console.jpg +0 -0
  164. package/skills/hallmark/references/themes/console.md +99 -0
  165. package/skills/hallmark/references/themes/coral.jpg +0 -0
  166. package/skills/hallmark/references/themes/coral.md +102 -0
  167. package/skills/hallmark/references/themes/darkroom.jpg +0 -0
  168. package/skills/hallmark/references/themes/darkroom.md +99 -0
  169. package/skills/hallmark/references/themes/dose.jpg +0 -0
  170. package/skills/hallmark/references/themes/dose.md +98 -0
  171. package/skills/hallmark/references/themes/dusk.jpg +0 -0
  172. package/skills/hallmark/references/themes/dusk.md +105 -0
  173. package/skills/hallmark/references/themes/editorial.jpg +0 -0
  174. package/skills/hallmark/references/themes/editorial.md +97 -0
  175. package/skills/hallmark/references/themes/formwork.jpg +0 -0
  176. package/skills/hallmark/references/themes/formwork.md +107 -0
  177. package/skills/hallmark/references/themes/furrow.jpg +0 -0
  178. package/skills/hallmark/references/themes/furrow.md +98 -0
  179. package/skills/hallmark/references/themes/garden.jpg +0 -0
  180. package/skills/hallmark/references/themes/garden.md +97 -0
  181. package/skills/hallmark/references/themes/grid.jpg +0 -0
  182. package/skills/hallmark/references/themes/grid.md +101 -0
  183. package/skills/hallmark/references/themes/halftone.jpg +0 -0
  184. package/skills/hallmark/references/themes/halftone.md +106 -0
  185. package/skills/hallmark/references/themes/hedgerow.jpg +0 -0
  186. package/skills/hallmark/references/themes/hedgerow.md +104 -0
  187. package/skills/hallmark/references/themes/hum.jpg +0 -0
  188. package/skills/hallmark/references/themes/hum.md +99 -0
  189. package/skills/hallmark/references/themes/index.json +2191 -0
  190. package/skills/hallmark/references/themes/ink.jpg +0 -0
  191. package/skills/hallmark/references/themes/ink.md +106 -0
  192. package/skills/hallmark/references/themes/kiln.jpg +0 -0
  193. package/skills/hallmark/references/themes/kiln.md +108 -0
  194. package/skills/hallmark/references/themes/loom.jpg +0 -0
  195. package/skills/hallmark/references/themes/loom.md +106 -0
  196. package/skills/hallmark/references/themes/lumen.jpg +0 -0
  197. package/skills/hallmark/references/themes/lumen.md +103 -0
  198. package/skills/hallmark/references/themes/manifesto.jpg +0 -0
  199. package/skills/hallmark/references/themes/manifesto.md +101 -0
  200. package/skills/hallmark/references/themes/midnight.jpg +0 -0
  201. package/skills/hallmark/references/themes/midnight.md +97 -0
  202. package/skills/hallmark/references/themes/newsprint.jpg +0 -0
  203. package/skills/hallmark/references/themes/newsprint.md +102 -0
  204. package/skills/hallmark/references/themes/overprint.jpg +0 -0
  205. package/skills/hallmark/references/themes/overprint.md +101 -0
  206. package/skills/hallmark/references/themes/pasteup.jpg +0 -0
  207. package/skills/hallmark/references/themes/pasteup.md +100 -0
  208. package/skills/hallmark/references/themes/piste.jpg +0 -0
  209. package/skills/hallmark/references/themes/piste.md +99 -0
  210. package/skills/hallmark/references/themes/ribbon.jpg +0 -0
  211. package/skills/hallmark/references/themes/ribbon.md +106 -0
  212. package/skills/hallmark/references/themes/rim.jpg +0 -0
  213. package/skills/hallmark/references/themes/rim.md +96 -0
  214. package/skills/hallmark/references/themes/riso.jpg +0 -0
  215. package/skills/hallmark/references/themes/riso.md +98 -0
  216. package/skills/hallmark/references/themes/solstice.jpg +0 -0
  217. package/skills/hallmark/references/themes/solstice.md +104 -0
  218. package/skills/hallmark/references/themes/specimen.jpg +0 -0
  219. package/skills/hallmark/references/themes/specimen.md +100 -0
  220. package/skills/hallmark/references/themes/spore.jpg +0 -0
  221. package/skills/hallmark/references/themes/spore.md +104 -0
  222. package/skills/hallmark/references/themes/sport.jpg +0 -0
  223. package/skills/hallmark/references/themes/sport.md +101 -0
  224. package/skills/hallmark/references/themes/squeeze.jpg +0 -0
  225. package/skills/hallmark/references/themes/squeeze.md +99 -0
  226. package/skills/hallmark/references/themes/sticker.jpg +0 -0
  227. package/skills/hallmark/references/themes/sticker.md +106 -0
  228. package/skills/hallmark/references/themes/studio.jpg +0 -0
  229. package/skills/hallmark/references/themes/studio.md +99 -0
  230. package/skills/hallmark/references/themes/terminal.jpg +0 -0
  231. package/skills/hallmark/references/themes/terminal.md +96 -0
  232. package/skills/hallmark/references/themes/terrazzo.jpg +0 -0
  233. package/skills/hallmark/references/themes/terrazzo.md +106 -0
  234. package/skills/hallmark/references/themes/ultraviolet.jpg +0 -0
  235. package/skills/hallmark/references/themes/ultraviolet.md +106 -0
  236. package/skills/hallmark/references/themes/vellum.jpg +0 -0
  237. package/skills/hallmark/references/themes/vellum.md +105 -0
  238. package/skills/hallmark/references/themes/wayfind.jpg +0 -0
  239. package/skills/hallmark/references/themes/wayfind.md +105 -0
  240. package/skills/hallmark/references/themes/werkstatt.jpg +0 -0
  241. package/skills/hallmark/references/themes/werkstatt.md +105 -0
  242. package/skills/hallmark/scripts/capture.mjs +125 -0
  243. package/skills/hallmark/scripts/fixtures/exemplars/atmospheric.html +324 -0
  244. package/skills/hallmark/scripts/fixtures/exemplars/editorial.html +363 -0
  245. package/skills/hallmark/scripts/fixtures/exemplars/modern-minimal.html +527 -0
  246. package/skills/hallmark/scripts/fixtures/exemplars/playful.html +353 -0
  247. package/skills/hallmark/scripts/fixtures/eyebrow-fail/index.html +118 -0
  248. package/skills/hallmark/scripts/fixtures/eyebrow-pass/index.html +137 -0
  249. package/skills/hallmark/scripts/fixtures/kept/after.html +27 -0
  250. package/skills/hallmark/scripts/fixtures/kept/before.html +26 -0
  251. package/skills/hallmark/scripts/fixtures/slopped.html +145 -0
  252. package/skills/hallmark/scripts/fixtures/tailwind-fail/Feature.vue +14 -0
  253. package/skills/hallmark/scripts/fixtures/tailwind-fail/Hero.tsx +33 -0
  254. package/skills/hallmark/scripts/fixtures/tailwind-fail/index.html +74 -0
  255. package/skills/hallmark/scripts/fixtures/tailwind-fail/style.css +6 -0
  256. package/skills/hallmark/scripts/fixtures/tailwind-pass/Hero.tsx +33 -0
  257. package/skills/hallmark/scripts/fixtures/tailwind-pass/index.html +57 -0
  258. package/skills/hallmark/scripts/fixtures/tailwind-pass/style.css +15 -0
  259. package/skills/hallmark/scripts/fixtures/waiver/index.html +54 -0
  260. package/skills/hallmark/scripts/imagegen.mjs +85 -0
  261. package/skills/hallmark/scripts/install-hook.mjs +70 -0
  262. package/skills/hallmark/scripts/install.mjs +141 -0
  263. package/skills/hallmark/scripts/lint-hook.mjs +49 -0
  264. package/skills/hallmark/scripts/photos.json +1 -0
  265. package/skills/hallmark/scripts/picture.mjs +71 -0
  266. package/skills/hallmark/scripts/refcheck.mjs +352 -0
  267. package/skills/hallmark/scripts/sloplint-render.mjs +1252 -0
  268. package/skills/hallmark/scripts/sloplint.mjs +4422 -0
  269. package/skills/hallmark/scripts/theme.mjs +650 -0
  270. package/skills/harness/SKILL.md +15 -8
  271. package/skills/i-have-adhd/SKILL.md +22 -11
  272. package/skills/no-ai-slop/SKILL.md +15 -1
  273. package/skills/plan-add/SKILL.md +33 -26
  274. package/skills/plan-status/SKILL.md +13 -7
  275. package/skills/plan-sync/SKILL.md +18 -12
  276. package/skills/pm-frameworks/SKILL.md +19 -10
  277. package/skills/pm-frameworks/SOURCE +7 -5
  278. package/skills/ponytail/SKILL.md +19 -14
  279. package/skills/product-analysis/SKILL.md +22 -18
  280. package/skills/product-flow/SKILL.md +14 -9
  281. package/skills/research/SKILL.md +75 -39
  282. package/skills/rl-env/SKILL.md +25 -13
  283. package/skills/rl-env/SOURCE +8 -6
  284. package/skills/tezgah-contract/SKILL.md +47 -42
  285. package/bin/tezgah-skill.py +0 -59
  286. /package/skills/ai-research/0-autoresearch-skill/{SKILL.md → ENTRY.md} +0 -0
  287. /package/skills/ai-research/01-model-architecture/litgpt/{SKILL.md → ENTRY.md} +0 -0
  288. /package/skills/ai-research/01-model-architecture/mamba/{SKILL.md → ENTRY.md} +0 -0
  289. /package/skills/ai-research/01-model-architecture/nanogpt/{SKILL.md → ENTRY.md} +0 -0
  290. /package/skills/ai-research/01-model-architecture/rwkv/{SKILL.md → ENTRY.md} +0 -0
  291. /package/skills/ai-research/01-model-architecture/torchtitan/{SKILL.md → ENTRY.md} +0 -0
  292. /package/skills/ai-research/02-tokenization/huggingface-tokenizers/{SKILL.md → ENTRY.md} +0 -0
  293. /package/skills/ai-research/02-tokenization/sentencepiece/{SKILL.md → ENTRY.md} +0 -0
  294. /package/skills/ai-research/03-fine-tuning/axolotl/{SKILL.md → ENTRY.md} +0 -0
  295. /package/skills/ai-research/03-fine-tuning/llama-factory/{SKILL.md → ENTRY.md} +0 -0
  296. /package/skills/ai-research/03-fine-tuning/peft/{SKILL.md → ENTRY.md} +0 -0
  297. /package/skills/ai-research/03-fine-tuning/unsloth/{SKILL.md → ENTRY.md} +0 -0
  298. /package/skills/ai-research/04-mechanistic-interpretability/nnsight/{SKILL.md → ENTRY.md} +0 -0
  299. /package/skills/ai-research/04-mechanistic-interpretability/pyvene/{SKILL.md → ENTRY.md} +0 -0
  300. /package/skills/ai-research/04-mechanistic-interpretability/saelens/{SKILL.md → ENTRY.md} +0 -0
  301. /package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/{SKILL.md → ENTRY.md} +0 -0
  302. /package/skills/ai-research/05-data-processing/nemo-curator/{SKILL.md → ENTRY.md} +0 -0
  303. /package/skills/ai-research/05-data-processing/ray-data/{SKILL.md → ENTRY.md} +0 -0
  304. /package/skills/ai-research/06-post-training/grpo-rl-training/{SKILL.md → ENTRY.md} +0 -0
  305. /package/skills/ai-research/06-post-training/miles/{SKILL.md → ENTRY.md} +0 -0
  306. /package/skills/ai-research/06-post-training/openrlhf/{SKILL.md → ENTRY.md} +0 -0
  307. /package/skills/ai-research/06-post-training/simpo/{SKILL.md → ENTRY.md} +0 -0
  308. /package/skills/ai-research/06-post-training/slime/{SKILL.md → ENTRY.md} +0 -0
  309. /package/skills/ai-research/06-post-training/torchforge/{SKILL.md → ENTRY.md} +0 -0
  310. /package/skills/ai-research/06-post-training/trl-fine-tuning/{SKILL.md → ENTRY.md} +0 -0
  311. /package/skills/ai-research/06-post-training/verl/{SKILL.md → ENTRY.md} +0 -0
  312. /package/skills/ai-research/07-safety-alignment/constitutional-ai/{SKILL.md → ENTRY.md} +0 -0
  313. /package/skills/ai-research/07-safety-alignment/llamaguard/{SKILL.md → ENTRY.md} +0 -0
  314. /package/skills/ai-research/07-safety-alignment/nemo-guardrails/{SKILL.md → ENTRY.md} +0 -0
  315. /package/skills/ai-research/07-safety-alignment/prompt-guard/{SKILL.md → ENTRY.md} +0 -0
  316. /package/skills/ai-research/08-distributed-training/accelerate/{SKILL.md → ENTRY.md} +0 -0
  317. /package/skills/ai-research/08-distributed-training/deepspeed/{SKILL.md → ENTRY.md} +0 -0
  318. /package/skills/ai-research/08-distributed-training/megatron-core/{SKILL.md → ENTRY.md} +0 -0
  319. /package/skills/ai-research/08-distributed-training/pytorch-fsdp2/{SKILL.md → ENTRY.md} +0 -0
  320. /package/skills/ai-research/08-distributed-training/pytorch-lightning/{SKILL.md → ENTRY.md} +0 -0
  321. /package/skills/ai-research/08-distributed-training/ray-train/{SKILL.md → ENTRY.md} +0 -0
  322. /package/skills/ai-research/09-infrastructure/lambda-labs/{SKILL.md → ENTRY.md} +0 -0
  323. /package/skills/ai-research/09-infrastructure/modal/{SKILL.md → ENTRY.md} +0 -0
  324. /package/skills/ai-research/09-infrastructure/skypilot/{SKILL.md → ENTRY.md} +0 -0
  325. /package/skills/ai-research/10-optimization/awq/{SKILL.md → ENTRY.md} +0 -0
  326. /package/skills/ai-research/10-optimization/bitsandbytes/{SKILL.md → ENTRY.md} +0 -0
  327. /package/skills/ai-research/10-optimization/flash-attention/{SKILL.md → ENTRY.md} +0 -0
  328. /package/skills/ai-research/10-optimization/gguf/{SKILL.md → ENTRY.md} +0 -0
  329. /package/skills/ai-research/10-optimization/gptq/{SKILL.md → ENTRY.md} +0 -0
  330. /package/skills/ai-research/10-optimization/hqq/{SKILL.md → ENTRY.md} +0 -0
  331. /package/skills/ai-research/10-optimization/ml-training-recipes/{SKILL.md → ENTRY.md} +0 -0
  332. /package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
  333. /package/skills/ai-research/11-evaluation/lm-evaluation-harness/{SKILL.md → ENTRY.md} +0 -0
  334. /package/skills/ai-research/11-evaluation/nemo-evaluator/{SKILL.md → ENTRY.md} +0 -0
  335. /package/skills/ai-research/12-inference-serving/llama-cpp/{SKILL.md → ENTRY.md} +0 -0
  336. /package/skills/ai-research/12-inference-serving/sglang/{SKILL.md → ENTRY.md} +0 -0
  337. /package/skills/ai-research/12-inference-serving/tensorrt-llm/{SKILL.md → ENTRY.md} +0 -0
  338. /package/skills/ai-research/12-inference-serving/vllm/{SKILL.md → ENTRY.md} +0 -0
  339. /package/skills/ai-research/13-mlops/mlflow/{SKILL.md → ENTRY.md} +0 -0
  340. /package/skills/ai-research/13-mlops/swanlab/{SKILL.md → ENTRY.md} +0 -0
  341. /package/skills/ai-research/13-mlops/tensorboard/{SKILL.md → ENTRY.md} +0 -0
  342. /package/skills/ai-research/13-mlops/weights-and-biases/{SKILL.md → ENTRY.md} +0 -0
  343. /package/skills/ai-research/14-agents/a-evolve/{SKILL.md → ENTRY.md} +0 -0
  344. /package/skills/ai-research/14-agents/autogpt/{SKILL.md → ENTRY.md} +0 -0
  345. /package/skills/ai-research/14-agents/crewai/{SKILL.md → ENTRY.md} +0 -0
  346. /package/skills/ai-research/14-agents/langchain/{SKILL.md → ENTRY.md} +0 -0
  347. /package/skills/ai-research/14-agents/llamaindex/{SKILL.md → ENTRY.md} +0 -0
  348. /package/skills/ai-research/15-rag/chroma/{SKILL.md → ENTRY.md} +0 -0
  349. /package/skills/ai-research/15-rag/faiss/{SKILL.md → ENTRY.md} +0 -0
  350. /package/skills/ai-research/15-rag/pinecone/{SKILL.md → ENTRY.md} +0 -0
  351. /package/skills/ai-research/15-rag/qdrant/{SKILL.md → ENTRY.md} +0 -0
  352. /package/skills/ai-research/15-rag/sentence-transformers/{SKILL.md → ENTRY.md} +0 -0
  353. /package/skills/ai-research/16-prompt-engineering/dspy/{SKILL.md → ENTRY.md} +0 -0
  354. /package/skills/ai-research/16-prompt-engineering/guidance/{SKILL.md → ENTRY.md} +0 -0
  355. /package/skills/ai-research/16-prompt-engineering/instructor/{SKILL.md → ENTRY.md} +0 -0
  356. /package/skills/ai-research/16-prompt-engineering/outlines/{SKILL.md → ENTRY.md} +0 -0
  357. /package/skills/ai-research/17-observability/langsmith/{SKILL.md → ENTRY.md} +0 -0
  358. /package/skills/ai-research/17-observability/phoenix/{SKILL.md → ENTRY.md} +0 -0
  359. /package/skills/ai-research/18-multimodal/audiocraft/{SKILL.md → ENTRY.md} +0 -0
  360. /package/skills/ai-research/18-multimodal/blip-2/{SKILL.md → ENTRY.md} +0 -0
  361. /package/skills/ai-research/18-multimodal/clip/{SKILL.md → ENTRY.md} +0 -0
  362. /package/skills/ai-research/18-multimodal/cosmos-policy/{SKILL.md → ENTRY.md} +0 -0
  363. /package/skills/ai-research/18-multimodal/llava/{SKILL.md → ENTRY.md} +0 -0
  364. /package/skills/ai-research/18-multimodal/openpi/{SKILL.md → ENTRY.md} +0 -0
  365. /package/skills/ai-research/18-multimodal/openvla-oft/{SKILL.md → ENTRY.md} +0 -0
  366. /package/skills/ai-research/18-multimodal/segment-anything/{SKILL.md → ENTRY.md} +0 -0
  367. /package/skills/ai-research/18-multimodal/stable-diffusion/{SKILL.md → ENTRY.md} +0 -0
  368. /package/skills/ai-research/18-multimodal/whisper/{SKILL.md → ENTRY.md} +0 -0
  369. /package/skills/ai-research/19-emerging-techniques/knowledge-distillation/{SKILL.md → ENTRY.md} +0 -0
  370. /package/skills/ai-research/19-emerging-techniques/long-context/{SKILL.md → ENTRY.md} +0 -0
  371. /package/skills/ai-research/19-emerging-techniques/model-merging/{SKILL.md → ENTRY.md} +0 -0
  372. /package/skills/ai-research/19-emerging-techniques/model-pruning/{SKILL.md → ENTRY.md} +0 -0
  373. /package/skills/ai-research/19-emerging-techniques/moe-training/{SKILL.md → ENTRY.md} +0 -0
  374. /package/skills/ai-research/19-emerging-techniques/speculative-decoding/{SKILL.md → ENTRY.md} +0 -0
  375. /package/skills/ai-research/20-ml-paper-writing/academic-plotting/{SKILL.md → ENTRY.md} +0 -0
  376. /package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/{SKILL.md → ENTRY.md} +0 -0
  377. /package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/{SKILL.md → ENTRY.md} +0 -0
  378. /package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/{SKILL.md → ENTRY.md} +0 -0
  379. /package/skills/ai-research/21-research-ideation/brainstorming-research-ideas/{SKILL.md → ENTRY.md} +0 -0
  380. /package/skills/ai-research/21-research-ideation/creative-thinking-for-research/{SKILL.md → ENTRY.md} +0 -0
  381. /package/skills/ai-research/22-agent-native-research-artifact/compiler/{SKILL.md → ENTRY.md} +0 -0
  382. /package/skills/ai-research/22-agent-native-research-artifact/research-manager/{SKILL.md → ENTRY.md} +0 -0
  383. /package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/{SKILL.md → ENTRY.md} +0 -0
  384. /package/skills/design-library/adaptive-interfaces/skills/colour-independence/{SKILL.md → ENTRY.md} +0 -0
  385. /package/skills/design-library/adaptive-interfaces/skills/flexible-typography/{SKILL.md → ENTRY.md} +0 -0
  386. /package/skills/design-library/adaptive-interfaces/skills/information-density/{SKILL.md → ENTRY.md} +0 -0
  387. /package/skills/design-library/adaptive-interfaces/skills/preference-audit/{SKILL.md → ENTRY.md} +0 -0
  388. /package/skills/design-library/adaptive-interfaces/skills/responsive-accessibility/{SKILL.md → ENTRY.md} +0 -0
  389. /package/skills/design-library/adaptive-interfaces/skills/responsive-review/{SKILL.md → ENTRY.md} +0 -0
  390. /package/skills/design-library/adaptive-interfaces/skills/simplified-views/{SKILL.md → ENTRY.md} +0 -0
  391. /package/skills/design-library/adaptive-interfaces/skills/specify/{SKILL.md → ENTRY.md} +0 -0
  392. /package/skills/design-library/adaptive-interfaces/skills/user-preference-respect/{SKILL.md → ENTRY.md} +0 -0
  393. /package/skills/design-library/cognitive-accessibility/skills/adaptive-personalisation/{SKILL.md → ENTRY.md} +0 -0
  394. /package/skills/design-library/cognitive-accessibility/skills/assess-load/{SKILL.md → ENTRY.md} +0 -0
  395. /package/skills/design-library/cognitive-accessibility/skills/cognitive-load-assessment/{SKILL.md → ENTRY.md} +0 -0
  396. /package/skills/design-library/cognitive-accessibility/skills/contextual-help-design/{SKILL.md → ENTRY.md} +0 -0
  397. /package/skills/design-library/cognitive-accessibility/skills/error-prevention-recovery/{SKILL.md → ENTRY.md} +0 -0
  398. /package/skills/design-library/cognitive-accessibility/skills/focus-attention-design/{SKILL.md → ENTRY.md} +0 -0
  399. /package/skills/design-library/cognitive-accessibility/skills/memory-load-reduction/{SKILL.md → ENTRY.md} +0 -0
  400. /package/skills/design-library/cognitive-accessibility/skills/plain-language-design/{SKILL.md → ENTRY.md} +0 -0
  401. /package/skills/design-library/cognitive-accessibility/skills/review/{SKILL.md → ENTRY.md} +0 -0
  402. /package/skills/design-library/cognitive-accessibility/skills/simplify/{SKILL.md → ENTRY.md} +0 -0
  403. /package/skills/design-library/cognitive-accessibility/skills/wayfinding-navigation/{SKILL.md → ENTRY.md} +0 -0
  404. /package/skills/design-library/design-research/skills/behavioural-analytics/{SKILL.md → ENTRY.md} +0 -0
  405. /package/skills/design-library/design-research/skills/qual-quant-triangulation/{SKILL.md → ENTRY.md} +0 -0
  406. /package/skills/design-library/inclusive-interaction/skills/audit/{SKILL.md → ENTRY.md} +0 -0
  407. /package/skills/design-library/inclusive-interaction/skills/design-flow/{SKILL.md → ENTRY.md} +0 -0
  408. /package/skills/design-library/inclusive-interaction/skills/feedback-and-status/{SKILL.md → ENTRY.md} +0 -0
  409. /package/skills/design-library/inclusive-interaction/skills/gesture-alternatives/{SKILL.md → ENTRY.md} +0 -0
  410. /package/skills/design-library/inclusive-interaction/skills/keyboard-navigation/{SKILL.md → ENTRY.md} +0 -0
  411. /package/skills/design-library/inclusive-interaction/skills/keyboard-review/{SKILL.md → ENTRY.md} +0 -0
  412. /package/skills/design-library/inclusive-interaction/skills/motion-sensitivity/{SKILL.md → ENTRY.md} +0 -0
  413. /package/skills/design-library/inclusive-interaction/skills/multi-modal-input/{SKILL.md → ENTRY.md} +0 -0
  414. /package/skills/design-library/inclusive-interaction/skills/touch-target-design/{SKILL.md → ENTRY.md} +0 -0
  415. /package/skills/design-library/inclusive-interaction/skills/voice-interaction/{SKILL.md → ENTRY.md} +0 -0
  416. /package/skills/design-library/visual-critique/skills/critique-affordance/{SKILL.md → ENTRY.md} +0 -0
  417. /package/skills/design-library/visual-critique/skills/critique-brand-consistency/{SKILL.md → ENTRY.md} +0 -0
  418. /package/skills/design-library/visual-critique/skills/critique-color/{SKILL.md → ENTRY.md} +0 -0
  419. /package/skills/design-library/visual-critique/skills/critique-composition/{SKILL.md → ENTRY.md} +0 -0
  420. /package/skills/design-library/visual-critique/skills/critique-information-density/{SKILL.md → ENTRY.md} +0 -0
  421. /package/skills/design-library/visual-critique/skills/critique-typography/{SKILL.md → ENTRY.md} +0 -0
  422. /package/skills/design-library/visual-critique/skills/critique-visual-hierarchy/{SKILL.md → ENTRY.md} +0 -0
  423. /package/skills/pm-frameworks/intended-vs-implemented/{SKILL.md → ENTRY.md} +0 -0
  424. /package/skills/pm-frameworks/opportunity-solution-tree/{SKILL.md → ENTRY.md} +0 -0
  425. /package/skills/rl-env/generate-nemo-gym-env/{SKILL.md → ENTRY.md} +0 -0
  426. /package/skills/rl-env/generate-openenv-env/{SKILL.md → ENTRY.md} +0 -0
  427. /package/skills/rl-env/generate-ors-env/{SKILL.md → ENTRY.md} +0 -0
  428. /package/skills/rl-env/generate-verifiers-env/{SKILL.md → ENTRY.md} +0 -0
  429. /package/skills/rl-env/rl-env-from-description/{SKILL.md → ENTRY.md} +0 -0
package/bin/tezgah-setup CHANGED
@@ -188,7 +188,7 @@ LOCAL_BIN = os.path.join(HOME, ".local", "bin")
188
188
  SKILLS = ["harness", "tezgah-contract", "ponytail", "i-have-adhd", "no-ai-slop",
189
189
  "analyze-app", "plan-add", "plan-status", "plan-sync", "research",
190
190
  "product-analysis", "product-flow", "feature-audit", "design-contract",
191
- "design-library", "pm-frameworks", "ai-research", "rl-env"]
191
+ "design-library", "hallmark", "pm-frameworks", "ai-research", "rl-env"]
192
192
 
193
193
 
194
194
  def skills_linked(base):
@@ -209,6 +209,19 @@ RESEARCH_SKILLS = frozenset(("ai-research", "rl-env"))
209
209
  # explicit value wins.
210
210
  OPENCODE_IGNORE = [".git/**", "node_modules/**", "dist/**", "build/**",
211
211
  ".venv/**", "__pycache__/**", ".ruff_cache/**"]
212
+
213
+
214
+ def opencode_v2():
215
+ """opencode 2 keeps its terminal settings in cli.json (it migrates tui.json
216
+ into it once, on first start) and ignores tui.json afterwards."""
217
+ return os.path.isfile(os.path.join(OPENCODE, "cli.json"))
218
+
219
+
220
+ # The opencode 2 status line: a link to the plugin directory hosts/opencode/tui/cli,
221
+ # listed in cli.json `plugins` as file://<this path>.
222
+ OPENCODE_CLI_PLUGIN = os.path.join(OPENCODE, "tui-plugins", "tezgah-cli")
223
+
224
+
212
225
  WORKFLOW_FILES = ["graph-map.js", "graph-review.js", "graph-impact.js"]
213
226
  ALL_HOSTS = ["claude", "codex", "opencode", "cursor", "dsh", "omp"]
214
227
  # orx ships an installable skill shim for four agents; dsh has no orx harness,
@@ -231,7 +244,8 @@ MATERIALISED = os.path.join(tp.CONFIG_DIR, "materialised")
231
244
  # copies die with ~/.config/tezgah; the pre-multi-host setup wrote them to
232
245
  # ~/.claude, which OFF_DIRS still honors, so a full uninstall sweeps those by
233
246
  # name too - a switch left behind is a rule that stays silent with no file saying why.
234
- OFF_SWITCH_NAMES = ("adhd-off", "agents-off", "consult-off", "docs-judge-off", "exec-mode.off",
247
+ OFF_SWITCH_NAMES = ("adhd-off", "agents-off", "consult-off", "design-off", "docs-judge-off",
248
+ "exec-mode.off",
235
249
  "judge-off", "lang-off", "orchestrate-off", "ponytail-auto.off",
236
250
  "pretooluse-off", "reminder-off", "research-off", "slices-off",
237
251
  "spec-off", "task-off", "triage-off", "update-check-off", "verify-off",
@@ -1039,7 +1053,10 @@ def install_common(new_roots, hosts):
1039
1053
  (tp.roots() or [tp.DEFAULT_ROOT])
1040
1054
  if tp.codegraph_bin():
1041
1055
  cfg["codegraph_bin"] = tp.codegraph_bin()
1042
- cfg["hosts"] = hosts
1056
+ # merged with the record, never replaced: arming one host must not drop
1057
+ # the others, whose wiring stays on disk and which --upgrade reads from here
1058
+ recorded = cfg.get("hosts") if isinstance(cfg.get("hosts"), list) else []
1059
+ cfg["hosts"] = recorded + [h for h in hosts if h not in recorded]
1043
1060
  # The resolved selection, written beside roots/hosts: the next run reads one
1044
1061
  # persisted answer instead of the flags some earlier run happened to carry,
1045
1062
  # which is what lets --disable mean anything. A key absent here (and in the
@@ -1090,6 +1107,8 @@ def install_common(new_roots, hosts):
1090
1107
  ("tezgah-route", os.path.join(HERE, "bin", "tezgah-route")),
1091
1108
  ("tezgah-lessons", os.path.join(HERE, "bin", "tezgah-lessons")),
1092
1109
  ("tezgah-skill", os.path.join(HERE, "bin", "tezgah-skill")),
1110
+ ("tezgah-review-quote", os.path.join(HERE, "bin", "tezgah-review-quote")),
1111
+ ("tezgah-ocr-review", os.path.join(HERE, "bin", "tezgah-ocr-review")),
1093
1112
  # `tezgah` is this installer under the name `tezgah update` is typed
1094
1113
  # with (npm ships the same name). Not here on purpose: tezgah-dsh
1095
1114
  # (wired as ~/.local/bin/dsh, see dsh_launcher) and
@@ -1647,6 +1666,11 @@ def skill_description(path):
1647
1666
  else:
1648
1667
  inline = re.search(r"description:\s*(.+)", block.group(1))
1649
1668
  desc = inline.group(1).strip().strip("\"'") if inline else ""
1669
+ return _trigger_line(desc)
1670
+
1671
+
1672
+ def _trigger_line(desc):
1673
+ """The sentence of `desc` carrying its `Use when ...` trigger, cut at 140."""
1650
1674
  desc = re.sub(r"\s+", " ", desc).strip()
1651
1675
  sentences = re.split(r"(?<=[.!?])\s+", desc)
1652
1676
  line = next((s for s in sentences if re.match(r"(?:Also )?[Uu]se\b", s)),
@@ -1654,6 +1678,22 @@ def skill_description(path):
1654
1678
  return line[:137].rstrip() + "..." if len(line) > 140 else line
1655
1679
 
1656
1680
 
1681
+ def skill_when_line(path):
1682
+ """The trigger line of a SKILL.md body's `## When to use` first paragraph.
1683
+
1684
+ The frontmatter description is a short bilingual trigger list now, too short
1685
+ for two near-neighbours to share subject words; the long text it replaced
1686
+ opens the body's `## When to use`, and that is what still tells them apart.
1687
+ Best effort like skill_description: anything unreadable yields ""."""
1688
+ try:
1689
+ with open(path, encoding="utf-8", errors="replace") as fh:
1690
+ text = fh.read()
1691
+ except OSError:
1692
+ return ""
1693
+ para = re.search(r"^## When to use\n\s*\n(.*?)(?:\n\s*\n|\Z)", text, re.M | re.S)
1694
+ return _trigger_line(para.group(1)) if para else ""
1695
+
1696
+
1657
1697
  def skill_category(name, path=""):
1658
1698
  if name in RESEARCH_SKILLS:
1659
1699
  return "research & papers"
@@ -1734,12 +1774,12 @@ under out up down off again more most other some such only own same too very can
1734
1774
  will just don should now read reads reading use used uses using also one two
1735
1775
  three every each any all both new old
1736
1776
  """.split())
1737
- # Two skills a session could pick either way from: their router lines share this
1738
- # many subject words, so the description alone cannot settle the choice. Three is
1739
- # where the shipped set stops pairing near-neighbours - `plan-add` / `plan-status`
1740
- # / `plan-sync` share four terms and `product-analysis` / `feature-audit` three,
1741
- # while at two `harness` / `no-ai-slop` pair over "asks, user" and the section
1742
- # reads as noise.
1777
+ # Two skills a session could pick either way from: the trigger lines of their
1778
+ # `## When to use` share this many subject words, so the description alone
1779
+ # cannot settle the choice. Three is where the shipped set pairs near-neighbours -
1780
+ # `plan-add` / `plan-status` / `plan-sync` share four or five terms,
1781
+ # `product-analysis` / `feature-audit` and `design-contract` / `design-library`
1782
+ # three - while `ponytail` / `i-have-adhd` stay apart.
1743
1783
  COLLISION_SHARED = 3
1744
1784
 
1745
1785
 
@@ -1758,7 +1798,7 @@ def skill_collisions(groups):
1758
1798
  router is what a session picks from when the descriptions tie. It reads the
1759
1799
  shipped names only: a vendored library's internal collisions are its own
1760
1800
  index's business, and a pair tezgah cannot rename is not a tezgah decision."""
1761
- lines = {name: skill_description(path)
1801
+ lines = {name: skill_when_line(path)
1762
1802
  for cat in ROUTER_ORDER for name, path in groups[cat]
1763
1803
  if name in SKILLS}
1764
1804
  names = sorted(lines)
@@ -1778,7 +1818,8 @@ def _collision_section(pairs):
1778
1818
  out = [
1779
1819
  "## Collisions",
1780
1820
  "",
1781
- "Pairs a description alone cannot settle: both router lines carry the"
1821
+ "Pairs a description alone cannot settle: both entries' `## When to use`"
1822
+ " lines carry the"
1782
1823
  " words below, so a session could pick either way from them. Read both"
1783
1824
  " entries before choosing.",
1784
1825
  "",
@@ -1856,20 +1897,38 @@ def install_opencode():
1856
1897
  write_artifact(SKILL_ROUTER_FULL, full, write_if_changed)
1857
1898
  print("%s %s (always-on, %d chars)" % (OK, SKILL_ROUTER, len(always)))
1858
1899
  print("%s %s (on demand, %d chars)" % (OK, SKILL_ROUTER_FULL, len(full)))
1859
- # TUI status line: opencode has no command statusLine, only a TUI plugin
1860
- # declared in tui.json. Local path spec resolves relative to tui.json.
1861
- print(link(os.path.join(HERE, "hosts", "opencode", "tui", "tezgah-tui.tsx"),
1862
- os.path.join(OPENCODE, "tui-plugins", "tezgah-tui.tsx")))
1863
- tpath = os.path.join(OPENCODE, "tui.json")
1864
- tui = read_config(tpath, {"$schema": "https://opencode.ai/tui.json"})
1865
- if tui is not None:
1866
- plugins = tui.get("plugin") or []
1867
- spec = "./tui-plugins/tezgah-tui.tsx"
1868
- if spec not in [p if isinstance(p, str) else p[0] for p in plugins]:
1869
- plugins.append(spec)
1870
- tui["plugin"] = plugins
1871
- write_json(tpath, tui)
1872
- print("%s tui.json (status line plugin)" % OK)
1900
+ # TUI status line: opencode has no command statusLine, only a TUI plugin.
1901
+ # opencode 1 declares it in tui.json (a local spec resolves relative to it);
1902
+ # opencode 2 ignores tui.json and lists CLI plugins in cli.json `plugins`. A
1903
+ # local v2 entry must name a DIRECTORY whose `tui` module default-exports
1904
+ # Plugin.define({id, setup}) - a spec naming a file is skipped (measured on
1905
+ # v2.0.32) - so the directory hosts/opencode/tui/cli is what gets linked.
1906
+ v2 = opencode_v2()
1907
+ if v2:
1908
+ dst = OPENCODE_CLI_PLUGIN
1909
+ print(link(os.path.join(HERE, "hosts", "opencode", "tui", "cli"), dst))
1910
+ cpath = os.path.join(OPENCODE, "cli.json")
1911
+ cli = read_config(cpath, {})
1912
+ if cli is not None:
1913
+ plugins = cli.get("plugins") or []
1914
+ if "file://" + dst not in plugins:
1915
+ plugins.append("file://" + dst)
1916
+ cli["plugins"] = plugins
1917
+ write_json(cpath, cli)
1918
+ print("%s cli.json (status line plugin)" % OK)
1919
+ else:
1920
+ print(link(os.path.join(HERE, "hosts", "opencode", "tui", "tezgah-tui.tsx"),
1921
+ os.path.join(OPENCODE, "tui-plugins", "tezgah-tui.tsx")))
1922
+ tpath = os.path.join(OPENCODE, "tui.json")
1923
+ tui = read_config(tpath, {"$schema": "https://opencode.ai/tui.json"})
1924
+ if tui is not None:
1925
+ plugins = tui.get("plugin") or []
1926
+ spec = "./tui-plugins/tezgah-tui.tsx"
1927
+ if spec not in [p if isinstance(p, str) else p[0] for p in plugins]:
1928
+ plugins.append(spec)
1929
+ tui["plugin"] = plugins
1930
+ write_json(tpath, tui)
1931
+ print("%s tui.json (status line plugin)" % OK)
1873
1932
  cfg = read_config(os.path.join(OPENCODE, "opencode.json"),
1874
1933
  {"$schema": "https://opencode.ai/config.json"})
1875
1934
  if cfg is None:
@@ -1921,7 +1980,8 @@ def install_opencode():
1921
1980
  # Context hygiene: prune replaces old tool results in the prompt with
1922
1981
  # "[Old tool result content cleared]" so a long session stops re-sending
1923
1982
  # every output on every step. setdefault keeps a user's explicit choice.
1924
- cfg.setdefault("compaction", {}).setdefault("prune", True)
1983
+ if not v2: # opencode 2 omits it as an unsupported legacy setting
1984
+ cfg.setdefault("compaction", {}).setdefault("prune", True)
1925
1985
  wat = cfg.setdefault("watcher", {})
1926
1986
  ign = wat.setdefault("ignore", [])
1927
1987
  for pat in OPENCODE_IGNORE:
@@ -1929,7 +1989,8 @@ def install_opencode():
1929
1989
  ign.append(pat)
1930
1990
  changed = write_json(os.path.join(OPENCODE, "opencode.json"), cfg)
1931
1991
  print("%s opencode.json (instructions + skill router + mcp + skill deny)" % OK)
1932
- print("%s opencode.json (compaction.prune + watcher.ignore)" % OK)
1992
+ print("%s opencode.json (%swatcher.ignore)"
1993
+ % (OK, "" if v2 else "compaction.prune + "))
1933
1994
  if changed or dropped:
1934
1995
  restart_hint("opencode", os.path.join(OPENCODE, "opencode.json"))
1935
1996
 
@@ -2945,6 +3006,20 @@ def _run_bounded(argv, timeout):
2945
3006
  except OSError:
2946
3007
  pass
2947
3008
  proc.wait()
3009
+ # a member mid-fork as the kill swept the group can come out of the
3010
+ # fork unsignalled (macOS under load: the orphan held the caller's
3011
+ # pipes, plan 115), so signal the group again until it is empty. Each
3012
+ # round catches what forked since the last and a healthy group is gone
3013
+ # within a few; the 2 s cap only bounds one that will not die (a member
3014
+ # stuck in uninterruptible I/O, a zombie nobody reaps) so the Ctrl-C
3015
+ # still returns.
3016
+ deadline = time.monotonic() + 2
3017
+ while time.monotonic() < deadline:
3018
+ try:
3019
+ os.killpg(proc.pid, signal.SIGKILL)
3020
+ except OSError: # ESRCH: nothing left in the group
3021
+ break
3022
+ time.sleep(0.01)
2948
3023
  raise
2949
3024
 
2950
3025
 
@@ -3363,9 +3438,12 @@ def verify_uninstall(hosts, full):
3363
3438
  if "opencode" in hosts:
3364
3439
  claim("opencode plugin unlinked",
3365
3440
  [os.path.join(OPENCODE, d, "tezgah.js") for d in ("plugins", "plugin")]
3366
- + [os.path.join(OPENCODE, "tui-plugins", "tezgah-tui.tsx")])
3441
+ + [os.path.join(OPENCODE, "tui-plugins", n)
3442
+ for n in ("tezgah-tui.tsx", "tezgah-cli")])
3367
3443
  clean("opencode opencode.json unwired",
3368
3444
  [os.path.join(OPENCODE, "opencode.json")])
3445
+ clean("opencode cli.json unwired", [os.path.join(OPENCODE, "cli.json")],
3446
+ needle=OPENCODE_CLI_PLUGIN)
3369
3447
  claim("opencode generated routers gone", [SKILL_ROUTER, SKILL_ROUTER_FULL])
3370
3448
  if "cursor" in hosts:
3371
3449
  clean("cursor hooks.json unwired", [os.path.join(CURSOR, "hooks.json")])
@@ -3499,6 +3577,14 @@ def unwire_opencode_config():
3499
3577
  tj.pop("plugin", None)
3500
3578
  write_json(t, tj)
3501
3579
  print("%s removed tezgah TUI plugin from %s" % (OK, t))
3580
+ cp = os.path.join(OPENCODE, "cli.json")
3581
+ cj = read_json(cp, None)
3582
+ if cj and isinstance(cj.get("plugins"), list):
3583
+ kept = [x for x in cj["plugins"] if x != "file://" + OPENCODE_CLI_PLUGIN]
3584
+ if kept != cj["plugins"]:
3585
+ cj["plugins"] = kept
3586
+ write_json(cp, cj)
3587
+ print("%s removed tezgah CLI plugin from %s" % (OK, cp))
3502
3588
 
3503
3589
 
3504
3590
  def uninstall_claude():
@@ -3560,7 +3646,8 @@ def uninstall_opencode():
3560
3646
  unlink_tezgah(os.path.join(OPENCODE, "skills", s))
3561
3647
  for d in ("plugins", "plugin"):
3562
3648
  unlink_tezgah(os.path.join(OPENCODE, d, "tezgah.js"))
3563
- unlink_tezgah(os.path.join(OPENCODE, "tui-plugins", "tezgah-tui.tsx"))
3649
+ for name in ("tezgah-tui.tsx", "tezgah-cli"):
3650
+ unlink_tezgah(os.path.join(OPENCODE, "tui-plugins", name))
3564
3651
  unwire_opencode_config()
3565
3652
  for path in (SKILL_ROUTER, SKILL_ROUTER_FULL):
3566
3653
  try:
@@ -3714,6 +3801,13 @@ def uninstall(hosts, dry_run=False):
3714
3801
  remove_install_tree()
3715
3802
  elif armed:
3716
3803
  rest = sorted(set(armed) - set(hosts))
3804
+ # install_common only ever merges into the record, so this is the one
3805
+ # place a host leaves it; kept, it made every later uninstall partial
3806
+ # and `tezgah update` re-armed it from the record
3807
+ cfg = read_config(tp.CONFIG, {})
3808
+ if cfg is not None:
3809
+ cfg["hosts"] = [h for h in armed if h in rest]
3810
+ write_json(tp.CONFIG, cfg)
3717
3811
  print("\n%s still armed: %s keeps its config and the install tree"
3718
3812
  % (NO, ", ".join(rest)))
3719
3813
  remaining = verify_uninstall(hosts, full)
@@ -4376,6 +4470,8 @@ def report(hosts):
4376
4470
  ("codegen provider key (OpenRouter, DeepSeek or Inception)",
4377
4471
  tp.have_provider_key()),
4378
4472
  ("orx on PATH (OpenResearch research routing)", bool(tp.orx_bin())),
4473
+ ("ocr on PATH (open-code-review spec for the reviewer, opt-in)",
4474
+ bool(tp.ocr_bin())),
4379
4475
  ("npx on PATH (app-analysis MCP servers)", bool(shutil.which("npx"))),
4380
4476
  ("app artifacts dir %s" % tezgah_apps.ARTIFACTS,
4381
4477
  os.path.isdir(tezgah_apps.ARTIFACTS)),
@@ -4694,6 +4790,7 @@ def host_checks_codex():
4694
4790
  def host_checks_opencode():
4695
4791
  cfg = read_json(os.path.join(OPENCODE, "opencode.json"), {})
4696
4792
  tui = read_json(os.path.join(OPENCODE, "tui.json"), {})
4793
+ cli = read_json(os.path.join(OPENCODE, "cli.json"), {})
4697
4794
  instr = cfg.get("instructions") or []
4698
4795
  specs = [p if isinstance(p, str) else p[0] for p in (tui.get("plugin") or [])]
4699
4796
  return [
@@ -4711,14 +4808,20 @@ def host_checks_opencode():
4711
4808
  ] + mcp_rows(set(cfg.get("mcp") or {}), "opencode.json") + [
4712
4809
  mcp_row(TEZGAH_MCP_NAME, (cfg.get("mcp") or {}).get(TEZGAH_MCP_NAME),
4713
4810
  "opencode.json", tezgah_mcp_command()),
4811
+ ] + ([
4812
+ ("status line plugin in cli.json + linked",
4813
+ "file://" + OPENCODE_CLI_PLUGIN in (cli.get("plugins") or [])
4814
+ and os.path.exists(OPENCODE_CLI_PLUGIN)),
4815
+ ] if opencode_v2() else [
4714
4816
  ("context pruning armed (compaction.prune)",
4715
4817
  (cfg.get("compaction") or {}).get("prune") is True),
4716
- ("watcher ignores noisy trees",
4717
- bool(set(OPENCODE_IGNORE)
4718
- & set(((cfg.get("watcher") or {}).get("ignore") or [])))),
4719
4818
  ("status line plugin in tui.json + linked",
4720
4819
  any("tezgah-tui" in s for s in specs)
4721
4820
  and os.path.exists(os.path.join(OPENCODE, "tui-plugins", "tezgah-tui.tsx"))),
4821
+ ]) + [
4822
+ ("watcher ignores noisy trees",
4823
+ bool(set(OPENCODE_IGNORE)
4824
+ & set(((cfg.get("watcher") or {}).get("ignore") or [])))),
4722
4825
  # opencode's own registry declares INCEPTION_API_KEY for the inception
4723
4826
  # provider, so nothing needs adding to opencode.json - but the route is
4724
4827
  # only usable once a key resolves, and that is what this reports.
@@ -5923,7 +6026,7 @@ def hosts_row():
5923
6026
  host's first HOST_CHECKS row), as one report row. Reported, never changed:
5924
6027
  `--upgrade`, `--enable`/`--disable` and a full uninstall act on the recorded
5925
6028
  list, so a host wired but not recorded is one they skip, and which list is
5926
- right is the user's call (`--install --hosts ...` records it)."""
6029
+ right is the user's call (`--install --hosts ...` adds to it)."""
5927
6030
  recorded = sorted(tp.config().get("hosts") or [])
5928
6031
  wired = sorted(h for h in ALL_HOSTS if HOST_CHECKS[h]()[0][1])
5929
6032
  return ("config.json hosts (%s) match the hosts wired (%s)"
package/bin/tezgah-skill CHANGED
@@ -25,7 +25,7 @@ import json
25
25
  import os
26
26
  import sys
27
27
 
28
- HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
28
+ HERE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
29
29
  sys.path.insert(0, os.path.join(HERE, "hooks"))
30
30
  import tezgah_skill_pick as sp # noqa: E402
31
31
 
package/bin/tezgah-status CHANGED
@@ -29,6 +29,22 @@ their status line; this CLI is how every host (and a human) gets it:
29
29
  The MCP half of a retirement decision is manual:
30
30
  `tezgah-setup --mcp-schemas` measures what a server
31
31
  costs, this counts what it fires.
32
+ tezgah-status --rates --since DATE --until DATE [--json]
33
+ every real ledger's rows in [since, until), folded per
34
+ window and per calendar day: turns, `claim` rows and
35
+ the Stop classes that blocked them, the shape flags
36
+ every judged reply carried, denies by rule, and the
37
+ rates (blocks per 100 turns, denies per 100 sessions,
38
+ the claim block and Stop shape shares, blocked claims
39
+ per 100 sessions by class). A repeated `--since`/
40
+ `--until` pair prints one block each and the pp delta
41
+ between adjacent windows - the unit plan 066's 056 row
42
+ states its bar in. A window that read more than one
43
+ row contract prints the split, and a delta across two
44
+ contracts says so (plan 135). `--json` prints
45
+ `{"windows": [...]}`.
46
+ `--until` is exclusive, so adjacent windows partition
47
+ their span.
32
48
  tezgah-status --skill-fitness
33
49
  per shipped skill, over the recorded sessions: how
34
50
  many opened it and which were never opened. A
@@ -78,6 +94,16 @@ their status line; this CLI is how every host (and a human) gets it:
78
94
  the set renders as dim "no state" instead of claiming
79
95
  "armed, not used yet". A host that cannot see a skill
80
96
  read (dsh) passes the tool-use four.
97
+ tezgah-status --manifest=FILE
98
+ read the repository extension from FILE instead of
99
+ the `.tezgah/extension.json` discovered from PATH (a
100
+ manifest outside the tree, e.g. an engagement's).
101
+ With `statusline.override: true` in the manifest the
102
+ project owns its line and points its own statusLine
103
+ here: the host's status-line payload on stdin (its
104
+ `cwd`/`workspace.current_dir` and `session_id`) is
105
+ read when no PATH is given, so this is the only line
106
+ and it keeps the built-in marks and the chips.
81
107
 
82
108
  The session id may be passed as the second argument or via TEZGAH_SESSION;
83
109
  without it the "used" marks cannot light up, so a host that knows the id should
@@ -97,6 +123,26 @@ from tezgah_context import ( # noqa: E402
97
123
  # the line and `--json` carries every row, so a reader can always see what was
98
124
  # cut.
99
125
  TOP = 15
126
+ # The host payload an override reads on stdin: a status-line host writes one
127
+ # small JSON object and closes the pipe, so the read is bounded in bytes and in
128
+ # how long an empty pipe is waited for - a caller that never writes costs this
129
+ # long, never a hang.
130
+ PAYLOAD_MAX = 65536
131
+ PAYLOAD_WAIT = 0.2
132
+
133
+
134
+ def host_payload():
135
+ """The status-line host's JSON payload on stdin, or {} - a terminal, an
136
+ empty or silent pipe, malformed JSON or no stdin at all."""
137
+ import select
138
+ try:
139
+ if sys.stdin.isatty() or not select.select([sys.stdin], [], [],
140
+ PAYLOAD_WAIT)[0]:
141
+ return {}
142
+ payload = json.loads(sys.stdin.read(PAYLOAD_MAX) or "{}")
143
+ except (OSError, ValueError, AttributeError):
144
+ return {}
145
+ return payload if isinstance(payload, dict) else {}
100
146
 
101
147
 
102
148
  def weeks_arg(args):
@@ -241,6 +287,115 @@ def print_orca(cwd, as_json):
241
287
  return 0
242
288
 
243
289
 
290
+ def window_pairs(args):
291
+ """`args` with the `--since`/`--until` pairs folded out, and the pairs in the
292
+ order written. Each `--until` closes the `--since` before it, and a flag
293
+ without its partner is ValueError: half a window is a question the reader
294
+ did not finish asking.
295
+
296
+ The spaced form is read here for the reason `--weeks`' is: the split in
297
+ `main` sends every non-`--` argument to `pos`, where a date would be read as
298
+ a session id."""
299
+ out, pairs, since, i = [], [], None, 0
300
+ while i < len(args):
301
+ a = args[i]
302
+ name = a.split("=", 1)[0]
303
+ if name not in ("--since", "--until"):
304
+ out.append(a)
305
+ i += 1
306
+ continue
307
+ if "=" in a:
308
+ value, i = a.split("=", 1)[1], i + 1
309
+ else:
310
+ value = args[i + 1] if i + 1 < len(args) else ""
311
+ i += 2 if i + 1 < len(args) else 1
312
+ if name == "--since":
313
+ if since is not None:
314
+ raise ValueError("--since %s has no --until" % since)
315
+ since = value
316
+ else:
317
+ if since is None:
318
+ raise ValueError("--until %s has no --since" % value)
319
+ pairs.append((since, value))
320
+ since = None
321
+ if since is not None:
322
+ raise ValueError("--since %s has no --until" % since)
323
+ return out, pairs
324
+
325
+
326
+ def share(value, numerator, denominator):
327
+ """A share in the unit 056's `+2 pp` bar reads, or `n/a` with no
328
+ denominator - a window that decided nothing has no rate."""
329
+ return ("n/a" if value is None
330
+ else "%.2f%% (%d/%d)" % (100 * value, numerator, denominator))
331
+
332
+
333
+ def dominant_contract(window):
334
+ """The row contract that wrote most of a window's rows, or `-` for a window
335
+ that read none. `claims` is one quantity under a contract and another across
336
+ a change to it (plan 135), so the delta line names both sides' contract."""
337
+ counts = window.get("row_versions") or {}
338
+ if not counts:
339
+ return "-"
340
+ return max(sorted(counts), key=lambda name: counts[name])
341
+
342
+
343
+ def print_rates(found):
344
+ """The windowed rates, for `--rates`: one block per window, then the pp delta
345
+ between each adjacent pair - the unit plan 066's rows state their bars in."""
346
+ for w in found:
347
+ rates = w["rates"]
348
+ print("window %s..%s: %d days, %d sessions, %d turns, %d claims, %d "
349
+ "replies" % (w["since"], w["until"], w["days"], w["sessions"],
350
+ w["turns"], w["claims"], w["replies"]))
351
+ for key, label in (("blocked_claims", "blocked_claims"),
352
+ ("shapes", "stop shapes"), ("denies", "denies")):
353
+ print(" %-15s %s" % (label, ", ".join(
354
+ "%s=%d" % kv for kv in sorted(w[key].items())) or "none"))
355
+ print(" %-15s %d" % ("shape_blocked", w["shape_blocked"]))
356
+ print(" %-15s blocks/100 turns %s, denies/100 sessions %s" % (
357
+ "rates", "n/a" if rates["blocks_per_100_turns"] is None
358
+ else "%.4f" % rates["blocks_per_100_turns"],
359
+ "n/a" if rates["denies_per_100_sessions"] is None
360
+ else "%.4f" % rates["denies_per_100_sessions"]))
361
+ print(" %-15s claim block %s, stop shape %s" % (
362
+ "shares", share(rates["claim_block_rate"],
363
+ sum(w["blocked_claims"].values()), w["claims"]),
364
+ share(rates["stop_shape_rate"], w["shape_flagged"], w["replies"])))
365
+ print(" %-15s %s" % ("blocked/100 sess", ", ".join(
366
+ "%s=%.4f" % kv for kv in
367
+ sorted(rates["blocked_claims_per_100_sessions"].items()))
368
+ or "none"))
369
+ # Which row contract wrote the rows, printed only when a window mixes
370
+ # two: `claims` means "every judged reply" before ROW_VERSION 3 and "a
371
+ # reply that claimed" after it, so a share read off a mixed or shifted
372
+ # window is not the same quantity as its neighbour's (plan 135).
373
+ if len(w["row_versions"]) > 1:
374
+ print(" %-15s %s" % ("contracts", ", ".join(
375
+ "%s=%d" % kv for kv in sorted(w["row_versions"].items()))))
376
+ for before, after in zip(found, found[1:]):
377
+ parts, unknown, over = [], False, False
378
+ for key, label in (("claim_block_rate", "claim block"),
379
+ ("stop_shape_rate", "stop shape")):
380
+ a, b = before["rates"][key], after["rates"][key]
381
+ if a is None or b is None:
382
+ unknown = True
383
+ parts.append("%s n/a" % label)
384
+ continue
385
+ parts.append("%s %+.2f pp" % (label, 100 * (b - a)))
386
+ over = over or round(b - a, 4) > 0.02
387
+ shift = dominant_contract(before) != dominant_contract(after)
388
+ print("delta %s..%s -> %s..%s: %s; 056 bar (+2 pp): %s%s" % (
389
+ before["since"], before["until"], after["since"], after["until"],
390
+ ", ".join(parts),
391
+ "unreadable (no denominator in a window)" if unknown
392
+ else "FAILS" if over else "holds",
393
+ "; the two windows' rows come from different contracts (%s vs %s), "
394
+ "so the shares are not one quantity" % (
395
+ dominant_contract(before), dominant_contract(after))
396
+ if shift else ""))
397
+
398
+
244
399
  def main():
245
400
  # `--` ends the options: what follows is PATH and SESSION_ID even when it
246
401
  # starts with a dash. The dsh route and the MCP tool hand caller-chosen
@@ -250,6 +405,11 @@ def main():
250
405
  rest = argv[argv.index("--") + 1:] if "--" in argv else []
251
406
  argv = argv[:argv.index("--")] if "--" in argv else argv
252
407
  args, weeks = weeks_arg(argv)
408
+ try:
409
+ args, windows = window_pairs(args)
410
+ except ValueError as err:
411
+ sys.stderr.write("tezgah-status: %s\n" % err)
412
+ return 2
253
413
  # A sanity bound, not a policy: ten years of weeks is longer than any ledger
254
414
  # this machine holds, and a number the reader mistyped should say so rather
255
415
  # than print a thousand rows of nothing.
@@ -260,9 +420,11 @@ def main():
260
420
  pos = [a for a in args if not a.startswith("--")] + rest
261
421
  known = {"--legend", "--json", "--color", "--no-color", "--counters",
262
422
  "--all", "--trend", "--skill-fitness", "--failure-shapes",
263
- "--rule-yield", "--enforcers", "--orca", "--judge", "--help"}
423
+ "--rule-yield", "--enforcers", "--orca", "--judge", "--rates",
424
+ "--help"}
264
425
  unknown = sorted(f for f in flags
265
- if f not in known and not f.startswith("--observable="))
426
+ if f not in known
427
+ and not f.startswith(("--observable=", "--manifest=")))
266
428
  if unknown:
267
429
  sys.stderr.write("tezgah-status: unknown option(s): %s (try --help)\n"
268
430
  % ", ".join(unknown))
@@ -293,6 +455,26 @@ def main():
293
455
  sys.stderr.write("tezgah-status: %s only means something with "
294
456
  "--counters\n" % modifier)
295
457
  return 2
458
+ if windows and "--rates" not in flags:
459
+ sys.stderr.write("tezgah-status: --since/--until only mean something "
460
+ "with --rates\n")
461
+ return 2
462
+ if "--rates" in flags and not windows:
463
+ sys.stderr.write("tezgah-status: --rates wants --since and --until\n")
464
+ return 2
465
+ if "--rates" in flags:
466
+ import tezgah_integrity as ti
467
+ try:
468
+ found = [ti.rates(since, until) for since, until in windows]
469
+ except ValueError as err:
470
+ sys.stderr.write("tezgah-status: %s\n" % err)
471
+ return 2
472
+ if "--json" in flags:
473
+ # one shape for one window and for many: a consumer never branches
474
+ print(json.dumps({"windows": found}, sort_keys=True))
475
+ return 0
476
+ print_rates(found)
477
+ return 0
296
478
  if "--judge" in flags:
297
479
  import tezgah_judge as tj
298
480
  last = tj.last_use()
@@ -430,7 +612,23 @@ def main():
430
612
  spec = next((f.split("=", 1)[1] for f in flags
431
613
  if f.startswith("--observable=")), None)
432
614
  observable = set(spec.split(",")) if spec else None
433
- segs = health_segments(cwd, session, observable=observable)
615
+ manifest = next((f.split("=", 1)[1] for f in flags
616
+ if f.startswith("--manifest=")), None) or None
617
+ if not pos:
618
+ import tezgah_extension
619
+ if tezgah_extension.override(cwd, manifest):
620
+ # the project's own statusLine runs this renderer: the host says
621
+ # which directory and session the line is for
622
+ payload = host_payload()
623
+ workspace = payload.get("workspace")
624
+ where = ((workspace.get("current_dir")
625
+ if isinstance(workspace, dict) else None)
626
+ or payload.get("cwd"))
627
+ if isinstance(where, str) and where:
628
+ cwd = os.path.realpath(where)
629
+ if isinstance(payload.get("session_id"), str) and payload["session_id"]:
630
+ session = payload["session_id"]
631
+ segs = health_segments(cwd, session, observable=observable, manifest=manifest)
434
632
  if "--json" in flags:
435
633
  print(json.dumps(segs))
436
634
  return 0