universal-dev-standards 6.12.0 → 6.13.0-beta.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.
@@ -15,7 +15,7 @@ status: current
15
15
 
16
16
  > **语言**: [English](../../README.md) | [繁體中文](../zh-TW/README.md) | 简体中文
17
17
 
18
- **版本**: 6.12.0 | **发布日期**: 2026-09-25 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
18
+ **版本**: 6.13.0-beta.2 (Pre-release) | **发布日期**: 2026-09-26 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
19
19
 
20
20
  语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
21
21
 
@@ -13,6 +13,7 @@ status: current
13
13
  <!-- UDS_SUPPORTED_VERSIONS_START -->
14
14
  | 版本 | 支持状态 |
15
15
  |------|--------|
16
+ | 6.13.0-beta.2 | ✅ 预发布版本 |
16
17
  | 6.12.0 | ✅ 最新正式版 |
17
18
  | < 6.0.0 | ❌ 已终止支持 |
18
19
  <!-- UDS_SUPPORTED_VERSIONS_END -->
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  source: ../../../core/turn-completion-integrity.md
3
- source_version: 1.3.0
4
- translation_version: 1.3.0
5
- last_synced: 2026-09-08
6
- source_hash: c396baa28423
3
+ source_version: 1.4.0
4
+ translation_version: 1.4.0
5
+ last_synced: 2026-09-26
6
+ source_hash: 401b74843abc
7
7
  status: current
8
8
  ---
9
9
 
@@ -11,8 +11,8 @@ status: current
11
11
 
12
12
  > **语言**: [English](../../../core/turn-completion-integrity.md) | [繁體中文](../../zh-TW/core/turn-completion-integrity.md) | 简体中文
13
13
 
14
- **版本**: 1.3.0
15
- **最后更新**: 2026-09-08
14
+ **版本**: 1.4.0
15
+ **最后更新**: 2026-09-25
16
16
  **适用范围**: 任何由 agent 结束回合、把控制权交还给人的执行环境
17
17
  **Scope**: universal
18
18
  **行业标准**: 不声称任何来源——由实际观察到的失败归纳,见「证据」
@@ -141,6 +141,41 @@ agent 写下「我接着做 X」,然后结束回合,而 X 没有做。
141
141
 
142
142
  ---
143
143
 
144
+ ## 支持的执行环境
145
+
146
+ 这个检查只在「适配层存在,且 hook 真的被接入该执行环境自己的配置」时才生效。
147
+ 截至 v1.4.0:
148
+
149
+ | 执行环境 | 事件 | 配置文件 | 拦截契约 |
150
+ |---|---|---|---|
151
+ | Claude Code | Stop | `.claude/settings.json` | stdout 输出 `{"decision":"block","reason":...}`,exit 0;沉默即放行 |
152
+ | Codex | Stop | `.codex/hooks.json` | stdout 输出 `{"decision":"block","reason":...}`,exit 0——官方文档写明这个事件纯文本或空输出无效 |
153
+ | Gemini CLI | AfterAgent | `.gemini/settings.json` | stdout 输出 `{"decision":"deny","reason":...}`,exit 0——官方文档标记为优先于 exit code 2 的做法 |
154
+
155
+ Codex 的 R9 豁免是尽力而为,不是静默失效:Codex 的 Stop payload 直接给出
156
+ agent 的最后一条消息,却不给出用户的;要拿到用户那一侧必须解析一份
157
+ 对话记录文件。已对真实 codex-cli 0.156.1 安装坐实(2026-09-26):
158
+ `~/.codex/sessions/**/*.jsonl` 里的一条用户消息长这样——
159
+ `{"type":"response_item","payload":{"type":"message","role":"user",
160
+ "content":[{"type":"input_text","text":...}]}}`——消息位于 `payload`
161
+ 之下,不在该行最外层、也不在 `message` 键下;同一种形状但
162
+ `role: "developer"` 的记录不算用户消息。6.13.0-beta.1 的适配层尝试过的
163
+ 两种形状都不是这个真实形状,所以 R9 在 Codex 上从未真正豁免过任何一轮;
164
+ 6.13.0-beta.2 已修复。解析失败(或遇到无法识别的记录形状)时仍只是让
165
+ 用户那一侧变空、不会抛出异常——检测仍照样运行在 agent 消息上,只有那一轮
166
+ 的 R9 豁免可能漏掉。
167
+
168
+ Cursor 已评估但不支持:截至撰写本文时,Cursor 的 stop hook 能不能真的
169
+ 拦下一个回合仍未确定,若对着一个没人验证过的契约交付一份适配层,
170
+ 等于重演 R3 要防的那个失败——一个没人确认过真的在执法的执法机制。
171
+
172
+ 不在上表的任何执行环境,这个检查都是失效的——与语言不支持(R8)同一种
173
+ 「默认沉默」失败。`uds init --with-hooks` 会报告它把 hook 接入了哪些
174
+ 执行环境;它不会在这里穷举其余的,因为那份清单是一张等着在下一个
175
+ 执行环境被加入或移除时就过期的引用。
176
+
177
+ ---
178
+
144
179
  ## 检测器匹配什么
145
180
 
146
181
  形状是:**第一人称将来标记,接一个动作动词,在同一个句子里**,再减掉三个排除项。
@@ -188,3 +223,5 @@ agent 写下「我接着做 X」,然后结束回合,而 X 没有做。
188
223
  - [ ] 检查认得出自己的拦截消息,不把它读成人说的话
189
224
  - [ ] 检查认得出 R2 定义的逐项阻塞点结束,而且不拦它
190
225
  - [ ] 归属词的搜索排除检查自己的标题与结构
226
+ - [ ] 每个支持的执行环境的拦截契约都对照该环境自己的官方文档验证过,不是照搬另一个环境
227
+ - [ ] 安装器只为采用者实际选择的执行环境写入该环境的 hook 配置
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  source: ../../../docs/CLI-INIT-OPTIONS.md
3
- source_version: 3.5.2
4
- translation_version: 3.5.2
5
- last_synced: 2026-09-18
3
+ source_version: 3.6.0
4
+ translation_version: 3.6.0
5
+ last_synced: 2026-09-25
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-TW/docs/CLI-INIT-OPTIONS.md) | 简体中文
12
12
  >
13
- > **版本**: 3.5.2
14
- > **最后更新**: 2026-09-18
13
+ > **版本**: 3.6.0
14
+ > **最后更新**: 2026-09-25
15
15
 
16
16
  本文档详细说明 `uds init` 命令的每一个选项,包含使用情境、影响范围和建议选择。
17
17
 
@@ -838,6 +838,25 @@ uds init --experimental
838
838
  | Claude Code 目标文件 | `--claude-target` | Claude Code 集成内容要写到哪里:`project`(`CLAUDE.md`,默认)或 `local`(`CLAUDE.local.md`) |
839
839
  | 模式(已弃用) | `-m, --mode` | 安装模式(skills, full)- 请改用 `--skills-location` |
840
840
 
841
+ ### Claude Code 以外的强制执行 Hooks
842
+
843
+ `--with-hooks` 一定会安装进 `.claude/settings.json`。四个有 hook 支持的标准
844
+ 之一——`turn-completion-integrity`(见 CHANGELOG,Unreleased)——也会装进
845
+ **Codex** 与 **Gemini CLI**,门槛是你有没有在 [AI 工具选择](#1-ai-工具选择)
846
+ 里选了那个工具(或用非交互模式的工具标志带入):
847
+
848
+ | 工具 | 写入的配置文件 | 触发条件 |
849
+ |------|---------------|---------|
850
+ | Codex | `.codex/hooks.json` | 选了 **OpenAI Codex** |
851
+ | Gemini CLI | `.gemini/settings.json` | 选了 **Gemini CLI** |
852
+
853
+ 没选的工具不会写入任何东西——`uds init` 不会在没用到 Codex 或 Gemini CLI
854
+ 的项目里创建 `.codex/` 或 `.gemini/` 目录。其余三个有 hook 支持的标准
855
+ (commit message 校验、logging、security)目前仍只支持 Claude Code;
856
+ 为什么目前只推广 turn-completion-integrity,以及 Cursor 的现状
857
+ (已评估、不支持),见
858
+ [支持的执行环境](../../../core/turn-completion-integrity.md#supported-harnesses)。
859
+
841
860
  ### Claude Code 集成目标文件(`--claude-target`)
842
861
 
843
862
  UDS 默认把 Claude Code 内容写进 `CLAUDE.md`——团队共用、会进版本控制的那个文件。
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  source: ../../CHANGELOG.md
3
- source_version: 6.12.0
4
- translation_version: 6.12.0
5
- last_synced: 2026-09-24
3
+ source_version: 6.13.0-beta.2
4
+ translation_version: 6.13.0-beta.2
5
+ last_synced: 2026-09-26
6
6
  status: current
7
7
  ---
8
8
 
@@ -17,6 +17,30 @@ status: current
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [6.13.0-beta.2] - 2026-09-26
21
+
22
+ > **測試版** — 以 `npm install -g universal-dev-standards@beta` 安裝。要測什麼、已知限制、如何退回正式版:見 [docs/PRE-RELEASE.md](../../docs/PRE-RELEASE.md)。
23
+
24
+ ### 修正
25
+
26
+ - **`uds uninstall` 從未移除 `installHooks()`/`installCodexHooks()`/`installGeminiHooks()` 寫入的關卡——不只是 Codex 與 Gemini CLI(6.13.0-beta.1 記載的已知限制),Claude Code 自己的 `.claude/settings.json` 也有一模一樣的缺口,而且從未被記錄過。** `hooks` 這個 uninstall 分類原本只處理 `.husky/pre-commit` 與 `.git/hooks/pre-commit`;三支安裝函式實際寫入的設定檔完全沒有任何 uninstaller 在管,導致每一個關卡在 `uds uninstall` 之後仍持續執行。新增的 `uninstallClaudeCodeHooks`/`uninstallCodexHooks`/`uninstallGeminiHooks`(`src/uninstallers/hook-uninstaller.js`)現在會精準移除 `.claude/settings.json`、`.codex/hooks.json`、`.gemini/settings.json` 裡 UDS 安裝的項目——辨識依據是指令路徑**加上**一份 UDS 目前確實有出貨的腳本檔名清單,不是只看路徑,這樣使用者自己放進 UDS 同一個 `scripts/hooks/` 目錄底下的 hook 就不會被誤刪。移除後變空的事件陣列會一併從設定裡移除;設定檔若因此變成完全空的物件(代表整份都是 UDS 寫入的)就直接刪除檔案,否則保留檔案並寫回其餘內容。JSON 格式損壞時回報錯誤並保持原樣,不會覆寫。已接入 `uds uninstall` 既有的 `hooks` 分類、`--dry-run` 預覽,以及互動選單裡該分類的說明文字。
27
+ - **Codex 轉接層的 R9 豁免(「使用者叫停就放行」)在真實 Codex 安裝上從未真的生效過——`scripts/hooks/check-turn-completion-codex.mjs` 的 `bestEffortLastUserMessage()` 試過的兩種紀錄形狀都讀不到欄位。** 已對真實 codex-cli 0.156.1 的 `~/.codex/sessions/**/*.jsonl` 逐字稿坐實:一則使用者訊息紀錄長這樣——`{"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":...}]}}`——訊息住在 `payload` 底下,不在該筆紀錄最外層、也不在 `message` 鍵底下,所以那個欄位一直被讀成不存在,R9 從未真的豁免過任何一輪 Codex 回合。現在優先讀 `payload.type === "message"`(其餘紀錄型別若剛好用到原本那兩種形狀仍保留為後備),一併支援 `input_text` 內容項目(與既有的 `text` 形狀並存),且不把 `role: "developer"` 的紀錄當成使用者訊息。`core/turn-completion-integrity.md`「支援的執行環境」一節與 `docs/PRE-RELEASE.md` 已從「未對照真實安裝驗證過」更新為已坐實的真實形狀。
28
+ - **R9 的 zh-TW 叫停偵測漏掉「要離開、稍後再續」這一族——已實測:「我要出門了,等我回來再繼續」在 2026-09-25 真的誤擋了一次 Claude Code 的回合完成關卡。** 這句與「暫停,我要出門」都沒有被既有的任何一支 `STOP_REQUEST` 樣式接住。新增兩支窄樣式:離開類詞(出門/離開一下/先走)必須跟「稍後再續」類詞(等我回來/回來再/明天再/晚點再/待會再)或裸的「暫停」同時出現在同一小段裡——單獨的離開詞(例如「出門前把這三件做完」,這是要求離開前做完,不是叫停)語料仍必須判 false。順手查了 en 語料包有沒有一樣的缺口,發現 `\bI'?m (heading|going) (home|out)\b` 是永遠打不中的死碼:`isStopRequest()` 一律先呼叫 `normalize()`,會把 "I'm" 改寫成 "I am",只認縮寫形的樣式因此永遠配不到;已修成 `I(?:'m| am) (heading|going) (home|out)`,坐實裸的 "I'm heading out." 修前為 false、修後為 true。
29
+
30
+ ## [6.13.0-beta.1] - 2026-09-26
31
+
32
+ > **測試版** — 以 `npm install -g universal-dev-standards@beta` 安裝。要測什麼、已知限制、如何退回正式版:見 [docs/PRE-RELEASE.md](../../docs/PRE-RELEASE.md)。已知限制:`uds uninstall` 尚不會移除 Codex/Gemini CLI 設定裡的關卡。
33
+
34
+ ### 新增
35
+
36
+ - **`turn-completion-integrity` 1.4.0 把 Stop 關卡推廣到 Codex 與 Gemini CLI,與既有的 Claude Code 轉接層並存。** 判斷邏輯(語料包、冷卻、滾動視窗、自我回音標記)抽到共用的 `turn-completion/engine.mjs`,三支工具腳本各自只轉譯自己工具的契約,不再各帶一份判斷邏輯。Codex(`.codex/hooks.json`,Stop 事件)直接從 stdin 讀 `last_assistant_message`,在 exit 0 時於 stdout 印 `{"decision":"block","reason":...}` 來擋下——官方文件寫明這個事件純文字或空輸出無效,這點與 Claude Code 的「沉默即放行」不同;使用者的最後一則訊息用 `transcript_path` 盡力讀取,因為其確切格式未對照真實安裝驗證過,所以 R9(豁免使用者主動喊停的回合)在 Codex 上是文件記載的已知落差,不是靜默失效。Gemini CLI(`.gemini/settings.json`,`AfterAgent` 事件)的 stdin 直接給 `prompt` 與 `prompt_response`,完全不需要解析逐字稿,擋下方式是 `{"decision":"deny","reason":...}`——官方文件標記為優先於 exit code 2 的做法。`uds init --with-hooks` 現在也會呼叫 `installCodexHooks`/`installGeminiHooks`,門檻是採用者有沒有選那個工具,沒用到 Codex 或 Gemini CLI 的專案不會被寫入任何東西。Cursor 已評估,明確標記為不支援(Cursor 的 stop hook 能不能真的擋下一個回合仍未確定)。見[支援的執行環境](../../core/turn-completion-integrity.md#supported-harnesses)與 [CLI-INIT-OPTIONS.md](../../docs/CLI-INIT-OPTIONS.md#claude-code-以外的強制執行-hooks)。
37
+
38
+ - **`developer-memory` 1.2.0:新增 `code-reference` 過期查核——記憶引用的檔案路徑或符號一旦搬走或不存在,浮出前就會被標記,不再被悄悄沿用。** 沿用 `knowledge-graph-memory` 1.0.0 已定義的雙模式(§2),不另外發明第三種:降級模式(沒有圖引擎——AI 自己用 Glob/Grep/Read 確認引用還在,與既有的記憶驗證原則同一套機制)與引擎模式(有圖引擎時,例如 EngramGraph 的 `egr refs check`,回報每個引用的狀態:`present`/`moved`(附新位置)/`missing`/`unresolvable`)。`unresolvable` 一律不得當成 `present` 或 `missing`——它代表查核器無法判斷,不是引用沒事或已消失。時機掛在既有的 `proactive-surfacing` 規則(§4.1),查核在記憶浮出**之前**進行,不是之後。第一批只涵蓋檔案路徑與符號名稱(函式/類別);`file:line` 明確排除在外——行號會隨任何不相關的編輯漂移,屬於不同種類的過期(見 DEC-115 OQ-1,2027-01-31 前重新評估)。`core/developer-memory.md` §11 加入一段非規範性的 Claude Code `SessionStart` hook 範例;其他工具則改走各自 repo 的說明檔(CLAUDE.md/AGENTS.md/.cursorrules 等)。(DEC-115-L1)
39
+
40
+ ### 修正
41
+
42
+ - **`turn-completion-integrity` 的 zh-TW 與 en 偵測器,會在前提是使用者自己決定的條件式承諾上誤擋。** 「你選定後,我會把這一輪的發想寫成正式決策紀錄…」被判成未兌現的承諾——既有的豁免只涵蓋「要求資訊」與「要求回報」,沒有涵蓋「前提是使用者的決定」這種條件句。zh-TW 新增一支窄樣式:你 + 短決定動詞(選定/選好/決定/確認/回覆/點頭)+ 後 + 逗號 + 我;en 新增以文法為準(不是動詞清單,延續本包既有設計)的 `(once|after|as soon as) you ..., I` 樣式。兩份語料都補了成對的反例(主詞不是你/you,或只有一半的形狀),證明收窄沒有連帶漏擋真的未兌現承諾;en 版本也記下一個刻意留下未解的已知限制(前提與 "I" 之間沒有逗號時仍會誤擋——放寬會漏擋真的未兌現承諾)。
43
+
20
44
  ## [6.12.0] - 2026-09-25
21
45
 
22
46
  ### 新增
@@ -15,7 +15,7 @@ status: current
15
15
 
16
16
  > **語言**: [English](../../README.md) | 繁體中文 | [简体中文](../zh-CN/README.md)
17
17
 
18
- **版本**: 6.12.0 | **發布日期**: 2026-09-25 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
18
+ **版本**: 6.13.0-beta.2 (Pre-release) | **發布日期**: 2026-09-26 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
19
19
 
20
20
  語言無關、框架無關的軟體專案文件標準。透過 AI 原生工作流,確保不同技術堆疊之間的一致性、品質和可維護性。
21
21
 
@@ -13,6 +13,7 @@ status: current
13
13
  <!-- UDS_SUPPORTED_VERSIONS_START -->
14
14
  | 版本 | 支援狀態 |
15
15
  |------|--------|
16
+ | 6.13.0-beta.2 | ✅ 預發布版本 |
16
17
  | 6.12.0 | ✅ 最新正式版 |
17
18
  | < 6.0.0 | ❌ 已終止支援 |
18
19
  <!-- UDS_SUPPORTED_VERSIONS_END -->
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  source: ../../../core/turn-completion-integrity.md
3
- source_version: 1.3.0
4
- translation_version: 1.3.0
5
- last_synced: 2026-09-08
6
- source_hash: c396baa28423
3
+ source_version: 1.4.0
4
+ translation_version: 1.4.0
5
+ last_synced: 2026-09-26
6
+ source_hash: 401b74843abc
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.3.0
15
- **最後更新**: 2026-09-08
14
+ **版本**: 1.4.0
15
+ **最後更新**: 2026-09-25
16
16
  **適用範圍**: 任何由 agent 結束回合、把控制權交還給人的執行環境
17
17
  **Scope**: universal
18
18
  **產業標準**: 不宣稱任何來源——由實際觀察到的失敗歸納,見「證據」
@@ -141,6 +141,41 @@ agent 寫下「我接著做 X」,然後結束回合,而 X 沒有做。
141
141
 
142
142
  ---
143
143
 
144
+ ## 支援的執行環境
145
+
146
+ 這個檢查只在「轉接層存在,且 hook 真的被接進該執行環境自己的設定」時才生效。
147
+ 截至 v1.4.0:
148
+
149
+ | 執行環境 | 事件 | 設定檔 | 阻擋契約 |
150
+ |---|---|---|---|
151
+ | Claude Code | Stop | `.claude/settings.json` | stdout 印 `{"decision":"block","reason":...}`,exit 0;沉默即放行 |
152
+ | Codex | Stop | `.codex/hooks.json` | stdout 印 `{"decision":"block","reason":...}`,exit 0——官方文件寫明這個事件純文字或空輸出無效 |
153
+ | Gemini CLI | AfterAgent | `.gemini/settings.json` | stdout 印 `{"decision":"deny","reason":...}`,exit 0——官方文件標記為優先於 exit code 2 的做法 |
154
+
155
+ Codex 的 R9 豁免是盡力而為,不是靜默失效:Codex 的 Stop payload 直接給
156
+ agent 的最後一則訊息,卻不給使用者的;要拿到使用者那一側必須解析一份
157
+ 逐字稿檔案。已對真實 codex-cli 0.156.1 安裝坐實(2026-09-26):
158
+ `~/.codex/sessions/**/*.jsonl` 裡的一則使用者訊息長這樣——
159
+ `{"type":"response_item","payload":{"type":"message","role":"user",
160
+ "content":[{"type":"input_text","text":...}]}}`——訊息住在 `payload`
161
+ 底下,不在該行的最外層、也不在 `message` 鍵底下;同一種形狀但
162
+ `role: "developer"` 的紀錄不算使用者訊息。6.13.0-beta.1 的轉接層試過的
163
+ 兩種形狀都不是這個真實形狀,所以 R9 在 Codex 上從未真的豁免過任何一輪;
164
+ 6.13.0-beta.2 已修正。解析失敗(或遇到辨識不出的紀錄形狀)時仍只是讓
165
+ 使用者那一側變空、不會拋出例外——偵測仍照樣跑在 agent 訊息上,只有那一輪
166
+ 的 R9 豁免可能漏掉。
167
+
168
+ Cursor 已評估但不支援:截至撰寫本文時,Cursor 的 stop hook 能不能真的
169
+ 擋下一個回合仍未確定,若對著一個沒人驗證過的契約出一份轉接層,
170
+ 等於重演 R3 要防的那個失敗——一個沒人確認過真的在執法的執法機制。
171
+
172
+ 不在上表的任何執行環境,這個檢查都是失效的——與語言不支援(R8)同一種
173
+ 「預設沉默」失敗。`uds init --with-hooks` 會回報它把 hook 接進了哪些
174
+ 執行環境;它不會在這裡窮舉其餘的,因為那份清單是一張等著在下一個
175
+ 執行環境被加入或移除時就過期的引用。
176
+
177
+ ---
178
+
144
179
  ## 偵測器比對什麼
145
180
 
146
181
  形狀是:**第一人稱未來標記,接一個動作動詞,在同一個句子裡**,再減掉三個排除項。
@@ -188,3 +223,5 @@ agent 寫下「我接著做 X」,然後結束回合,而 X 沒有做。
188
223
  - [ ] 檢查認得出自己的攔阻訊息,不把它讀成人說的話
189
224
  - [ ] 檢查認得出 R2 定義的逐項卡點結束,而且不擋它
190
225
  - [ ] 歸屬詞的搜尋排除檢查自己的標題與結構
226
+ - [ ] 每個支援的執行環境的阻擋契約都對照該環境自己的官方文件驗證過,不是照抄另一個環境
227
+ - [ ] 安裝器只為採用者實際選擇的執行環境寫入該環境的 hook 設定
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  source: ../../../docs/CLI-INIT-OPTIONS.md
3
- source_version: 3.5.2
4
- translation_version: 3.5.2
5
- last_synced: 2026-09-18
3
+ source_version: 3.6.0
4
+ translation_version: 3.6.0
5
+ last_synced: 2026-09-25
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.5.2
14
- > **最後更新**: 2026-09-18
13
+ > **版本**: 3.6.0
14
+ > **最後更新**: 2026-09-25
15
15
 
16
16
  本文件詳細說明 `uds init` 命令的每一個選項,包含使用情境、影響範圍和建議選擇。
17
17
 
@@ -838,6 +838,25 @@ uds init --experimental
838
838
  | Claude Code 目標檔 | `--claude-target` | Claude Code 整合內容要寫到哪裡:`project`(`CLAUDE.md`,預設)或 `local`(`CLAUDE.local.md`) |
839
839
  | 模式(已棄用) | `-m, --mode` | 安裝模式(skills, full)- 請改用 `--skills-location` |
840
840
 
841
+ ### Claude Code 以外的強制執行 Hooks
842
+
843
+ `--with-hooks` 一定會安裝進 `.claude/settings.json`。四個有 hook 支援的標準
844
+ 之一——`turn-completion-integrity`(見 CHANGELOG,Unreleased)——也會裝進
845
+ **Codex** 與 **Gemini CLI**,門檻是你有沒有在 [AI 工具選擇](#1-ai-工具選擇)
846
+ 裡選了那個工具(或用非互動模式的工具旗標帶入):
847
+
848
+ | 工具 | 寫入的設定檔 | 觸發條件 |
849
+ |------|-------------|---------|
850
+ | Codex | `.codex/hooks.json` | 選了 **OpenAI Codex** |
851
+ | Gemini CLI | `.gemini/settings.json` | 選了 **Gemini CLI** |
852
+
853
+ 沒選的工具不會寫入任何東西——`uds init` 不會在沒用到 Codex 或 Gemini CLI
854
+ 的專案裡建立 `.codex/` 或 `.gemini/` 目錄。其餘三個有 hook 支援的標準
855
+ (commit message 驗證、logging、security)目前仍只支援 Claude Code;
856
+ 為什麼目前只推廣 turn-completion-integrity,以及 Cursor 的現況
857
+ (已評估、不支援),見
858
+ [支援的執行環境](../../../core/turn-completion-integrity.md#supported-harnesses)。
859
+
841
860
  ### Claude Code 整合目標檔(`--claude-target`)
842
861
 
843
862
  UDS 預設把 Claude Code 內容寫進 `CLAUDE.md`——團隊共用、會進版控的那個檔案。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-dev-standards",
3
- "version": "6.12.0",
3
+ "version": "6.13.0-beta.2",
4
4
  "description": "CLI tool for adopting Universal Development Standards",
5
5
  "keywords": [
6
6
  "documentation",
@@ -279,6 +279,32 @@ export async function initCommand(options) {
279
279
  console.log(chalk.gray(` ${line}`));
280
280
  }
281
281
  }
282
+
283
+ // turn-completion-integrity is also extended to Codex and Gemini CLI
284
+ // (2026-09-25), each with its own config file and output contract — see
285
+ // installCodexHooks/installGeminiHooks in hooks-installer.js. Gated on the
286
+ // tools the adopter actually selected: writing a hooks.json into every
287
+ // project's .codex/ regardless of whether Codex is used would be noise,
288
+ // not help.
289
+ const selectedTools = config.integrations || config.aiTools || [];
290
+ if (selectedTools.includes('codex')) {
291
+ const { installCodexHooks } = await import('../installers/hooks-installer.js');
292
+ const codexResult = installCodexHooks(projectPath);
293
+ if (codexResult.installed) {
294
+ console.log(chalk.green(' ✓ Codex Stop hook installed (turn-completion-integrity)'));
295
+ } else {
296
+ console.log(chalk.yellow(` ⚠ Codex hook not installed — ${codexResult.reason}`));
297
+ }
298
+ }
299
+ if (selectedTools.includes('gemini-cli')) {
300
+ const { installGeminiHooks } = await import('../installers/hooks-installer.js');
301
+ const geminiResult = installGeminiHooks(projectPath);
302
+ if (geminiResult.installed) {
303
+ console.log(chalk.green(' ✓ Gemini CLI AfterAgent hook installed (turn-completion-integrity)'));
304
+ } else {
305
+ console.log(chalk.yellow(` ⚠ Gemini CLI hook not installed — ${geminiResult.reason}`));
306
+ }
307
+ }
282
308
  }
283
309
 
284
310
  // 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)`, value: 'hooks', checked: true },
59
+ { name: `${msg.categoryHooks} (.husky/pre-commit, .claude/settings.json, .codex/hooks.json, .gemini/settings.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 }
@@ -218,3 +218,111 @@ export function installHooks(projectPath) {
218
218
  languageLimits: probeLanguageLimits(hooksDir, scripts),
219
219
  };
220
220
  }
221
+
222
+ /**
223
+ * turn-completion-integrity is the only standard extended to Codex and Gemini
224
+ * CLI so far (2026-09-25). Unlike installHooks() above, these two functions do
225
+ * NOT walk every standard's `enforcement:` block — the other three shipped
226
+ * standards declare Claude-Code-specific events (PreToolUse/PostToolUse with
227
+ * a Bash/Write matcher) that Codex and Gemini CLI's hook models don't obviously
228
+ * map onto, and generalizing that mapping is a separate piece of work. These
229
+ * are narrowly scoped to the one hook that has been verified against each
230
+ * tool's own docs.
231
+ *
232
+ * @see core/turn-completion-integrity.md
233
+ */
234
+ // Exported so the uninstaller (../uninstallers/hook-uninstaller.js) can
235
+ // recognize exactly these two script names as UDS's own, rather than
236
+ // guessing from the shared `scripts/hooks/` directory path alone — a path
237
+ // an adopter's own hook could just as easily live under.
238
+ export const CODEX_HOOK_SCRIPT = 'check-turn-completion-codex.mjs';
239
+ export const GEMINI_HOOK_SCRIPT = 'check-turn-completion-gemini.mjs';
240
+
241
+ /** Copy the shared hook scripts into the project, same as installHooks() does. */
242
+ function copyHookScripts(hookDir, hooksDir) {
243
+ if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });
244
+ cpSync(hookDir, hooksDir, { recursive: true });
245
+ }
246
+
247
+ /**
248
+ * Install the turn-completion-integrity Stop hook for Codex.
249
+ *
250
+ * Config lives at <project>/.codex/hooks.json — a dedicated file, NOT
251
+ * config.toml's [hooks] table. Codex runs matching hooks from every file that
252
+ * defines them (https://learn.chatgpt.com/docs/hooks, fetched 2026-09-25), so
253
+ * writing only hooks.json here is a deliberate choice, not an oversight: it
254
+ * does not need to also read or merge config.toml to avoid double-registering
255
+ * the same hook, and an adopter who already has a Stop hook in config.toml
256
+ * keeps it untouched.
257
+ *
258
+ * @param {string} projectPath
259
+ * @returns {{ installed: boolean, settingsPath: string, event?: string, reason?: string }}
260
+ */
261
+ export function installCodexHooks(projectPath) {
262
+ const hooksJsonPath = join(projectPath, '.codex', 'hooks.json');
263
+ const hookDir = hooksSourceDir();
264
+
265
+ if (!hookDir || !existsSync(join(hookDir, CODEX_HOOK_SCRIPT))) {
266
+ return { installed: false, settingsPath: hooksJsonPath, reason: `hook script not found: ${CODEX_HOOK_SCRIPT}` };
267
+ }
268
+
269
+ const codexDir = join(projectPath, '.codex');
270
+ if (!existsSync(codexDir)) mkdirSync(codexDir, { recursive: true });
271
+ copyHookScripts(hookDir, join(projectPath, 'scripts', 'hooks'));
272
+
273
+ let config = {};
274
+ if (existsSync(hooksJsonPath)) {
275
+ try { config = JSON.parse(readFileSync(hooksJsonPath, 'utf-8')); } catch { config = {}; }
276
+ }
277
+ if (!config.hooks) config.hooks = {};
278
+ if (!config.hooks.Stop) config.hooks.Stop = [];
279
+
280
+ // Matcher is omitted, not empty-stringed: Codex ignores any matcher on Stop
281
+ // ("any configured matcher is ignored"), and mergeHookArray's dedupe treats
282
+ // undefined === undefined, so this still merges idempotently.
283
+ config.hooks.Stop = mergeHookArray(config.hooks.Stop, [
284
+ { hooks: [{ type: 'command', command: `node scripts/hooks/${CODEX_HOOK_SCRIPT}`, timeout: 30 }] },
285
+ ]);
286
+
287
+ writeFileSync(hooksJsonPath, JSON.stringify(config, null, 2) + '\n');
288
+ return { installed: true, settingsPath: hooksJsonPath, event: 'Stop' };
289
+ }
290
+
291
+ /**
292
+ * Install the turn-completion-integrity AfterAgent hook for Gemini CLI.
293
+ *
294
+ * Config lives at <project>/.gemini/settings.json — shared with the rest of
295
+ * Gemini CLI's project settings, so only the `hooks.AfterAgent` key is ever
296
+ * touched here (https://geminicli.com/docs/hooks/, fetched 2026-09-25).
297
+ *
298
+ * @param {string} projectPath
299
+ * @returns {{ installed: boolean, settingsPath: string, event?: string, reason?: string }}
300
+ */
301
+ export function installGeminiHooks(projectPath) {
302
+ const settingsPath = join(projectPath, '.gemini', 'settings.json');
303
+ const hookDir = hooksSourceDir();
304
+
305
+ if (!hookDir || !existsSync(join(hookDir, GEMINI_HOOK_SCRIPT))) {
306
+ return { installed: false, settingsPath, reason: `hook script not found: ${GEMINI_HOOK_SCRIPT}` };
307
+ }
308
+
309
+ const geminiDir = join(projectPath, '.gemini');
310
+ if (!existsSync(geminiDir)) mkdirSync(geminiDir, { recursive: true });
311
+ copyHookScripts(hookDir, join(projectPath, 'scripts', 'hooks'));
312
+
313
+ let settings = {};
314
+ if (existsSync(settingsPath)) {
315
+ try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { settings = {}; }
316
+ }
317
+ if (!settings.hooks) settings.hooks = {};
318
+ if (!settings.hooks.AfterAgent) settings.hooks.AfterAgent = [];
319
+
320
+ // AfterAgent does not use matchers either (matchers apply only to Tool
321
+ // hooks); see the Codex function above for why matcher is omitted, not "".
322
+ settings.hooks.AfterAgent = mergeHookArray(settings.hooks.AfterAgent, [
323
+ { hooks: [{ type: 'command', command: `node scripts/hooks/${GEMINI_HOOK_SCRIPT}`, timeout: 5000 }] },
324
+ ]);
325
+
326
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
327
+ return { installed: true, settingsPath, event: 'AfterAgent' };
328
+ }