universal-dev-standards 6.13.0 → 6.14.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/ai/standards/open-work-tracking.ai.yaml +69 -4
- package/bundled/ai/standards/turn-completion-integrity.ai.yaml +14 -7
- package/bundled/core/open-work-tracking.md +111 -8
- package/bundled/core/turn-completion-integrity.md +58 -11
- package/bundled/hooks/check-turn-completion-agy.mjs +147 -0
- package/bundled/locales/zh-CN/CHANGELOG.md +29 -3
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-CN/SECURITY.md +2 -1
- package/bundled/locales/zh-CN/core/turn-completion-integrity.md +46 -13
- package/bundled/locales/zh-CN/docs/CHEATSHEET.md +4 -1
- package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +9 -8
- package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +9 -6
- package/bundled/locales/zh-TW/CHANGELOG.md +29 -3
- package/bundled/locales/zh-TW/README.md +1 -1
- package/bundled/locales/zh-TW/SECURITY.md +2 -1
- package/bundled/locales/zh-TW/core/open-work-tracking.md +88 -9
- package/bundled/locales/zh-TW/core/turn-completion-integrity.md +46 -13
- package/bundled/locales/zh-TW/docs/CHEATSHEET.md +4 -1
- package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +9 -8
- package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +9 -6
- package/package.json +1 -1
- package/src/commands/init.js +15 -2
- package/src/commands/uninstall.js +1 -1
- package/src/commands/update.js +23 -12
- package/src/installers/hooks-installer.js +89 -2
- package/src/reconciler/desired-state-calculator.js +84 -1
- package/src/reconciler/plan-executor.js +34 -1
- package/src/uninstallers/hook-uninstaller.js +107 -4
- package/src/utils/integration-generator.js +97 -19
- package/standards-registry.json +8 -8
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../../core/open-work-tracking.md
|
|
3
|
-
source_version: 1.
|
|
4
|
-
translation_version: 1.
|
|
5
|
-
last_synced: 2026-09-
|
|
6
|
-
source_hash:
|
|
3
|
+
source_version: 1.1.0
|
|
4
|
+
translation_version: 1.1.0
|
|
5
|
+
last_synced: 2026-09-29
|
|
6
|
+
source_hash: 3ea4d18700d3
|
|
7
7
|
status: current
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -11,8 +11,8 @@ status: current
|
|
|
11
11
|
|
|
12
12
|
> **Language**: [English](../../../core/open-work-tracking.md) | 繁體中文
|
|
13
13
|
|
|
14
|
-
**版本**: 1.
|
|
15
|
-
**最後更新**: 2026-09-
|
|
14
|
+
**版本**: 1.1.0
|
|
15
|
+
**最後更新**: 2026-09-29
|
|
16
16
|
**適用**: 任何跨越一個以上工作階段承載工作、有可能在階段之間遺失項目的專案
|
|
17
17
|
**範圍**: universal
|
|
18
18
|
|
|
@@ -34,7 +34,8 @@ status: current
|
|
|
34
34
|
| 某項目因等待別的事件而暫停 | 「等待中」若沒有記錄解除條件,與「被忘記」無法分辨 | 與等待一起記錄的解除條件 |
|
|
35
35
|
| 已規劃的項目還沒動工,時間過去 | 沒有時鐘的項目會無聲腐爛——沒有東西會再指向它 | 一個門檻,或一次被迫的定期檢視,讓它重新浮現 |
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
下面每一條要求都對應這張表的一列、對應本標準設計當天觀察到的四個失效之一,
|
|
38
|
+
或(OWT-017–OWT-019)對應 1.1.0 補上的兩個缺口
|
|
38
39
|
(見〈[證據與校準](#證據與校準)〉)。**沒有任何一個機制被規定**——
|
|
39
40
|
理由與 [deferred-item-exit](deferred-item-exit.md) 對自己出口的約束相同(DEC-049:
|
|
40
41
|
UDS 定義必須成立的關係,維持它的機制由採用層選擇)。
|
|
@@ -56,6 +57,7 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
|
|
|
56
57
|
| 「這是最新的」這句宣稱必須從什麼可被證明 | 具體用哪個雜湊函式、diff 工具或 CI 供應商 |
|
|
57
58
|
| 一個回報數字與它看不到的部分之間的關係 | 儀表板版面或報告範本 |
|
|
58
59
|
| 控制權交回人的那一點存在一個確認點,且它永不阻斷 | 用什麼 hook 系統、shell 或 cron 實作它 |
|
|
60
|
+
| 一次目標的修改與說明它的修訂紀錄之間必須存在的關係 | 哪一種版本控制系統、核可工具或檔案配置承載其中任何一個 |
|
|
59
61
|
|
|
60
62
|
**直說它的後果**:本標準**不附帶任何閘門**。它只說一個承載開放工作的地方必須具備什麼性質;
|
|
61
63
|
有沒有東西在檢查,是採用專案的決定——[OWT-014](#要求) 與 [OWT-015](#要求)
|
|
@@ -67,7 +69,9 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
|
|
|
67
69
|
|
|
68
70
|
**一個承載開放工作的地方,必須:(1)不要求分類就能收下新項目、(2)為每一個標為等待中的項目記下解除條件、
|
|
69
71
|
(3)對任何有可靠來源可推導的欄位改用生成、(4)回報還剩什麼時同時揭露看不到什麼、
|
|
70
|
-
(5)在控制權從 agent
|
|
72
|
+
(5)在控制權從 agent 交回人的那一刻被檢視——而且那個檢視不能讓回合失敗、
|
|
73
|
+
(6)把「這份工作為了什麼」與「做到哪了」分開存放,並替前者的每一次修改留下交代、
|
|
74
|
+
(7)讓每一個「下一步」都點名一個讀的人找得到的東西。**
|
|
71
75
|
|
|
72
76
|
---
|
|
73
77
|
|
|
@@ -91,6 +95,9 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
|
|
|
91
95
|
| **OWT-014** | 本標準的每一條要求都可表述為 artefact 之間可判定的關係。不能如此表述的要求不得進入本標準 | error |
|
|
92
96
|
| **OWT-015** | 被提出作為本標準任一要求之證據的檢查,已被觀察到對一個刻意違反該要求的樣本回報失敗。從未紅過的檢查不是可採信的證據 | error |
|
|
93
97
|
| **OWT-016** | 本標準各要求所引用的任何窗口或閾值,載明來歷,或標為未校準 | warning |
|
|
98
|
+
| **OWT-017** | 承載一件工作之目標、驗收條件或限制的載體,不同時承載它的進度或下一步,反之亦然。以走訪各載體的結構欄位(小節、欄位、型別化標記)判定,絕不看檔名。更新進度因此不需要碰目標 | warning |
|
|
99
|
+
| **OWT-018** | 對一件工作的目標、驗收條件或限制的每一次修改,都留下載明改了什麼、誰核可、為什麼的修訂紀錄。沒有核可者的修改,在控制權交回人時被列出(OWT-007),不得靜默 | error |
|
|
100
|
+
| **OWT-019** | 「下一步」欄位至少點名一個具體對象:檔案路徑、測試名稱、指令或需求編號之一。只有動詞(「繼續」「處理剩下的」)不算。它判斷有沒有點名對象,絕不判斷句子措辭好壞 | warning |
|
|
94
101
|
|
|
95
102
|
---
|
|
96
103
|
|
|
@@ -173,6 +180,61 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
|
|
|
173
180
|
|
|
174
181
|
---
|
|
175
182
|
|
|
183
|
+
## 意圖與進度是兩種不同的事實
|
|
184
|
+
|
|
185
|
+
**意圖**——目標、驗收條件、限制——說的是「完成」是什麼意思。它很少改,而且只在有人決定要改時才改。
|
|
186
|
+
**進度**——做到哪、還剩什麼、下一步、卡在哪——每個工作階段都在變,由做事的 agent 來寫。
|
|
187
|
+
兩者住在同一個載體時,每一次例行的進度更新,都是在編輯那份定義「完成」的文件本身,
|
|
188
|
+
而「定義被改了」在審查裡、在 diff 裡,都與日常記帳分不出來。**OWT-017** 把它們分開。
|
|
189
|
+
形式不限:規格檔搭配工作紀錄、或目標/狀態兩個檔,都符合。**判準是結構,不是檔名**——
|
|
190
|
+
走訪每個載體的小節、欄位與型別化標記,問同一個載體是否同時裝著兩種東西。
|
|
191
|
+
|
|
192
|
+
**OWT-018** 點名的,是光靠分開存放防不了的失效:工作進行到一半,某條驗收條件被改了,
|
|
193
|
+
而沒有任何紀錄說明誰同意過。事後「每一條驗收都滿足了」依然為真——只是針對另一組條件。
|
|
194
|
+
它與「新戳蓋在舊內容上」是同一個形狀:被改過的目標,讀起來與一個一直這麼寫的目標一模一樣。
|
|
195
|
+
所以每一次對意圖的修改,都要留下**改了什麼、誰核可、為什麼**的紀錄。沒有核可者的修改並不被禁止——
|
|
196
|
+
agent 正當地會提出修改,禁止只會教它學會靜默地改——但它要在**控制權交回人的那一刻被列出來**(OWT-007)。
|
|
197
|
+
如同掛在那個事件上的一切,列出只是回報,永不阻斷(OWT-008)。這裡檢查能判定的就只有可判定的部分:
|
|
198
|
+
意圖有沒有變、有沒有新增一筆紀錄、那筆紀錄是否完整、核可者欄位有沒有填。
|
|
199
|
+
**它判定不了那筆紀錄是否誠實描述了這次修改**——那是關於語意的宣稱,不是 artefact 之間的關係(OWT-014),
|
|
200
|
+
本標準不假裝它做得到。
|
|
201
|
+
|
|
202
|
+
**這些嚴重度的理由。** OWT-018 是 `error`,因為這個失效是靜默的,而它只有一個時刻能被抓到——修改發生的那一刻;
|
|
203
|
+
事後所有人能讀到的,就只剩被改過的文字。OWT-017 是 `warning`,因為分開存放只是手段:
|
|
204
|
+
分開了卻沒有修訂紀錄(OWT-018)照樣漏,單一載體配上嚴格的修訂紀錄照樣達成 OWT-018 的目的,
|
|
205
|
+
所以違反的傷害是間接的。OWT-019 是 `warning`,因為「有點名對象」的結構檢查必然粗糙——
|
|
206
|
+
被點名的對象仍可能無關——而違反的代價是下一個工作階段的時間,不是工作本身。
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 一個什麼都沒點名的下一步,只是一種心情
|
|
211
|
+
|
|
212
|
+
「繼續實作」與「處理剩下的」,與一個被遺忘的項目分不出來:下一個工作階段沒有起點可以開始,
|
|
213
|
+
得重新推導工作停在哪——而那正是本標準整個存在要避免的成本。**OWT-019** 要求「下一步」欄位
|
|
214
|
+
至少點名一個讀的人找得到的對象——檔案路徑、測試名稱、指令、或需求編號。
|
|
215
|
+
它是**結構**判準(有沒有點名對象),不是措辭好壞的判斷;措辭漂亮但什麼都沒點名的句子照樣不過,
|
|
216
|
+
簡短但點了一個測試名稱的句子照樣過。這讓它留在 OWT-010 與 OWT-014 的範圍之內。
|
|
217
|
+
|
|
218
|
+
檢查回報三種結果,而不是一個綠燈:**點名且已找到**(對象被找到——例如路徑存在)、**點名但未找到**
|
|
219
|
+
(有點名對象但找不到——當下一步就是要建立它時是正當的)、**未點名**(違反)。
|
|
220
|
+
辨認「這串字是路徑、指令、測試名稱還是編號」本身是樣式比對,所以依 OWT-011 其涵蓋率明示為未知:
|
|
221
|
+
認不出的格式會被回報為未點名,而乾淨的通過絕不表示「每個下一步都夠具體」。
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 本標準刻意不採納的東西
|
|
226
|
+
|
|
227
|
+
上面兩項新增,起因是使用者轉貼的一份提示詞——**作者與出處不明,也沒有任何實作**。
|
|
228
|
+
只借了它的設計形狀,本文不引用它的任何宣稱。它提出的其餘部分經過檢視、**沒有**採納,
|
|
229
|
+
理由是機制層的,不是口味:
|
|
230
|
+
|
|
231
|
+
| 不採納 | 理由(機制層) |
|
|
232
|
+
|---|---|
|
|
233
|
+
| 以手寫狀態檔作為狀態真相 | 手寫狀態會過期,而「戳比過期內容新」是隱形的(OWT-004、OWT-005 存在的起因)。只說「兩者不一致時以版本控制為準」,卻沒有任何機制讓該檔與版本控制對帳,就是採納一個已知會過期的來源。OWT-003 已經要求可推導的欄位改用生成 |
|
|
234
|
+
| 固定的開工儀式(讀檔→查版本控制→驗證) | 交接點已被管住:回合結束有 OWT-007,另有 [turn-completion-integrity](turn-completion-integrity.md)。開工儀式要靠各代理工具自己的指示來設定;寫進這裡只會得到一條沒有任何 artefact 上的檢查判定得了的要求,而那正是 OWT-014 排除的東西 |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
176
238
|
## 一條無法被檢查的要求,不是這裡的要求
|
|
177
239
|
|
|
178
240
|
**OWT-014** 是對本標準自身內容的約束,與 [deferred-item-exit](deferred-item-exit.md) 的
|
|
@@ -211,14 +273,20 @@ DEX-003 扮演的角色相同。上面每一條都指名了 artefact 與它們
|
|
|
211
273
|
| 確認點擋住回合結束、理由是「還有工作沒做完」 | 每個回合都會觸發;永遠為真的閘門會被關掉,關掉之後什麼都不保護 |
|
|
212
274
|
| 分類狀態只靠散文措辭判讀 | 正確到某個項目用清單沒預料到的方式寫出來為止 |
|
|
213
275
|
| 項目從承載庫裡無聲消失 | 與一個弄丟它的 bug 無從分辨 |
|
|
276
|
+
| 目標或驗收條件被改了,卻沒有任何「誰同意」的紀錄 | 「每一條驗收都滿足」依然為真,只是針對另一組條件;被改過的目標讀起來像一直這麼寫 |
|
|
277
|
+
| 把手寫的狀態檔當成狀態真相 | 會過期,而比過期內容新的戳看不見 |
|
|
278
|
+
| 下一步寫「繼續實作」或「處理剩下的」 | 沒有點名任何可以開始的東西;與被遺忘的項目無從分辨 |
|
|
214
279
|
|
|
215
280
|
---
|
|
216
281
|
|
|
217
282
|
## 什麼在執行本標準
|
|
218
283
|
|
|
219
|
-
**UDS
|
|
284
|
+
**UDS 不對本標準設任何閘門,而這件事是被記錄的,不是被暗示的。** UDS 陳述一個承載開放工作的地方
|
|
220
285
|
必須滿足的關係;有沒有東西去判定它,依上面的[寫法約束](#本標準的寫法以及為什麼這樣寫),
|
|
221
286
|
是採用專案的決定——與 [deferred-item-exit](deferred-item-exit.md) 對自己出口劃的界線相同。
|
|
287
|
+
自 1.1.0 起,UDS 為 OWT-017–OWT-019 附上一支**參考判定程序**(`scripts/check-open-work-tracking.mjs`),
|
|
288
|
+
作為 OWT-015 意義上的證據——它已被觀察到對違反的樣本回報失敗——供採用者直接執行或自行重做。
|
|
289
|
+
它沒有接進任何 UDS 發版閘門,因為 UDS 本身沒有承載開放工作的地方可供它檢查。
|
|
222
290
|
|
|
223
291
|
本標準做的事,是讓那個決定顯形:OWT-014 保證這裡每一條**能**被判定,OWT-015 固定
|
|
224
292
|
「一次判定要算數需要什麼」,OWT-005/OWT-011 固定「一次不完整的判定容許印出什麼」。
|
|
@@ -238,6 +306,15 @@ DEX-003 扮演的角色相同。上面每一條都指名了 artefact 與它們
|
|
|
238
306
|
- 依實際使用情況重新校準這兩個數字、或將其中任一個降級為專案特定指引,是採用專案自己的決定
|
|
239
307
|
與自己的時程——本標準不承諾這件事,如同它不附帶閘門一樣。
|
|
240
308
|
|
|
309
|
+
**1.1.0 的新增(OWT-017–OWT-019)**來自 2026-09-29 在一個採用專案裡發現的兩個缺口(DEC-122):
|
|
310
|
+
本標準對「把工作項目的目標與進度分開」沒有任何說法,而該專案某份規格裡的一條驗收條件,
|
|
311
|
+
在工作進行到一半時被修改,沒有任何紀錄說明誰同意過。設計形狀借自使用者轉貼的一份提示詞,作者不明
|
|
312
|
+
(見〈[本標準刻意不採納的東西](#本標準刻意不採納的東西)〉)。
|
|
313
|
+
那支參考判定程序只有幾小時大、只有一位作者,跑過的是人造樣本,不是真實的修訂歷史。
|
|
314
|
+
依 OWT-016,它用到的一切類似閾值的東西都是**未校準、初始判斷**:把某個小節認作意圖、進度、下一步、
|
|
315
|
+
或修訂紀錄的標題詞彙;它認得的指令名清單;副檔名清單;需求編號的樣式。
|
|
316
|
+
沒有任何一項對照過真實使用量測,採用專案應傳入自己的。
|
|
317
|
+
|
|
241
318
|
---
|
|
242
319
|
|
|
243
320
|
## 與其他標準的關係
|
|
@@ -253,3 +330,5 @@ DEX-003 扮演的角色相同。上面每一條都指名了 artefact 與它們
|
|
|
253
330
|
- [verification-evidence](verification-evidence.md) — OWT-015 所依賴的 exit code
|
|
254
331
|
與證據有效性推理的來源;也是 OWT-006/OWT-011 的部分涵蓋例外該被登記的地方,
|
|
255
332
|
而不是揭露一次就放著。
|
|
333
|
+
- OWT-018 掛在與 OWT-007 相同的交回點:沒有核可者的意圖修改,是在那裡多列出來的一項,
|
|
334
|
+
而且與列在那裡的一切相同,永不阻斷。
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../../core/turn-completion-integrity.md
|
|
3
|
-
source_version: 1.
|
|
4
|
-
translation_version: 1.
|
|
5
|
-
last_synced: 2026-09-
|
|
6
|
-
source_hash:
|
|
3
|
+
source_version: 1.5.0
|
|
4
|
+
translation_version: 1.5.0
|
|
5
|
+
last_synced: 2026-09-29
|
|
6
|
+
source_hash: 08579653d9a4
|
|
7
7
|
status: current
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -11,8 +11,8 @@ status: current
|
|
|
11
11
|
|
|
12
12
|
> **Language**: [English](../../../core/turn-completion-integrity.md) | 繁體中文
|
|
13
13
|
|
|
14
|
-
**版本**: 1.
|
|
15
|
-
**最後更新**: 2026-09-
|
|
14
|
+
**版本**: 1.5.0
|
|
15
|
+
**最後更新**: 2026-09-29
|
|
16
16
|
**適用範圍**: 任何由 agent 結束回合、把控制權交還給人的執行環境
|
|
17
17
|
**Scope**: universal
|
|
18
18
|
**產業標準**: 不宣稱任何來源——由實際觀察到的失敗歸納,見「證據」
|
|
@@ -144,13 +144,14 @@ agent 寫下「我接著做 X」,然後結束回合,而 X 沒有做。
|
|
|
144
144
|
## 支援的執行環境
|
|
145
145
|
|
|
146
146
|
這個檢查只在「轉接層存在,且 hook 真的被接進該執行環境自己的設定」時才生效。
|
|
147
|
-
截至 v1.
|
|
147
|
+
截至 v1.5.0:
|
|
148
148
|
|
|
149
149
|
| 執行環境 | 事件 | 設定檔 | 阻擋契約 |
|
|
150
150
|
|---|---|---|---|
|
|
151
151
|
| Claude Code | Stop | `.claude/settings.json` | stdout 印 `{"decision":"block","reason":...}`,exit 0;沉默即放行 |
|
|
152
152
|
| Codex | Stop | `.codex/hooks.json` | stdout 印 `{"decision":"block","reason":...}`,exit 0——官方文件寫明這個事件純文字或空輸出無效 |
|
|
153
153
|
| Gemini CLI(過時) | AfterAgent | `.gemini/settings.json` | stdout 印 `{"decision":"deny","reason":...}`,exit 0——官方文件標記為優先於 exit code 2 的做法 |
|
|
154
|
+
| Antigravity CLI(`agy`) | Stop | `.agents/hooks.json` | stdout 印 `{"decision":"continue","reason":...}`,exit 0;`{}` 即放行 |
|
|
154
155
|
|
|
155
156
|
在 Codex 上,接上了不等於會執行。Codex 會略過專案層級的 hook,直到專案被信任、**而且**
|
|
156
157
|
這一支 hook 的定義在互動式 Codex 工作階段裡透過 `/hooks` 被信任為止;信任紀錄綁在定義的
|
|
@@ -173,12 +174,42 @@ agent 的最後一則訊息,卻不給使用者的;要拿到使用者那一
|
|
|
173
174
|
|
|
174
175
|
Gemini CLI 已過時。Google 於 2026-06-18 對個人帳號停用 Gemini CLI,
|
|
175
176
|
改由 Antigravity CLI(`agy`)取代;企業帳號兩者都還能用。這個適配層為那些使用者保留,
|
|
176
|
-
但它從未在真實的 Gemini CLI 工作階段中驗證過;使用 Google
|
|
177
|
-
Antigravity CLI
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
但它從未在真實的 Gemini CLI 工作階段中驗證過;使用 Google 工具的新採用者應使用下面的
|
|
178
|
+
Antigravity CLI 適配層。
|
|
179
|
+
|
|
180
|
+
Antigravity CLI 已支援,依據是真實工作階段觀察到的契約(2026-09-29,agy 1.2.12),
|
|
181
|
+
而不只是它的文件。這份契約在關鍵之處與上表每一個適配層都不同:
|
|
182
|
+
|
|
183
|
+
- **設定**在 `.agents/hooks.json`,以 hook 名稱為鍵——
|
|
184
|
+
`{"<名稱>": {"Stop": [{"type":"command","command":"...","timeout":N}]}}`,
|
|
185
|
+
`timeout` 單位為秒。沒有 `hooks` 外層,handler 也不巢狀在 `hooks[]` 裡。
|
|
186
|
+
- **stdin 既沒有最後一則回覆、也沒有人的訊息**,只有 `transcriptPath` 與中繼資料,所以兩者
|
|
187
|
+
都要從逐字稿讀(JSONL,每筆有 `source`、`type`、`content`)。最後一則回覆是最後一筆
|
|
188
|
+
`source: MODEL`、`type: PLANNER_RESPONSE`。人的訊息是最後一筆
|
|
189
|
+
`source: USER_EXPLICIT`、`type: USER_INPUT`,取 `<USER_REQUEST>…</USER_REQUEST>` 之內的文字——
|
|
190
|
+
後面接著的系統區塊(`<ADDITIONAL_METADATA>` 等)不是人說的話。
|
|
191
|
+
- **`SYSTEM_MESSAGE` 紀錄絕不可當成人的訊息讀。** agy 會把這個 hook 自己的 `continue` 理由
|
|
192
|
+
寫回逐字稿,成為這種紀錄(`source: SYSTEM`、`type: SYSTEM_MESSAGE`,內容為
|
|
193
|
+
「Stop hook blocked termination: …」)。若把「不是模型的任何紀錄」都當成人,就會把 hook
|
|
194
|
+
自己的話當成人說的,R9 豁免隨之失效——也就是 R11 的失敗,換成這份逐字稿的形狀重演。
|
|
195
|
+
- **攔截是 `{"decision":"continue","reason":...}`**,不是 `block` 或 `deny`;`{}` 即放行。
|
|
196
|
+
- **hook 執行時的工作目錄是 `.agents/`,不是專案根目錄**(2026-09-29 實測,agy 1.2.12)。
|
|
197
|
+
因此安裝的指令是 `node ../scripts/hooks/check-turn-completion-agy.mjs`;以專案根目錄為準的
|
|
198
|
+
`node scripts/hooks/...` 會解析成 `<專案>/.agents/scripts/hooks/...`,出現
|
|
199
|
+
「Cannot find module」,而且**agy 對執行失敗的 hook 靜默放行**——沒有任何訊息、stdout 照常,
|
|
200
|
+
回合就這樣結束。路徑刻意用相對路徑(這個檔案本來就是要提交並共用的,絕對路徑只屬於某一台機器),
|
|
201
|
+
也不用任何 shell 語法(`sh -c`、`$(...)`),因為 agy 是否經過 shell 執行 `command` 沒有證據。
|
|
202
|
+
- **與 Claude Code 相反,hook 被呼叫時逐字稿已經寫到最後一則回覆。**
|
|
203
|
+
|
|
204
|
+
已驗證:agy 1.2.12、非互動的 `agy -p`、**單輪且沒有工具呼叫**——hook 被呼叫時最後一則回覆
|
|
205
|
+
已在逐字稿裡,`continue` 確實生效(模型又回了一輪),且沒有遇到信任提示(與 Codex 不同)。
|
|
206
|
+
**未驗證**:多輪對話、含工具呼叫的回合(此時最後一筆 `PLANNER_RESPONSE` 是不是最後回覆、
|
|
207
|
+
hook 執行時是否已寫入)、`fullyIdle: false`、`error` 非空、互動模式、專案層
|
|
208
|
+
`.agents/hooks.json` 是否像 `.agents/skills/` 一樣只對已登記的 Antigravity 專案生效,
|
|
209
|
+
以及工作目錄是否永遠是 `.agents/`(只對專案層檔案量測過;`uds init` 不會寫使用者層的
|
|
210
|
+
`~/.gemini/config/hooks.json`)。在未驗證的情境下,適配層可能判斷的是
|
|
211
|
+
較早的一則回覆而不是最後一則;讀取失敗時仍一律放行(R5)。`uds init --with-hooks` 會在安裝
|
|
212
|
+
那一行旁邊印出已驗證的範圍。
|
|
182
213
|
|
|
183
214
|
Cursor 已評估但不支援:截至撰寫本文時,Cursor 的 stop hook 能不能真的
|
|
184
215
|
擋下一個回合仍未確定,若對著一個沒人驗證過的契約出一份轉接層,
|
|
@@ -240,3 +271,5 @@ Cursor 已評估但不支援:截至撰寫本文時,Cursor 的 stop hook 能
|
|
|
240
271
|
- [ ] 歸屬詞的搜尋排除檢查自己的標題與結構
|
|
241
272
|
- [ ] 每個支援的執行環境的阻擋契約都對照該環境自己的官方文件驗證過,不是照抄另一個環境
|
|
242
273
|
- [ ] 安裝器只為採用者實際選擇的執行環境寫入該環境的 hook 設定
|
|
274
|
+
- [ ] 逐字稿裡會出現系統代寫訊息的執行環境,只從「人的紀錄」讀人的訊息,絕不從「不是模型寫的任何東西」讀
|
|
275
|
+
- [ ] 一份執行環境契約只對「真實工作階段觀察過的範圍」出貨,並註明沒觀察到的範圍
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UDS 速查表
|
|
2
2
|
|
|
3
|
-
> Quick reference for all UDS features | Last updated: 2026-09-
|
|
3
|
+
> Quick reference for all UDS features | Last updated: 2026-09-29
|
|
4
4
|
|
|
5
5
|
**Language**: [English](../../../docs/user/CHEATSHEET.md) | 繁體中文 | [简体中文](../../zh-CN/docs/CHEATSHEET.md)
|
|
6
6
|
|
|
@@ -352,8 +352,10 @@
|
|
|
352
352
|
| `check-docs-sync.sh` | Documentation Sync Checker |
|
|
353
353
|
| `check-error-exit.mjs` | 🔴 沒填就是沒設定,而沒設定會 exit 2, |
|
|
354
354
|
| `check-external-references.mjs` | External Reference Checker (SPEC-SELFDIAG-001 REQ- |
|
|
355
|
+
| `check-open-work-tracking.mjs` | Open-work-tracking reference checks for OWT-017 / |
|
|
355
356
|
| `check-orphan-specs.ps1` | Check Orphan Specs |
|
|
356
357
|
| `check-orphan-specs.sh` | Orphan Spec Detection Script |
|
|
358
|
+
| `check-prompt-footprint.mjs` | Prompt Footprint Ratchet — DEC-117 D2/L2 |
|
|
357
359
|
| `check-scope-sync.ps1` | Check Scope Sync |
|
|
358
360
|
| `check-scope-sync.sh` | Scope Consistency Check Script |
|
|
359
361
|
| `check-skill-next-steps-sync.ps1` | Check Skill Next Steps Sync |
|
|
@@ -367,6 +369,7 @@
|
|
|
367
369
|
| `check-translation-hash-ratchet.sh` | XSPEC-392 R6 棘輪:新的翻譯必須帶 source_hash,既有的欠債冷凍為基線。 |
|
|
368
370
|
| `check-translation-sync.ps1` | Check Translation Sync |
|
|
369
371
|
| `check-translation-sync.sh` | Translation Sync Checker |
|
|
372
|
+
| `check-upgrade-fidelity.sh` | Upgrade Fidelity Checker |
|
|
370
373
|
| `check-usage-docs-sync.ps1` | Check if usage documentation needs to be regenerat |
|
|
371
374
|
| `check-usage-docs-sync.sh` | check-usage-docs-sync.sh |
|
|
372
375
|
| `check-version-sync.ps1` | Check Version Sync |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../../docs/CLI-INIT-OPTIONS.md
|
|
3
|
-
source_version: 3.7.
|
|
4
|
-
translation_version: 3.7.
|
|
5
|
-
last_synced: 2026-09-
|
|
3
|
+
source_version: 3.7.1
|
|
4
|
+
translation_version: 3.7.1
|
|
5
|
+
last_synced: 2026-09-29
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -10,8 +10,8 @@ status: current
|
|
|
10
10
|
|
|
11
11
|
> **語言**: [English](../../../docs/CLI-INIT-OPTIONS.md) | 繁體中文 | [简体中文](../../zh-CN/docs/CLI-INIT-OPTIONS.md)
|
|
12
12
|
>
|
|
13
|
-
> **版本**: 3.7.
|
|
14
|
-
> **最後更新**: 2026-09-
|
|
13
|
+
> **版本**: 3.7.1
|
|
14
|
+
> **最後更新**: 2026-09-29
|
|
15
15
|
|
|
16
16
|
本文件詳細說明 `uds init` 命令的每一個選項,包含使用情境、影響範圍和建議選擇。
|
|
17
17
|
|
|
@@ -870,16 +870,17 @@ UDS 的專案——並在 `[pre-commit]` 底下回報同樣的修復方式;此
|
|
|
870
870
|
|
|
871
871
|
`--with-hooks` 一定會安裝進 `.claude/settings.json`。四個有 hook 支援的標準
|
|
872
872
|
之一——`turn-completion-integrity`(見 CHANGELOG,Unreleased)——也會裝進
|
|
873
|
-
**Codex
|
|
873
|
+
**Codex**、**Gemini CLI**(過時)與 **Antigravity CLI**(`agy`),門檻是你有沒有在 [AI 工具選擇](#1-ai-工具選擇)
|
|
874
874
|
裡選了那個工具(或用非互動模式的工具旗標帶入):
|
|
875
875
|
|
|
876
876
|
| 工具 | 寫入的設定檔 | 觸發條件 |
|
|
877
877
|
|------|-------------|---------|
|
|
878
878
|
| Codex | `.codex/hooks.json` | 選了 **OpenAI Codex** |
|
|
879
879
|
| Gemini CLI | `.gemini/settings.json` | 選了 **Gemini CLI** |
|
|
880
|
+
| Antigravity CLI | `.agents/hooks.json` | 選了 **Google Antigravity** |
|
|
880
881
|
|
|
881
|
-
沒選的工具不會寫入任何東西——`uds init` 不會在沒用到 Codex 或
|
|
882
|
-
的專案裡建立 `.codex/` 或 `.
|
|
882
|
+
沒選的工具不會寫入任何東西——`uds init` 不會在沒用到 Codex、Gemini CLI 或 Antigravity
|
|
883
|
+
的專案裡建立 `.codex/`、`.gemini/` 或 `.agents/` 目錄。其餘三個有 hook 支援的標準
|
|
883
884
|
(commit message 驗證、logging、security)目前仍只支援 Claude Code;
|
|
884
885
|
為什麼目前只推廣 turn-completion-integrity,以及 Cursor 的現況
|
|
885
886
|
(已評估、不支援),見
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# UDS 功能參考手冊
|
|
2
2
|
|
|
3
3
|
> Universal Development Standards - 完整功能文件
|
|
4
|
-
> Auto-generated | Last updated: 2026-09-
|
|
4
|
+
> Auto-generated | Last updated: 2026-09-29
|
|
5
5
|
|
|
6
6
|
**Language**: [English](../../../docs/reference/FEATURE-REFERENCE.md) | 繁體中文 | [简体中文](../../zh-CN/docs/FEATURE-REFERENCE.md)
|
|
7
7
|
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
4. [代理](#agents) (5)
|
|
16
16
|
5. [工作流程](#workflows) (5)
|
|
17
17
|
6. [核心規範](#core-standards) (153)
|
|
18
|
-
7. [腳本](#scripts) (
|
|
18
|
+
7. [腳本](#scripts) (62)
|
|
19
19
|
|
|
20
|
-
**Total Features:
|
|
20
|
+
**Total Features: 354**
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -478,7 +478,7 @@
|
|
|
478
478
|
| `deployment-standards` | 1.1.0 | This standard defines guidelines for safely deploying software to production, co |
|
|
479
479
|
| `deprecation-standards` | 1.1.0 | |
|
|
480
480
|
| `design-document-standards` | 1.0.0 | |
|
|
481
|
-
| `developer-memory` | 1.
|
|
481
|
+
| `developer-memory` | 1.2.0 | This standard defines a structured system for capturing, retrieving, and surfaci |
|
|
482
482
|
| `disaster-recovery-drill` | - | |
|
|
483
483
|
| `documentation-lifecycle` | 1.0.0 | This standard defines **when** to update documentation, **when** to check it, an |
|
|
484
484
|
| `documentation-structure` | 1.5.0 | This standard defines a consistent documentation structure for software projects |
|
|
@@ -516,7 +516,7 @@
|
|
|
516
516
|
| `mutation-testing` | 1.1.0 | Mutation testing evaluates test suite effectiveness by injecting artificial bugs |
|
|
517
517
|
| `no-cicd-deployment` | - | |
|
|
518
518
|
| `observability-standards` | 1.0.0 | |
|
|
519
|
-
| `open-work-tracking` | 1.
|
|
519
|
+
| `open-work-tracking` | 1.1.0 | The deferred-item-exit standard requires that a deferred item leave its document |
|
|
520
520
|
| `packaging-standards` | 1.1.0 | This standard defines a Recipe-based packaging framework that enables user proje |
|
|
521
521
|
| `performance-standards` | 1.2.0 | This standard defines comprehensive guidelines for software performance engineer |
|
|
522
522
|
| `pii-classification` | 1.1.0 | **Status**: Active | **Updated**: 2026-06-19 | |
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
| `timeout-standards` | - | |
|
|
575
575
|
| `token-budget` | - | |
|
|
576
576
|
| `translation-lifecycle-standards` | 1.0.1 | Translation lifecycle standards: MISSING vs OUTDATED distinction, semver-aware s |
|
|
577
|
-
| `turn-completion-integrity` | 1.
|
|
577
|
+
| `turn-completion-integrity` | 1.5.0 | An agent writes *"I'll do X next"* and then ends the turn without doing X. |
|
|
578
578
|
| `user-journey-testing` | - | |
|
|
579
579
|
| `user-story-mapping` | 1.0.0 | **Status**: Active | **Updated**: 2026-06-17 | |
|
|
580
580
|
| `verification-evidence` | 1.3.0 | Establish an "Iron Law" that no task can be claimed as complete without verifica |
|
|
@@ -610,8 +610,10 @@
|
|
|
610
610
|
| `check-docs-sync.sh` | Documentation Sync Checker |
|
|
611
611
|
| `check-error-exit.mjs` | 🔴 沒填就是沒設定,而沒設定會 exit 2, |
|
|
612
612
|
| `check-external-references.mjs` | External Reference Checker (SPEC-SELFDIAG-001 REQ-5, AC-7) |
|
|
613
|
+
| `check-open-work-tracking.mjs` | Open-work-tracking reference checks for OWT-017 / OWT-018 / OWT-019. |
|
|
613
614
|
| `check-orphan-specs.ps1` | Check Orphan Specs |
|
|
614
615
|
| `check-orphan-specs.sh` | Orphan Spec Detection Script |
|
|
616
|
+
| `check-prompt-footprint.mjs` | Prompt Footprint Ratchet — DEC-117 D2/L2 |
|
|
615
617
|
| `check-scope-sync.ps1` | Check Scope Sync |
|
|
616
618
|
| `check-scope-sync.sh` | Scope Consistency Check Script |
|
|
617
619
|
| `check-skill-next-steps-sync.ps1` | Check Skill Next Steps Sync |
|
|
@@ -625,6 +627,7 @@
|
|
|
625
627
|
| `check-translation-hash-ratchet.sh` | XSPEC-392 R6 棘輪:新的翻譯必須帶 source_hash,既有的欠債冷凍為基線。 |
|
|
626
628
|
| `check-translation-sync.ps1` | Check Translation Sync |
|
|
627
629
|
| `check-translation-sync.sh` | Translation Sync Checker |
|
|
630
|
+
| `check-upgrade-fidelity.sh` | Upgrade Fidelity Checker |
|
|
628
631
|
| `check-usage-docs-sync.ps1` | Check if usage documentation needs to be regenerated |
|
|
629
632
|
| `check-usage-docs-sync.sh` | check-usage-docs-sync.sh |
|
|
630
633
|
| `check-version-sync.ps1` | Check Version Sync |
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -281,8 +281,8 @@ export async function initCommand(options) {
|
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
// turn-completion-integrity is also extended to Codex
|
|
285
|
-
// (2026-09-25), each with its own config file and output contract — see
|
|
284
|
+
// turn-completion-integrity is also extended to Codex, Gemini CLI and
|
|
285
|
+
// Antigravity CLI (2026-09-25; agy 2026-09-29), each with its own config file and output contract — see
|
|
286
286
|
// installCodexHooks/installGeminiHooks in hooks-installer.js. Gated on the
|
|
287
287
|
// tools the adopter actually selected: writing a hooks.json into every
|
|
288
288
|
// project's .codex/ regardless of whether Codex is used would be noise,
|
|
@@ -311,6 +311,19 @@ export async function initCommand(options) {
|
|
|
311
311
|
console.log(chalk.yellow(` ⚠ Gemini CLI hook not installed — ${geminiResult.reason}`));
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
|
+
if (selectedTools.includes('antigravity')) {
|
|
315
|
+
const { installAgyHooks } = await import('../installers/hooks-installer.js');
|
|
316
|
+
const agyResult = installAgyHooks(projectPath);
|
|
317
|
+
if (agyResult.installed) {
|
|
318
|
+
console.log(chalk.green(' ✓ Antigravity CLI Stop hook installed (turn-completion-integrity; .agents/hooks.json)'));
|
|
319
|
+
// Verified 2026-09-29 (agy 1.2.12): single turn, no tool calls, `agy -p`.
|
|
320
|
+
// Multi-turn, tool-call turns and interactive mode are not verified —
|
|
321
|
+
// say so here, where the adopter believes it now works everywhere.
|
|
322
|
+
console.log(chalk.yellow(' ⚠ Verified against a real agy session for a single turn without tool calls in `agy -p` mode only; multi-turn, tool-call turns and interactive mode are not yet verified.'));
|
|
323
|
+
} else {
|
|
324
|
+
console.log(chalk.yellow(` ⚠ Antigravity CLI hook not installed — ${agyResult.reason}`));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
314
327
|
}
|
|
315
328
|
|
|
316
329
|
// 5. Setup Pre-commit Hook
|
|
@@ -56,7 +56,7 @@ export async function uninstallCommand(options) {
|
|
|
56
56
|
const categories = await checkbox({
|
|
57
57
|
message: msg.selectCategories,
|
|
58
58
|
choices: [
|
|
59
|
-
{ name: `${msg.categoryHooks} (.husky/pre-commit, .claude/settings.json, .codex/hooks.json, .gemini/settings.json)`, value: 'hooks', checked: true },
|
|
59
|
+
{ name: `${msg.categoryHooks} (.husky/pre-commit, .claude/settings.json, .codex/hooks.json, .gemini/settings.json, .agents/hooks.json)`, value: 'hooks', checked: true },
|
|
60
60
|
{ name: `${msg.categorySkills} (skills, commands)`, value: 'skills', checked: true },
|
|
61
61
|
{ name: `${msg.categoryIntegrations} (CLAUDE.md, .cursorrules, ...)`, value: 'integrations', checked: true },
|
|
62
62
|
{ name: `${msg.categoryStandards} (.standards/)`, value: 'standards', checked: true }
|
package/src/commands/update.js
CHANGED
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
resolveContentModeForTool,
|
|
16
16
|
generateIntegrationContent,
|
|
17
17
|
extractMarkedContent,
|
|
18
|
-
buildToolIntegrationConfig
|
|
18
|
+
buildToolIntegrationConfig,
|
|
19
|
+
resolveIntegrationLanguage
|
|
19
20
|
} from '../utils/integration-generator.js';
|
|
20
21
|
import {
|
|
21
22
|
calculateCategoriesFromStandards,
|
|
@@ -782,13 +783,20 @@ export async function updateCommand(options) {
|
|
|
782
783
|
// basename() removes both. See resolveStandardFilename.
|
|
783
784
|
const installedStandardsList = manifest.standards || [];
|
|
784
785
|
|
|
785
|
-
// Determine language setting
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
786
|
+
// Determine language setting.
|
|
787
|
+
//
|
|
788
|
+
// Content language comes from `display_language`, falling back to
|
|
789
|
+
// `output_language`/`commit_language` only when there is no display
|
|
790
|
+
// setting to go on — see resolveIntegrationLanguage's docblock. This used
|
|
791
|
+
// to derive `commonLanguage` from output_language/commit_language alone,
|
|
792
|
+
// which is the commit-message language, not the display language: a
|
|
793
|
+
// project installed with `--locale zh-tw` and `output_language: bilingual`
|
|
794
|
+
// got the correct Chinese heading from `init`/the reconciler and an
|
|
795
|
+
// English one from the next plain `uds update`, silently overwriting the
|
|
796
|
+
// block that was already there. `buildToolIntegrationConfig` picked up
|
|
797
|
+
// the fix on 2026-09-16 (XSPEC-343 R2 follow-up); this call site — the
|
|
798
|
+
// plain `uds update` main flow — did not.
|
|
799
|
+
const commonLanguage = resolveIntegrationLanguage(manifest);
|
|
792
800
|
|
|
793
801
|
// Track generated files to handle AGENTS.md sharing
|
|
794
802
|
const generatedFiles = new Set();
|
|
@@ -1982,10 +1990,13 @@ async function updateIntegrationsOnly(projectPath, manifest, options = {}) {
|
|
|
1982
1990
|
const next = generateIntegrationContent({
|
|
1983
1991
|
tool,
|
|
1984
1992
|
categories: ['anti-hallucination', 'commit-standards', 'code-review'],
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1993
|
+
// See resolveIntegrationLanguage's docblock: content language comes
|
|
1994
|
+
// from `display_language`, not `output_language`/`commit_language`.
|
|
1995
|
+
// This inline derivation ignored `display_language` entirely, so
|
|
1996
|
+
// `--plan` reported "would change" (or "unchanged" against an
|
|
1997
|
+
// already-wrong file) using the wrong language for any project with
|
|
1998
|
+
// display_language != output_language (e.g. zh-tw + bilingual).
|
|
1999
|
+
language: resolveIntegrationLanguage(manifest),
|
|
1989
2000
|
// Passed raw. `basename()` here destroyed the two things the
|
|
1990
2001
|
// generator needs: a registry ID cannot be turned back into a
|
|
1991
2002
|
// filename once it has been through basename() (it comes out
|
|
@@ -220,8 +220,9 @@ export function installHooks(projectPath) {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
|
-
* turn-completion-integrity is the only standard extended to Codex
|
|
224
|
-
* CLI so far (2026-09-25). Unlike
|
|
223
|
+
* turn-completion-integrity is the only standard extended to Codex, Gemini
|
|
224
|
+
* CLI and Antigravity CLI so far (2026-09-25; agy 2026-09-29). Unlike
|
|
225
|
+
* installHooks() above, these three functions do
|
|
225
226
|
* NOT walk every standard's `enforcement:` block — the other three shipped
|
|
226
227
|
* standards declare Claude-Code-specific events (PreToolUse/PostToolUse with
|
|
227
228
|
* a Bash/Write matcher) that Codex and Gemini CLI's hook models don't obviously
|
|
@@ -237,6 +238,25 @@ export function installHooks(projectPath) {
|
|
|
237
238
|
// an adopter's own hook could just as easily live under.
|
|
238
239
|
export const CODEX_HOOK_SCRIPT = 'check-turn-completion-codex.mjs';
|
|
239
240
|
export const GEMINI_HOOK_SCRIPT = 'check-turn-completion-gemini.mjs';
|
|
241
|
+
export const AGY_HOOK_SCRIPT = 'check-turn-completion-agy.mjs';
|
|
242
|
+
// agy's hooks.json is keyed by hook NAME, and this is the one UDS owns. The
|
|
243
|
+
// uninstaller removes only handlers under this key that also run a script UDS
|
|
244
|
+
// ships, so a user's own hook (under any name, or even under this name with a
|
|
245
|
+
// different command) is never touched.
|
|
246
|
+
export const AGY_HOOK_NAME = 'uds-turn-completion-integrity';
|
|
247
|
+
// 🔴 agy runs a hook with the working directory set to `.agents/` (measured
|
|
248
|
+
// 2026-09-29 on agy 1.2.12, PC15: the project-root-relative command
|
|
249
|
+
// `node scripts/hooks/...` failed with "Cannot find module
|
|
250
|
+
// '<project>/.agents/scripts/hooks/...'", and agy lets a failed hook through
|
|
251
|
+
// silently). The command therefore climbs out of `.agents/` first. It is
|
|
252
|
+
// deliberately relative, not absolute: hooks.json is meant to be committed and
|
|
253
|
+
// shared, and an absolute path is one machine's. It is also deliberately not
|
|
254
|
+
// `sh -c` / `$(git rev-parse ...)`: whether agy runs `command` through a shell
|
|
255
|
+
// has no evidence behind it.
|
|
256
|
+
export const AGY_HOOK_COMMAND = `node ../scripts/hooks/${AGY_HOOK_SCRIPT}`;
|
|
257
|
+
// The first form this installer wrote (never released); recognised only so a
|
|
258
|
+
// re-install repairs it and uninstall still removes it.
|
|
259
|
+
export const AGY_HOOK_COMMAND_LEGACY = `node scripts/hooks/${AGY_HOOK_SCRIPT}`;
|
|
240
260
|
|
|
241
261
|
/** Copy the shared hook scripts into the project, same as installHooks() does. */
|
|
242
262
|
function copyHookScripts(hookDir, hooksDir) {
|
|
@@ -326,3 +346,70 @@ export function installGeminiHooks(projectPath) {
|
|
|
326
346
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
327
347
|
return { installed: true, settingsPath, event: 'AfterAgent' };
|
|
328
348
|
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Install the turn-completion-integrity Stop hook for Antigravity CLI (agy).
|
|
352
|
+
*
|
|
353
|
+
* Config lives at <project>/.agents/hooks.json. Its shape differs from every
|
|
354
|
+
* other adapter's file: a top-level map of hook NAME -> { <Event>: handler[] },
|
|
355
|
+
* with the handler written flat (`{type, command, timeout}`), no `hooks`
|
|
356
|
+
* wrapper key and no `hooks[]` nesting (https://antigravity.google/docs/hooks/,
|
|
357
|
+
* fetched 2026-09-29; the shape was also confirmed by a real agy 1.2.12 run).
|
|
358
|
+
* So neither mergeHookArray nor the uninstaller's Claude-shaped stripping can
|
|
359
|
+
* be reused here.
|
|
360
|
+
*
|
|
361
|
+
* - The command is `node ../scripts/hooks/...`, not `node scripts/hooks/...`:
|
|
362
|
+
* agy's hook working directory is `.agents/` (see AGY_HOOK_COMMAND).
|
|
363
|
+
* - `timeout` is seconds (agy's default is 30), like Codex, unlike Gemini CLI.
|
|
364
|
+
* - `enabled` is deliberately NOT written: an `enabled: false` there is a
|
|
365
|
+
* silent off switch, and omitting it is the documented default.
|
|
366
|
+
* - A hooks.json that exists but cannot be parsed is NOT overwritten (the
|
|
367
|
+
* other installers fall back to `{}` and would discard the adopter's file);
|
|
368
|
+
* the install reports why and leaves the file alone.
|
|
369
|
+
* - Whether agy runs a project `.agents/hooks.json` only for a registered
|
|
370
|
+
* Antigravity project (as it does for `.agents/skills/`) is not verified.
|
|
371
|
+
*
|
|
372
|
+
* @param {string} projectPath
|
|
373
|
+
* @returns {{ installed: boolean, settingsPath: string, event?: string, reason?: string }}
|
|
374
|
+
*/
|
|
375
|
+
export function installAgyHooks(projectPath) {
|
|
376
|
+
const hooksJsonPath = join(projectPath, '.agents', 'hooks.json');
|
|
377
|
+
const hookDir = hooksSourceDir();
|
|
378
|
+
|
|
379
|
+
if (!hookDir || !existsSync(join(hookDir, AGY_HOOK_SCRIPT))) {
|
|
380
|
+
return { installed: false, settingsPath: hooksJsonPath, reason: `hook script not found: ${AGY_HOOK_SCRIPT}` };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let config = {};
|
|
384
|
+
if (existsSync(hooksJsonPath)) {
|
|
385
|
+
try {
|
|
386
|
+
config = JSON.parse(readFileSync(hooksJsonPath, 'utf-8'));
|
|
387
|
+
} catch {
|
|
388
|
+
return { installed: false, settingsPath: hooksJsonPath, reason: '.agents/hooks.json exists but is not valid JSON; left untouched' };
|
|
389
|
+
}
|
|
390
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
391
|
+
return { installed: false, settingsPath: hooksJsonPath, reason: '.agents/hooks.json is not a JSON object; left untouched' };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const agentsDir = join(projectPath, '.agents');
|
|
396
|
+
if (!existsSync(agentsDir)) mkdirSync(agentsDir, { recursive: true });
|
|
397
|
+
copyHookScripts(hookDir, join(projectPath, 'scripts', 'hooks'));
|
|
398
|
+
|
|
399
|
+
const command = AGY_HOOK_COMMAND;
|
|
400
|
+
const entry = config[AGY_HOOK_NAME];
|
|
401
|
+
const mine = entry && typeof entry === 'object' && !Array.isArray(entry) ? entry : {};
|
|
402
|
+
// A UDS handler written by an earlier install with the pre-fix command
|
|
403
|
+
// (`node scripts/hooks/...`, which does not resolve from agy's cwd) is
|
|
404
|
+
// replaced, not left beside the new one. Only that exact stale command is
|
|
405
|
+
// touched; anything else the adopter put under this name stays.
|
|
406
|
+
const stop = (Array.isArray(mine.Stop) ? mine.Stop : [])
|
|
407
|
+
.filter((h) => !(h && h.command === AGY_HOOK_COMMAND_LEGACY));
|
|
408
|
+
if (!stop.some((h) => h && h.command === command)) {
|
|
409
|
+
stop.push({ type: 'command', command, timeout: 30 });
|
|
410
|
+
}
|
|
411
|
+
config[AGY_HOOK_NAME] = { ...mine, Stop: stop };
|
|
412
|
+
|
|
413
|
+
writeFileSync(hooksJsonPath, JSON.stringify(config, null, 2) + '\n');
|
|
414
|
+
return { installed: true, settingsPath: hooksJsonPath, event: 'Stop' };
|
|
415
|
+
}
|