@r1z4x/tezgah 1.6.0 → 1.8.2

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 (71) hide show
  1. package/CHANGELOG.md +140 -1
  2. package/MANIFEST +7 -0
  3. package/README.md +6 -3
  4. package/bin/tezgah-decisions +6 -14
  5. package/bin/tezgah-design +21 -5
  6. package/bin/tezgah-docs +18 -16
  7. package/bin/tezgah-doctor +152 -0
  8. package/bin/tezgah-lessons +4 -1
  9. package/bin/tezgah-research +249 -17
  10. package/bin/tezgah-route +70 -8
  11. package/bin/tezgah-setup +26 -13
  12. package/bin/tezgah-task +877 -60
  13. package/bin/tezgah-taste +56 -15
  14. package/docs/architecture.md +28 -11
  15. package/docs/clarity-baseline.json +2 -2
  16. package/docs/contract.md +108 -24
  17. package/docs/environment.md +3 -1
  18. package/docs/evidence.md +209 -28
  19. package/docs/feature-audit.md +1 -1
  20. package/docs/gate.md +78 -23
  21. package/docs/glossary.md +4 -4
  22. package/docs/hosts.md +8 -8
  23. package/docs/index.json +11 -5
  24. package/docs/judge.md +9 -3
  25. package/docs/models.md +75 -14
  26. package/docs/operations.md +133 -3
  27. package/docs/research.md +349 -15
  28. package/docs/skills.md +1 -1
  29. package/docs/status-line.md +5 -5
  30. package/docs/testing.md +20 -13
  31. package/docs/vision.md +12 -4
  32. package/hooks/hooks.json +2 -2
  33. package/hooks/lang/en.json +323 -0
  34. package/hooks/lang/tr.json +344 -0
  35. package/hooks/projects-auto-init.py +29 -1
  36. package/hooks/projects-posttooluse.py +24 -5
  37. package/hooks/projects-pretooluse.py +9 -4
  38. package/hooks/projects-stop.py +28 -16
  39. package/hooks/tezgah_agents.py +53 -17
  40. package/hooks/tezgah_clarity.py +7 -3
  41. package/hooks/tezgah_context.py +489 -438
  42. package/hooks/tezgah_gate.py +996 -80
  43. package/hooks/tezgah_integrity.py +1547 -197
  44. package/hooks/tezgah_models.py +427 -45
  45. package/hooks/tezgah_paths.py +147 -16
  46. package/hooks/tezgah_policy.py +261 -129
  47. package/hooks/tezgah_rank.py +29 -27
  48. package/hooks/tezgah_replay.py +200 -30
  49. package/hooks/tezgah_research.py +1303 -14
  50. package/hooks/tezgah_stopspec.py +7 -0
  51. package/hooks/tezgah_store.py +74 -27
  52. package/hooks/tezgah_task.py +1201 -40
  53. package/hooks/tezgah_taste.py +8 -4
  54. package/hosts/codex/hook.py +32 -13
  55. package/hosts/cursor/hook.py +32 -14
  56. package/hosts/dsh/hooks.json +2 -2
  57. package/hosts/omp/hook.py +44 -23
  58. package/hosts/omp/tezgah-hook.ts.in +93 -7
  59. package/hosts/opencode/plugins/tezgah.js +273 -6
  60. package/output-styles/tezgah.md +76 -42
  61. package/package.json +1 -1
  62. package/skills/i-have-adhd/EVALS.md +22 -2
  63. package/skills/i-have-adhd/SKILL.md +12 -5
  64. package/skills/plan-add/SKILL.md +27 -11
  65. package/skills/plan-add/render_table.py +8 -3
  66. package/skills/plan-sync/SKILL.md +5 -3
  67. package/skills/ponytail/EVALS.md +21 -2
  68. package/skills/ponytail/SKILL.md +4 -4
  69. package/skills/research/SKILL.md +89 -1
  70. package/skills/tezgah-contract/EVALS.md +52 -7
  71. package/skills/tezgah-contract/SKILL.md +154 -59
@@ -0,0 +1,344 @@
1
+ {
2
+ "name": "Turkish",
3
+ "done": [
4
+ "yaptım|tamamladım|tamamlandı|bitirdim|bitirildi|ekledim|eklendi|düzelttim|düzeltildi|hallettim|güncellendi|kuruldu|uygulandı|yayına girdi|kapatıldı|yüklendi|tüm testler geçti|testler geçti|testler yeşil|çalışıyor"
5
+ ],
6
+ "verified": [
7
+ "doğruladım|test ettim|kontrol ettim|denetledim|doğrulandı|test edildi"
8
+ ],
9
+ "negated": [
10
+ "doğrulanmadı|doğrulamadım|kontrol edilmedi|test edilmedi"
11
+ ],
12
+ "admitted": [
13
+ "çeliş|geçemez|imk[aâ]nsız"
14
+ ],
15
+ "sycophant": {
16
+ "interjection": [
17
+ "evet"
18
+ ],
19
+ "opener": [
20
+ "haklısın|haklısınız|iyi yakaladın|detaylı bakmadım"
21
+ ]
22
+ },
23
+ "closer": [],
24
+ "next_action": [
25
+ "sıradaki adım|sonraki adım"
26
+ ],
27
+ "preamble": [
28
+ "aşağıda|şöyle\\b|öncelikle\\b|şimdi\\b"
29
+ ],
30
+ "markers": {
31
+ "letters": [
32
+ "ç",
33
+ "ğ",
34
+ "ı",
35
+ "ö",
36
+ "ş",
37
+ "ü",
38
+ "Ç",
39
+ "Ğ",
40
+ "İ",
41
+ "Ö",
42
+ "Ş",
43
+ "Ü"
44
+ ],
45
+ "words": [
46
+ "ve",
47
+ "bir",
48
+ "bu",
49
+ "şu",
50
+ "için",
51
+ "ile",
52
+ "ama",
53
+ "fakat",
54
+ "veya",
55
+ "mı",
56
+ "mu",
57
+ "mü",
58
+ "çok",
59
+ "daha",
60
+ "gibi",
61
+ "kadar",
62
+ "sonra",
63
+ "önce",
64
+ "var",
65
+ "yok",
66
+ "değil",
67
+ "olarak",
68
+ "olan",
69
+ "hem",
70
+ "ise",
71
+ "ki",
72
+ "artık",
73
+ "hâlâ",
74
+ "hala",
75
+ "şimdi",
76
+ "yani",
77
+ "diye",
78
+ "tüm",
79
+ "bütün",
80
+ "kalan",
81
+ "neden",
82
+ "nasıl",
83
+ "yeni",
84
+ "eski",
85
+ "evet",
86
+ "hayır"
87
+ ]
88
+ },
89
+ "negation_before": [
90
+ "\\bdeğil"
91
+ ],
92
+ "question_particle": [
93
+ "m[ıiuü]|değil"
94
+ ],
95
+ "question_end": [
96
+ "m[ıiuü]"
97
+ ],
98
+ "clause_join": [
99
+ "ama|ve"
100
+ ],
101
+ "external_state": [
102
+ "yok|eksik|yayımlan|yayınlan"
103
+ ],
104
+ "external_ci_state": [
105
+ "geçti|başarısız"
106
+ ],
107
+ "publish_state": [
108
+ "yok|eksik|yayımlan|yayınlan"
109
+ ],
110
+ "residual_label": [
111
+ "açık (?:kalan|iş|madde|nokta|soru)|eksik(?!siz)|yapılama|yapılma(?:yan|dı)|yapılacak|bekleyen|bakıla?ma|bitme(?:yen|di)|tamamlanma|kısmen|doğrulan(?:a)?ma|test edile?me|çalıştırıla?ma|koşula?ma|dokunula?ma|sonraki adım|sıradaki|sırada\\b|kapsam dışı|atlan(?:an|dı)|ertelen|bilinen (?:hata|sınırlama|sorun)|teknik borç|ileride|gelecek (?:iş|çalışma)|önerilen (?:iyileştirme|adım)|manuel doğrulama|elle doğrula|engel(?:ler)?\\b|engelleyenler\\b|erişilemeyen|fix-?later|\\w+m[ae]d[ıiuü](?:m|kl[ae]r(?:[ıi]m)?)\\b|kontrol edil(?:e)?me|incelen(?:e)?me|ölçül(?:e)?me|çözül(?:e)?me|denen(?:e)?me|ulaşılama|(?:yarım|askıda|sonraya) kalan|sonra bakılacak"
112
+ ],
113
+ "residual_first": [
114
+ "kalan(?:lar)?(?=\\s*(?:\\(|$))|kalan\\s+(?:tek\\s+)?(?:iş|işler|adım|adımlar|madde|maddeler|görev|görevler|şey)\\b"
115
+ ],
116
+ "label_done": [
117
+ "giderildi|çözüldü|yazıldı|bitti|ölçüldü|yapıldı|halledildi|kapandı|kalmadı|tamamlanan|düzeltilen|giderilen|kapatılan|çözülen|artık (?:var|mevcut)"
118
+ ],
119
+ "partial": [
120
+ "kısmen"
121
+ ],
122
+ "nothing_left": [
123
+ "\\byok"
124
+ ],
125
+ "nothing_lead": [],
126
+ "empty_item": [
127
+ "yok|hiç(?:biri|bir şey)?"
128
+ ],
129
+ "blockers": {
130
+ "decision": {
131
+ "pattern": "KARAR"
132
+ },
133
+ "irreversible": {
134
+ "pattern": "GER[İI] ALINAMAZ"
135
+ },
136
+ "access": {
137
+ "pattern": "YETK[İI][A-ZÇĞİIÖŞÜ]*"
138
+ },
139
+ "env": {
140
+ "pattern": "ORTAM[A-ZÇĞİIÖŞÜ]*"
141
+ },
142
+ "said_not": [
143
+ "gerek(?:mi|mez|tirm|li\\s+değil)|istem|yok\\b|değil\\b"
144
+ ],
145
+ "tried_not": [
146
+ "değil\\b|gerek(?:mi|mez|tirm)|istem"
147
+ ],
148
+ "absent": [
149
+ "yok"
150
+ ]
151
+ },
152
+ "blocker_labels": [
153
+ "engel"
154
+ ],
155
+ "blockers_only": [
156
+ "engel(?:ler)?"
157
+ ],
158
+ "env_error": [
159
+ "bulunamad|kurulu değil|yüklü değil|mevcut değil"
160
+ ],
161
+ "access_error": [
162
+ "izin|reddedil|erişim"
163
+ ],
164
+ "spawn_words": [
165
+ "ajan|sağlayıcı"
166
+ ],
167
+ "was_exclude": [
168
+ "şimdi|kendi|ileri|geri"
169
+ ],
170
+ "was": [
171
+ "\\w+(?<!m[ae])(?:yordu|[dt][ıiuü])"
172
+ ],
173
+ "now": [
174
+ "^artık\\b.*(?:\\bvar\\b|\\bmevcut\\b|\\w+yor\\b)"
175
+ ],
176
+ "future": [
177
+ "\\w+(?:acak|ecek)"
178
+ ],
179
+ "word_order": "object_first",
180
+ "ui_objects": [
181
+ "akış|adım|ekran|sayfa|arayüz|form|menü|buton|düğme|panel|tasarım|görünüm|kart|bileşen|sekme|pencere"
182
+ ],
183
+ "ui_quality": [],
184
+ "hints": {
185
+ "spec": [
186
+ "düzgün çalış\\w*|düzgün görün\\w*|güzel görün\\w*|daha iyi (ol|görün)\\w*|kullanıcı dostu|modern görün\\w*|şık (ol|görün)\\w*|profesyonel görün\\w*|temiz (bir )?(arayüz|görün)\\w*|anlaşılır\\w*|kullanılabilirlik\\w*",
187
+ {
188
+ "ui_ask": "basitleştir\\w*|sadeleştir\\w*"
189
+ },
190
+ "yeniden tasarla\\w*|baştan tasarla\\w*|tasarım\\w*|focus[- ]durum\\w*|tipografi\\w*|bileşen\\w*|tema\\w*|animasyon\\w*|erişilebilir\\w*|prototip\\w*|hizala\\w*|hiza\\w*|boşluk\\w*|satır aral\\w*|gölge\\w*|kenarlık\\w*|yazı tipi\\w*|punto\\w*|renk\\w*|palet\\w*|devre dışı\\w*|boş durum\\w*|kırılma nokta\\w*|mobil (uyumlu|görünüm)|karanlık mod\\w*|aydınlık mod\\w*|renk şema\\w*|stil rehber\\w*|tasarım sistemi|tasarım token\\w*|ekran okuyucu\\w*|kontrast\\w*|ekran görüntü\\w*|görsel (incele|kontrol|karşılaştır)\\w*"
191
+ ],
192
+ "consult": [
193
+ "mimari\\w*|kök neden\\w*|geri dönüşü olmayan|kırıcı değişiklik\\w*|geriye dönük uyum\\w*|api sürüm\\w*|sürümleme\\w*|versiyonlama\\w*|şema\\w*|göç\\w*|migrasyon\\w*|veri (taşı|göç)\\w*|geri al\\w*|geri (alınamaz|dönüşü (yok|olmaz|olmayan))|performans bütçe\\w*|gecikme bütçe\\w*|güvenlik\\w*|zafiyet\\w*|bağımlılık(ları)? güncelle\\w*|paket güncelle\\w*|sürüm yükselt\\w*|tedarik zincir\\w*|eş ?zamanlı\\w*|yarış (durumu|koşulu)\\w*|kapasite (plan|hesab|test|sınır)\\w*|ölçeklen\\w*|yük test\\w*|mimari karar\\w*|tasarım karar\\w*|politika değiş\\w*|sözleşme (değiş|yenile)\\w*|yayına al\\w*|canlıya al\\w*|sürüm çıkar\\w*|ödün (ver|vermey)\\w*"
194
+ ],
195
+ "research": [
196
+ "araştır\\w*|literatür\\w*|hipotez\\w*|deney|karşılaştır\\w*|karşılaştırmalı analiz|kıyaslama\\w*|varyant\\w*|a/b testi|(kullanıcı|müşteri) (interview\\w*|görüşme\\w*|mülakat\\w*)|user (görüşme\\w*|mülakat\\w*)|anket\\w*|kök neden (analiz|araştır)\\w*|kaynak tarama\\w*|referans tarama\\w*|(yöntem|yaklaşım|kütüphane|varyant|seçenek)\\w* karşılaştır\\w*|ölçüm\\w*|deneysel\\w*|deneyler\\w*|makale\\w*|veri (seti|kümesi)|olay sonrası (analiz|değerlendirme)\\w*|hata bütçe\\w*|gerçekten (doğru|öyle) mu|kanıt\\w*|test kapsam\\w*|kapsam oran\\w*|kapsamı (artır|yükselt)\\w*|doküman(tasyon)?\\w* (yetersiz|eksik|kötü|zayıf)"
197
+ ],
198
+ "product": [
199
+ "ürün\\w*|yol harita\\w*|kuzey yıldız\\w*|aktivasyon\\w*|dönüşüm\\w*|fiyatlandır\\w*|önceliklendir\\w*|kullanıcı araştırma\\w*|ürün keşf\\w*|müşteri geri bildirim\\w*|benimsen\\w*|konumlandır\\w*|segmentasyon\\w*|hedef kitle\\w*|(kullanıcı|müşteri) discovery|keşif (görüşme|çalışma)\\w*|fırsat\\w*|memnuniyet\\w*|anket\\w*|(kullanıcı|müşteri)[ -]?(feedback|geri bildirim)\\w*|(customer|user)[ -]?geri bildirim\\w*|şikayet\\w*|rakip\\w*|rekabet\\w*|pazar pay\\w*|terk oran\\w*|elde tutma\\w*|abonelik\\w*|gelir model\\w*|özellik\\w*|sayfa(?! numara)\\w*|ekran(?! kart)(?!a\\b[^.!?\\n]{0,40}\\b(?:yaz|bas)\\w*)\\w*|arayüz\\w*|arama kutu\\w*|filtre\\w*|tablo(?![^.!?\\n]{0,60}\\b(?:sütun|kolon|sql|index|indeks)\\w*)\\w*|adım(?! sayı)\\w*|kullanıcı liste\\w*|form(u|un|da|daki|lar|ları|unu)\\w*"
200
+ ],
201
+ "graph": [
202
+ "kim çağır\\w*|çağrı yerleri|nerede tanımlı|nasıl bağlan\\w*|etkilenir\\w*|hangi dosyalar etkilen\\w*|etki (analiz|alan)\\w*|neyi etkile\\w*|neleri etkile\\w*|etkilen\\w*|bağımlı\\w*|kullanılmayan\\w*|kullanılmıyor|ölü kod|döngüsel (bağımlılık|import)\\w*|giriş nokta\\w*|uygulama (nerede|nereden) başl\\w*|kim import ed\\w*|(fonksiyon|metod|sınıf|modül|api)\\w* (kim|nerede) kullan\\w*|nerede (kullanıl|çağrıl)\\w*|tanım\\w* nerede|kullanım yerleri|çağrı (zinciri|grafiği)|bağımlılık (grafiği|ağacı)|yeniden adlandır\\w*"
203
+ ]
204
+ },
205
+ "fan_verbs": [
206
+ "düzelt|güncelle|ekle|yaz|sil|kaldır|taşı|bitir|tamamla|incele|oluştur|değiştir"
207
+ ],
208
+ "fan_join": [
209
+ "ve"
210
+ ],
211
+ "fan_ordinals": [
212
+ "birinci(?:si)?",
213
+ "ikinci(?:si)?"
214
+ ],
215
+ "fan_directives": [
216
+ "paralel\\w*|alt ?ajan\\w*|orkestra\\w*"
217
+ ],
218
+ "data_ask": [
219
+ "(?:metri[ck]s?|kpis?|data|sales|revenue) (?:\\w+ ){0,2}pano|(?:veri\\w*|satış\\w*|gelir\\w*) (?:\\w+ ){0,2}(?:dashboard|pano)|(?:dashboard|gösterge panel)\\w* (?:\\w+ ){0,4}(?:tasarla|oluştur|hazırla|yap|göster)\\w*|öner\\w* (?:\\w+ ){0,3}grafi\\w*|hangi (?:metri[ck]\\w*|kpi\\w*|grafi\\w*|göstergeleri?)|(?:metri[ck]\\w*|kpi\\w*) (?:\\w+ ){0,3}(?:tasarla|seç|tanımla|belirle|takip|izle)\\w*|gösterge panel\\w*|visuali[sz]\\w* (?:\\w+ ){0,3}veri\\w*|veri\\w* (?:\\w+ ){0,3}görselleştir\\w*"
220
+ ],
221
+ "code_sense": [
222
+ "\\b(?:indeks\\w*|veri yapı\\w*|fonksiyon\\w*|bileşen\\w*|modül\\w*|dosya\\w*|kopyala\\w*|seçici\\w*|yükleme|ilerleme|paket\\w*)\\b"
223
+ ],
224
+ "constraint": {
225
+ "dont": [
226
+ "(?P<obj>[\\w./@~-]+?)(?:'\\w+)?\\s+(?:sakın\\s+)?dokunma(?:yın|yınız)?\\b(?!\\s+(?:hedef|alan|olay|duyar)\\w*)"
227
+ ],
228
+ "ask": [
229
+ "(?:bana\\s+)?sormadan\\s+(?P<ask>\\w+)\\s+(?:etme|yapma)\\w*"
230
+ ],
231
+ "grant": [
232
+ "(?:bana\\s+)?sormadan(?!\\s+(?:\\w+\\s+)?(?:etme|yapma|ilerleme|gitme)\\w*)|onay(?:ı|ımı)?\\s+ver(?:iyorum|dim)|izin\\s+ver(?:iyorum|dim)|onaylıyorum|sormana gerek yok"
233
+ ],
234
+ "lift": [
235
+ "(?:artık|bundan sonra|tekrar|yine)\\s+(?:bana\\s+)?sor\\b|onay\\w*\\s+geri\\s+al\\w*|izn\\w*\\s+geri\\s+al\\w*"
236
+ ],
237
+ "lift_outward": [
238
+ "(?:pr\\b|push\\w*|release\\w*|yayınla\\w*|yayımla\\w*|sürüm\\b|versiyonla\\w*|etiketle\\w*|tag\\w*)(?:\\s+\\S+){0,2}?\\s+(?:et|yap|çıkar)(?:ma|me)(?:y[ıi]n\\w*)?\\b",
239
+ "(?:push|yay[ıi]nla|yayımla|etiketle|versiyonla|tag)(?:la|le)?m[ae](?:y[ıi]n\\w*)?\\b"
240
+ ],
241
+ "grant_not": [
242
+ "\\b(?!bütün\\b)\\w+[dt][ıiuü]n(?:[ıi]z)?\\b|\\bneden\\b|\\bniye\\b"
243
+ ],
244
+ "grant_not_verb": [
245
+ "\\w{2,}m[ae](?:y[ıi]n(?:[ıi]z)?)?"
246
+ ],
247
+ "grant_not_soft": [
248
+ "durma|bekleme|sorma|unutma"
249
+ ]
250
+ },
251
+ "ask": {
252
+ "process": [
253
+ "\\b(?:başlayayım|devam edeyim|ilerleyeyim|geçeyim|sürdüreyim|uygulayayım|yapayım|açayım|kapatayım|birleştireyim|merge edeyim|commit(?:le)?(?:yeyim|eyim| atayım)|koşayım|çalıştırayım)\\b|\\b(?:devam etmemi|başlamamı|ilerlememi|commit atmamı|merge etmemi|uygulamamı|yapmamı) ister mis\\w*|\\bhangisi(?:nden|yle|ni)? (?:önce|ile başla|başla\\w*)|\\bnereden başla\\w*"
254
+ ],
255
+ "never_default": [
256
+ "zorla|geçmiş\\w* (?:yeniden yaz|değiştir)\\w*|\\bsil\\w*|kaldır\\w*|yok et\\w*|üretim\\w*|\\bcanlı\\w*|\\bprod\\w*|göç\\w*|kimlik bilgi\\w*|şifre\\w*|parola\\w*|\\bhesa[bp]\\w*|e-?posta\\w*|gönder\\w*"
257
+ ],
258
+ "spend": [
259
+ "ücret\\w*|ödeme\\w*|harca\\w*|bütçe\\w*|maliyet\\w*|\\bpara\\b|dolar\\w*|\\$\\s?\\d"
260
+ ],
261
+ "default_mark": [
262
+ "öneri\\w*|önerilen|önerim|varsayılan\\w*|tavsiye\\w*"
263
+ ],
264
+ "options": [
265
+ "\\bveya\\b|\\bya da\\b|\\byoksa\\b|seçenek\\w*|alternatif\\w*|\\bm[ıiuü]\\b.+\\bm[ıiuü]\\b|\\b(?:a|1)\\)?\\s.+?,\\s*(?:b|2)\\)?\\s"
266
+ ],
267
+ "cheap_answer": [
268
+ "evet|tamam|olur|devam(?: et)?|yap|başla|ilerle|okey|onay(?:lıyorum)?"
269
+ ],
270
+ "outward": {
271
+ "push": "\\bpush\\w*",
272
+ "release": "versiyon\\w*|sürüm\\w*|\\brelease\\w*|etiket\\w*",
273
+ "publish": "yayınla\\w*|yayımla\\w*|\\bnpm\\b|homebrew",
274
+ "pr": "\\bpr\\b|pull request"
275
+ }
276
+ },
277
+ "needle_stop": [
278
+ "ve",
279
+ "veya",
280
+ "ile"
281
+ ],
282
+ "rank_stopwords": [
283
+ "bir",
284
+ "bunu",
285
+ "buna",
286
+ "bunlar",
287
+ "şunu",
288
+ "için",
289
+ "ile",
290
+ "ama",
291
+ "gibi",
292
+ "daha",
293
+ "çok",
294
+ "kadar",
295
+ "veya",
296
+ "değil",
297
+ "her",
298
+ "hem",
299
+ "olan",
300
+ "olarak",
301
+ "sonra",
302
+ "önce",
303
+ "ise",
304
+ "yani",
305
+ "nasil",
306
+ "neden"
307
+ ],
308
+ "fold": [
309
+ "ıi",
310
+ "âa",
311
+ "îi",
312
+ "ûu"
313
+ ],
314
+ "say": {
315
+ "unverified": "doğrulanmadı",
316
+ "reply_lang": "Turkish",
317
+ "reply_in": "in Turkish, even when the user\nwrites English",
318
+ "reply_split": "in Turkish, ALWAYS, even when the user writes English",
319
+ "reply_short": "reply Turkish",
320
+ "gap_example": "\"deploy öncesi doğrulamadım; riski şöyle değerlendirdim: ...\"",
321
+ "blocker_tokens": "KARAR, YETKİ, GERİ ALINAMAZ, ORTAM",
322
+ "agree_word": "\"haklısın\"",
323
+ "opener_short": "\"haklısın\", \"you're right\", \"good catch\", \"detaylı bakmadım\", \"I didn't look closely\"",
324
+ "decision_gloss": "DECISION: ",
325
+ "access_gloss": "ACCESS: ",
326
+ "irreversible_gloss": "IRREVERSIBLE: ",
327
+ "env_gloss": "ENV: ",
328
+ "blockers_heading": "Engeller",
329
+ "decision": "KARAR",
330
+ "access": "YETKİ",
331
+ "irreversible": "GERİ ALINAMAZ",
332
+ "env": "ORTAM",
333
+ "done_claim": "yaptım",
334
+ "opener_examples": "\"haklısın\", \"you're right\", \"absolutely right\", \"good catch\", \"iyi yakaladın\", \"detaylı bakmadım\", \"I didn't look closely\", \"I should have checked\"",
335
+ "opener_ban": "never \"haklısın\" / \"you're right\" / \"detaylı bakmadım\" / an apology",
336
+ "residual_lead": "Bu maddeleri şimdi yap veya engel türü ve kanıtla yaz. ",
337
+ "slice_lead": "Durmuş bir dilim var. ",
338
+ "ready_lead": "Hazır adımlar bekliyor. ",
339
+ "ask_lead": "Bu soruyu sorma: kararı ver ya da önerili onay sorusuna çevir. ",
340
+ "approval_heading": "Onay için sorular",
341
+ "recommended": "Öneri",
342
+ "grant_examples": "\"bana sormadan pushla\", \"onay veriyorum\""
343
+ }
344
+ }
@@ -14,7 +14,7 @@ sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
14
14
  from tezgah_guard import attest_session, import_failed, safe # noqa: E402
15
15
  try:
16
16
  from tezgah_context import context_for, record # noqa: E402
17
- from tezgah_paths import HOST_DIRS # noqa: E402
17
+ from tezgah_paths import HOST_DIRS, root_for # noqa: E402
18
18
  except Exception as exc:
19
19
  import_failed(exc)
20
20
 
@@ -62,6 +62,20 @@ def core_is_in_a_file():
62
62
  return False
63
63
 
64
64
 
65
+ def export_session(env_file, session_id):
66
+ """Append `export TEZGAH_SESSION=<id>` to Claude's CLAUDE_ENV_FILE, appended
67
+ because other hooks write the same file."""
68
+ import shlex
69
+ with open(env_file, "a", encoding="utf-8") as fh:
70
+ fh.write("export TEZGAH_SESSION=%s\n" % shlex.quote(session_id))
71
+
72
+
73
+ def note_child(session_id, agent_id, agent_type):
74
+ """The child's lineage row in its parent's ledger (`tezgah-route --report`)."""
75
+ from tezgah_integrity import note
76
+ note(session_id, "subagent_start", agent_type, agent=agent_id)
77
+
78
+
65
79
  def main():
66
80
  try:
67
81
  payload = json.load(sys.stdin)
@@ -74,6 +88,14 @@ def main():
74
88
  # tezgah wires, and dsh (which runs it too) has no transcript to derive
75
89
  # the kind from, so the store is the only channel its line has.
76
90
  safe(payload.get("session_id"), record, payload.get("session_id"), "orch")
91
+ # The child's lineage row: a Claude subagent runs under its parent's
92
+ # session id, its calls carrying `agent_id`, so this row in the parent's
93
+ # ledger is what types it for `tezgah-route --report` (hooks reference,
94
+ # SubagentStart input: agent_id, agent_type).
95
+ aid, kind = payload.get("agent_id"), payload.get("agent_type")
96
+ if isinstance(aid, str) and aid and isinstance(kind, str) and root_for(cwd):
97
+ safe(payload.get("session_id"), note_child, payload.get("session_id"),
98
+ aid, kind)
77
99
  if event == "SessionStart":
78
100
  # Claude and dsh run this one script; Claude's manifest declares
79
101
  # TEZGAH_CORE_IN_FILE on SessionStart and dsh's does not, so the flag
@@ -82,6 +104,12 @@ def main():
82
104
  sid = payload.get("session_id")
83
105
  host = "claude" if os.environ.get("TEZGAH_CORE_IN_FILE") == "1" else "dsh"
84
106
  safe(sid, attest_session, host, sid, cwd)
107
+ # The session id the route, judge and status CLIs write under: Claude
108
+ # sources this file before every Bash call (CLAUDE_ENV_FILE, SessionStart
109
+ # only), as omp exports TEZGAH_SESSION to its shells
110
+ env_file = os.environ.get("CLAUDE_ENV_FILE")
111
+ if env_file and isinstance(sid, str) and sid:
112
+ safe(sid, export_session, env_file, sid)
85
113
  text = safe(payload.get("session_id"), context_for,
86
114
  EVENTS.get(event, "session_start"), cwd, payload,
87
115
  # the brief is a subagent's own text, never the file's; every
@@ -31,6 +31,18 @@ try:
31
31
  except Exception as exc:
32
32
  import_failed(exc)
33
33
 
34
+
35
+ def cross_note(session_id, tool, inp, cwd):
36
+ """The cross-project write notice (hooks/tezgah_gate.py::cross_project_note),
37
+ imported here and not at the top: a gate module without it costs this one
38
+ line, never the evidence row."""
39
+ try:
40
+ from tezgah_gate import cross_project_note
41
+ except ImportError:
42
+ return None
43
+ return cross_project_note(session_id, tool, inp, cwd)
44
+
45
+
34
46
  # The tool-use kinds this hook can see, for the status line's used marks. Claude
35
47
  # reads them from its transcript and ignores this store, but dsh runs this same
36
48
  # file with no transcript of its own - the store is the only channel its Web
@@ -139,13 +151,20 @@ def main():
139
151
  # the Bash result's own stdout/stderr, read on a bounded tail for the
140
152
  # empty-run contract (tezgah_integrity.EMPTY_RUN)
141
153
  empty_run=ran_nothing(result),
142
- agent=agent)
154
+ agent=agent,
155
+ # a background spawn's result names its child (`agentId`), the
156
+ # `agent_id` that child's own calls carry: its slice record keeps it
157
+ # (hooks/tezgah_gate.py::lease_reason)
158
+ child=(result.get("agentId") if isinstance(result, dict)
159
+ and result.get("status") == "async_launched" else None))
143
160
  # the opt-in taste note: the learnings in scope for the first write to this
144
- # file type in the session (hooks/tezgah_taste.write_note), beside the
145
- # provenance line rather than instead of it
161
+ # file type in the session (hooks/tezgah_taste.write_note), and the
162
+ # cross-project write notice (hooks/tezgah_gate.py::cross_project_note),
163
+ # beside the provenance line rather than instead of it
146
164
  if event == "PostToolUse" and not failed:
147
- notice = "\n".join(t for t in (notice, safe(session_id, write_note, session_id,
148
- inp, cwd, tool)) if t)
165
+ notice = "\n".join(t for t in (
166
+ notice, safe(session_id, write_note, session_id, inp, cwd, tool),
167
+ safe(session_id, cross_note, session_id, tool, inp, cwd)) if t)
149
168
  if notice:
150
169
  json.dump({"hookSpecificOutput": {
151
170
  "hookEventName": event,
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """Claude Code PreToolUse hook (matcher Agent|Task|Grep|Bash), inert outside roots.
2
+ """Claude Code PreToolUse hook (matcher Agent|Task|Grep|Bash|...|AskUserQuestion),
3
+ inert outside roots.
3
4
 
4
5
  Claude's envelope around the shared gate (hooks/tezgah_gate.py): refuse the
5
6
  grep-only explorer subagent, and nudge the first code-symbol search of a
@@ -13,15 +14,19 @@ import sys
13
14
  sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
14
15
  from tezgah_guard import import_failed, safe # noqa: E402
15
16
  try:
16
- from tezgah_gate import decision, graph_advice # noqa: E402
17
+ from tezgah_gate import ask_advice, decision, graph_advice, spawn_advice # noqa: E402
17
18
  except Exception as exc:
18
19
  import_failed(exc)
19
20
 
20
21
 
21
22
  def verdict(tool, inp, cwd, session_id, agent):
22
- """(deny reason, graph line): the line only for a call the gate lets through."""
23
+ """(deny reason, advice line): the graph line, the shared-worktree warning
24
+ of a spawn or the ask-policy warning of an AskUserQuestion call, only for a
25
+ call the gate lets through."""
23
26
  reason = decision(tool, inp, cwd, session_id, agent=agent)
24
- return reason, None if reason else graph_advice(tool, inp, cwd, "claude")
27
+ return reason, None if reason else (graph_advice(tool, inp, cwd, "claude")
28
+ or spawn_advice(tool, inp, session_id, cwd)
29
+ or ask_advice(tool, inp, session_id, cwd))
25
30
 
26
31
 
27
32
  def main():
@@ -1,16 +1,23 @@
1
1
  #!/usr/bin/env python3
2
2
  """Claude Code Stop / SubagentStop hook: refuse to end a turn on a false claim.
3
3
 
4
- Blocks (decision: "block") when the final message opens by placating, or claims
4
+ Blocks (decision: "block") when the final message opens by placating, claims
5
5
  done/tested/passing in a session whose newest turn changed code and left a check
6
- failing or unproven by a later pass. An explicit "doğrulanmadı" always clears
7
- it, so honest uncertainty is never punished. With `stop_hook_active` set - the
8
- reply after a block - the reply is judged in record-only mode (an `after_block`
9
- row) and never blocked again, and Claude Code overrides the hook after its own
10
- consecutive-block cap, so this can never trap a session. A subagent's end
11
- (SubagentStop) is judged record-only too: one `subagent_end` row, never a
12
- block (ADR 011). Inert outside a tezgah root or under the `verify-off` kill
13
- switch.
6
+ failing or unproven by a later pass, or ends a turn that did work on a list of
7
+ what is left with an item no blocker accounts for (`residual work`). An explicit
8
+ "doğrulanmadı" clears only a claim whose repair is a check, and only after a
9
+ check that ran and failed since the newest change: the word alone used to clear
10
+ every refusal, so writing it was cheaper than verifying (plan 080). With
11
+ `stop_hook_active` set - the reply after a block - the reply is judged again:
12
+ it is refused once more only for a reason class this turn was not refused for
13
+ yet, at most two refusals per turn (`tezgah_integrity._record_only`, counted
14
+ from the ledger), and otherwise recorded as an `after_block` row. Claude Code
15
+ also overrides the hook after its own consecutive-block cap, so this can never
16
+ trap a session. A subagent's end (SubagentStop) is judged record-only: one
17
+ `subagent_end` row, never a block (ADR 011); it also closes the slice record
18
+ of a background child that ended (`tezgah_task.close_child`), so its lease
19
+ goes with it. Inert outside a tezgah root or under the `verify-off` kill
20
+ switch (the record close only under `slices-off`).
14
21
  """
15
22
  import json
16
23
  import os
@@ -24,10 +31,13 @@ try:
24
31
  except Exception as exc:
25
32
  import_failed(exc)
26
33
 
27
- # How many times one stop chain may be refused: the one-nudge semantics is
28
- # deliberate, and raising it is owner decision 11. Claude says only whether a
29
- # block came before (`stop_hook_active`), so a chain has blocked 0 or 1 times.
30
- STOP_REASKS = 1
34
+
35
+ def close_child(cwd, session_id, agent_id):
36
+ """The background child's record closes when its SubagentStop arrives:
37
+ Claude sends no other end for a `run_in_background` launch."""
38
+ import tezgah_task
39
+ root = tezgah_task.main_repo(cwd)
40
+ return root and tezgah_task.close_child(root, session_id, agent_id)
31
41
 
32
42
 
33
43
  def main():
@@ -39,20 +49,22 @@ def main():
39
49
  return
40
50
  session_id = p.get("session_id")
41
51
  safe(session_id, bind_session, session_id) # the switch latch (tezgah_paths.off)
52
+ cwd = p.get("cwd") or os.getcwd()
53
+ if p.get("hook_event_name") == "SubagentStop" and p.get("agent_id") \
54
+ and root_for(cwd) and not off("slices-off"):
55
+ safe(session_id, close_child, cwd, session_id, p.get("agent_id"))
42
56
  if off("verify-off"):
43
57
  return
44
- cwd = p.get("cwd") or os.getcwd()
45
58
  if not root_for(cwd):
46
59
  return
47
60
  if p.get("hook_event_name") == "SubagentStop":
48
61
  safe(session_id, stop_reason, p.get("last_assistant_message"),
49
62
  session_id, cwd=cwd, subagent=True, agent=p.get("agent_id"))
50
63
  return
51
- blocked = 1 if p.get("stop_hook_active") else 0
52
64
  # a core that cannot answer has refused nothing, so the turn ends: the rule
53
65
  # never traps a session, and that holds for its own failure too
54
66
  reason = safe(session_id, stop_reason, p.get("last_assistant_message"),
55
- session_id, cwd=cwd, record_only=blocked >= STOP_REASKS)
67
+ session_id, cwd=cwd, after_block=bool(p.get("stop_hook_active")))
56
68
  if reason:
57
69
  json.dump({"decision": "block", "reason": reason}, sys.stdout)
58
70