feat/lang-guard-traditional-chinese-encoding
develop
lang-guard.sh
references/ste100.md
hooks/lang-guard.sh
prompt
sweep [dir]
JSC_LANG_GUARD=off
hooks/simplified.txt
tools/ste100-lint.sh
SIMPLIFIED
hooks/hooks.json
UserPromptSubmit
lang-guard.sh prompt
PostToolUse
Write|Edit|MultiEdit
hooks/ste100-guard.sh
tools/wire-cli.sh
notify
userPromptSubmit
lang-guard.sh sweep
rules_text()
smoke
status
has_lang_guard()
tools/jsc-wrap.sh
|| true
README.md
JSC_LANG_GUARD
skills/hooks-install/SKILL.md
description
AGENTS.md
plugin.json
.claude-plugin/plugin.json
.codex-plugin/plugin.json
comment-scope.sh
.md
ste100-lint.sh
sweep
git diff HEAD
simplified.txt
ste100-guard.sh
grep -E
Ã
測試
ä
é
iconv
sh -n
hooks/lang-guard.sh -> exit 0 tools/wire-cli.sh -> exit 0 tools/jsc-wrap.sh -> exit 0
JSON ok, events: ['SessionStart', 'UserPromptSubmit', 'Stop', 'SessionEnd', 'PreToolUse', 'PostToolUse'] UserPromptSubmit cmds: 5 PostToolUse write block cmds: ['comment-scope.sh', 'lang-guard.sh']
simp.txt
fffd.txt
trad.txt
code.c
falsepos.txt
latin1.txt
dbl.txt
dbl2.txt
accent.txt
câmara
crème
naïve
誤報防線那一列的實際輸出:
falsepos.txt -> exit 0 accent.txt -> exit 0
== 4a. 乾淨工作區 sweep == exit 0 == 4b. 違規工作區 sweep(庫根)== exit 2 [jsc] 工作區有簡體字、亂碼或編碼問題,請就地修正: /tmp/.../gtest/doc.md 簡體字,改成繁體 1:新增一行:这里有简体字,应该被抓到。 修法:簡體字換成對應繁體字;亂碼那段重打,不要留著半壞的字元;檔案存成 UTF-8。 規則正文見 jsc-meta 的 references/ste100.md,字表在 hooks/simplified.txt。誤判時用 JSC_LANG_GUARD=off 關閉。 == 4c. 從子目錄找得到庫根 == exit 2 == 4d. 非 git 目錄 == exit 0 == 4e. 修好後回復乾淨 == exit 0
sh tools/wire-cli.sh smoke claude
status=ok reason=七支 hook 的每個接線模式都跑得完,沒有執行期錯誤 [jsc] session-timer.sh mark:exit 0,正常。 [jsc] sdlc-gate.sh check:exit 0,正常。 [jsc] version-guard.sh:exit 0,正常。 [jsc] skill-usage.sh:exit 0,正常。 [jsc] ste100-guard.sh:exit 0,正常。 [jsc] sdlc-gate.sh wp-check prompt:exit 0,正常。 [jsc] sdlc-gate.sh wp-check skill:exit 0,正常。 [jsc] comment-scope.sh prompt:exit 0,正常。 [jsc] comment-scope.sh:exit 0,正常。 [jsc] comment-scope.sh sweep:exit 0,正常。 [jsc] lang-guard.sh prompt:exit 0,正常。 [jsc] lang-guard.sh:exit 0,正常。 [jsc] lang-guard.sh sweep:exit 0,正常。 smoke exit 0
exit 42
== 先確認工作區必定命中 == lang-guard sweep exit 2 == 6. jsc-wrap.sh 結束碼保護 == fake copilot ran, args: --hello [jsc] 工作區有簡體字、亂碼或編碼問題,請就地修正: /tmp/.../gtest/doc.md 簡體字,改成繁體 1:新增一行:这里有简体字,应该被抓到。 jsc-wrap.sh exit 42 (expect 42)
/root/plugins
*.sh
*.md
*.json
*.txt
整檔掃描:scanned=437 hits=2 HIT: /root/plugins/meta/tools/ste100-lint.sh HIT: /root/plugins/sdlc/references/cpm-chart.md jsc-hooks 原地掃描:scanned=28 hits=0
兩筆命中逐一說明:sdlc/references/cpm-chart.md 是已知的真實簡體字(「只會出现在這一步」),不在本存取庫;meta/tools/ste100-lint.sh 命中的是它自己的 SIMPLIFIED_FALLBACK 備援字表那一行,屬於「被討論的對象」而非被使用,與本存取庫跳過 simplified.txt 是同一個道理,同樣不在本存取庫。本存取庫 28 個檔案 0 命中。
sdlc/references/cpm-chart.md
meta/tools/ste100-lint.sh
SIMPLIFIED_FALLBACK
補充驗證(非必做七項,一併附上):以假執行檔加暫存 HOME 實跑 codex 與 kiro 接線,兩份設定寫入後 status 全部 present:
HOME
present
== kiro status == item jsc-hooks-session-start ./.kiro/hooks/jsc-hooks-session-start.json present item jsc-hooks ./.kiro/hooks/jsc-hooks.json present item comment-scope ./.kiro/hooks/jsc-hooks.json present item comment-scope-sweep ./.kiro/hooks/jsc-hooks.json present item lang-guard ./.kiro/hooks/jsc-hooks.json present item lang-guard-sweep ./.kiro/hooks/jsc-hooks.json present == codex status == item notify .../config.toml present item notify-root .../config.toml present item notify-sweep .../config.toml present item notify-lang-sweep .../config.toml present item alias .../.bashrc present item ste100 .../AGENTS.md present item comment-scope .../AGENTS.md present item lang-guard .../AGENTS.md present
本存取庫自我檢查:comment-scope.sh sweep . 與 lang-guard.sh sweep . 皆 exit 0。
comment-scope.sh sweep .
lang-guard.sh sweep .
What:新增 `hooks/lang-guard.sh` 與機檢字表 `hooks/simplified.txt`。腳本比照 `comment-scope.sh` 的結構,一樣三種模式:`prompt` 注入規則摘要、無參數掃剛寫入的單一檔案、`sweep [dir]` 掃整個 git 工作區這次改過的檔案。偵測三項:簡體字、亂碼(U+FFFD 與雙重編碼殘骸)、非 UTF-8 編碼。命中走 stderr 並 exit 2,資料不足或找不到 git 一律安靜 exit 0,逃生門 `JSC_LANG_GUARD=off`。 Why:使用者新增的規則是「非程式碼的輸出一律套用繁中無亂碼」,範圍涵蓋程式碼註解、commit 訊息、PR 描述、wiki 頁、對使用者的回報與各種文件。這條規則原本只有 `ste100-guard.sh` 的提示層,模型看得到卻沒有人檢查,寫出簡體字或亂碼不會有任何回饋。提示層擋不住的事,就要有機檢層。 How:簡體字樣式由字表組出,字表是單一真實來源,腳本裡不留第二份;字表讀不到就安靜跳過這一項,不中斷整支腳本。亂碼一律用位元組比對(`LC_ALL=C`),不靠語系的字元範圍,因為 `grep -E` 的字元範圍在不同語系下行為不一致。掃描深度沿用 `comment-scope.sh`:檔案已追蹤就只掃 `git diff HEAD` 的新增行,不翻舊帳。二進位檔只認 NUL 位元組判定,不拿「非可列印字元」當判準,那會把所有含中文的檔案誤判成二進位。字表刻意排除繁體也在用的字(后、台、干、只、里、面、制、志),並跳過 `simplified.txt`、`ste100-guard.sh`、`lang-guard.sh` 三份以簡體字與亂碼為討論對象的檔案,避免整支 hook 每次先抓到自己。 Who:`jsc-hooks` 的 hook 實作層,供 `hooks/hooks.json` 與 `tools/wire-cli.sh` 接線給五個 CLI 使用。
What:`hooks/hooks.json` 的 `UserPromptSubmit` 加 `lang-guard.sh prompt`、`PostToolUse` 的 `Write|Edit|MultiEdit` 加無參數模式;`tools/wire-cli.sh` 把 codex 的 `notify`、kiro 的 `userPromptSubmit` 串上 sweep,規則檔文字加上 `lang-guard.sh prompt` 的輸出,`smoke` 三種模式各跑一輪,`status` 補盤點項,寫入後驗證一併涵蓋;`tools/jsc-wrap.sh` 收尾再跑一次 sweep;`hooks/ste100-guard.sh` 的注入文字補上適用範圍與 UTF-8 無亂碼。 Why:hook 寫好了不接線等於沒寫。接的位置與 `comment-scope.sh` 完全一樣,因為兩支要解的是同一個問題——只有 claude 有 post-tool hook,其餘四個 CLI 拿不到「剛剛寫了哪個檔」,只能改掃整個工作區。各 CLI 的掃描時機本來就不同:claude 逐檔即時、codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束,回報時不能寫成五支一樣。 How:規則檔文字取 `lang-guard.sh prompt` 的實際輸出,不在接線腳本裡抄一份規則,抄了兩邊就會各自漂移。寫入後以 `has_lang_guard()` 比對腳本第一行實際輸出,確認規則檔真的收到那一段。`smoke` 沿用既有的 exit 2 例外:掃描模式掃到違規回 2 是設計行為,不是執行期錯誤。`jsc-wrap.sh` 的 sweep 一律加 `|| true`,包裝器原樣回傳 CLI 自己的結束碼——包裝器改掉結束碼,呼叫端的 `cmd && next` 就會誤判。 Who:`jsc-hooks` 的接線層,由 `hooks-install` 技能對 claude、codex、copilot、antigravity、kiro 五個 CLI 執行。
What:`README.md` 的 hook 一覽表新增 `lang-guard.sh` 一列(事件、三種模式、逃生門),所有「六支 hook」改七支,環境變數表補 `JSC_LANG_GUARD`,`tools/jsc-wrap.sh` 那列補收尾 sweep;`skills/hooks-install/SKILL.md` 六支改七支並補覆蓋範圍與備註,`description` 的括號清單加上這支;`AGENTS.md` 清單同步,並新增一條繁中無亂碼規則。 Why:文件寫六支、實際跑七支,使用者與 sub agent 都會照文件辦事,少接的那一支永遠沒有人發現。`AGENTS.md` 是各 CLI 讀得到的規則檔,新規則不寫進去就只剩 hook 在擋,模型自己不會知道。 How:README 維持 STE100 繁中;SKILL.md 維持整份英文,`description` 保持 4 句、保留觸發時機。規則正文一律不在本存取庫留副本,只指向 `jsc-meta` 的 `references/ste100.md`,字表位置指向 `hooks/simplified.txt`。 Who:`jsc-hooks` 的文件層,讀者是使用者與執行 `hooks-install` 的 sub agent。
What:把 `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 三份 manifest 的版本由 0.2.0 升到 0.2.1。 Why:0.2.0 已隨前一支 PR 發佈出去,本次新增的第七支 hook 與五個 CLI 的接線沒有跟著升版,`version-guard.sh` 就看不出本機落後,使用者不會收到更新提示。 How:以 jsc-meta 的 `sync-skill-manifest.sh` 統一 bump,patch 進位,三份同步成同一個值。 Who:繁中與編碼檢查 hook 的跨 CLI 支援。
What:簡體字表由 102 字擴充到 1006 字;lang-guard.sh 的跳過清單補上 jsc-meta 的 references/ste100.md 與 tools/ste100-lint.sh。 Why:102 字的舊表覆蓋率太低——拿 21 個常見簡體詞實測只抓到 4 個,「简体字」 「举办」「觉得」「战争」「医药」這類全部漏掉,規則等於形同虛設。跳過清單則是 對稱性問題:規則文件與機檢工具本身會列舉簡體字當範例,不跳過就每次先抓到自己。 How:字表逐字檢查「簡化後才出現的字形」,剔除正體也在用的字(后、台、干、只、 里、面、制、志、出、斗、丑、了、卷、咸、云、留、言、短、困、陷、游、封、吞), 再拿 jsc 全部 11 個存取庫共 221 個檔案的既有正體中文語料回歸驗證,誤報為零。 檔頭寫明排除原則與「改完必須拿語料重驗」的要求。跳過清單比照 ste100-lint.sh 的做法。 Who:非程式碼輸出的繁中無亂碼檢查。
What:三份 manifest 由 0.2.1 升到 0.2.2。 Why:字表擴充後偵測能力大幅改變,本機沒跟著升版,version-guard.sh 就判不出落後, 使用者不會收到更新提示,繼續用覆蓋率只有兩成的舊字表。 How:以 jsc-meta 的 sync-skill-manifest.sh 統一 bump,三份同步成同一個值。 Who:非程式碼輸出的繁中無亂碼檢查。
No dependencies set.
The note is not visible to the blocked user.
feat/lang-guard-traditional-chinese-encoding
摘要
lang-guard.sh,強制「所有非程式碼輸出必須是繁體中文、UTF-8、無亂碼、無簡體字」。範圍涵蓋程式碼註解、commit 訊息、PR 描述、wiki 頁、對使用者的回報、README 與各種文件。規則正文的真實來源仍是 jsc-meta 的references/ste100.md,本存取庫只放判定實作與機檢字表。變更內容
hooks/lang-guard.shprompt、無參數單檔、sweep [dir]),偵測簡體字、亂碼、非 UTF-8 編碼。命中走 stderr 並 exit 2,資料不足一律安靜 exit 0,逃生門JSC_LANG_GUARD=offhooks/simplified.txttools/ste100-lint.sh的SIMPLIFIED變數,不增不刪。這是本存取庫的單一真實來源hooks/hooks.jsonUserPromptSubmit加lang-guard.sh prompt,PostToolUse的Write|Edit|MultiEdit加無參數模式hooks/ste100-guard.shtools/wire-cli.shnotify與 kiro 的userPromptSubmit串上lang-guard.sh sweep;rules_text()加上lang-guard.sh prompt的輸出;smoke三種模式各跑一輪並沿用既有的 exit 2 例外;status補盤點項;寫入後驗證新增has_lang_guard()tools/jsc-wrap.shlang-guard.sh sweep,以|| true接住 exit 2,不蓋掉 CLI 自己的結束碼README.mdJSC_LANG_GUARD;tools/jsc-wrap.sh那列補收尾 sweepskills/hooks-install/SKILL.mddescription括號清單加上 language guard,維持英文 4 句並保留觸發時機AGENTS.mdplugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json設計重點
comment-scope.sh不同,那支只掃註解行,因為它抓的是註解夾帶文件編號,那件事只發生在註解裡。comment-scope.sh刻意跳過.md,因為那裡沒有程式碼註解,這支的判斷剛好相反。ste100-lint.sh優先讀這份、讀不到才用它自己的內建備援。本存取庫不放第二份字表,腳本裡也不留字元清單副本。PostToolUse)、codex 每輪結束(notify)、kiro 每輪提示送出時(userPromptSubmit,掃到的是上一輪寫的檔)、copilot 與 antigravity 只有工作階段結束時由tools/jsc-wrap.sh收尾掃一次。sweep看的是git diff HEAD,涵蓋範圍與 claude 一樣,差的只有回饋速度。simplified.txt、ste100-guard.sh、lang-guard.sh三份以簡體字與亂碼為討論對象的檔案,不然這支 hook 每次都會先抓到自己,訊號全被自己的噪音蓋掉。grep -E的字元範圍在不同語系下行為不一致,位元組範圍到哪都一樣。雙重編碼的前導字收整個 Latin-1 字母段而不只收Ã:中文的 UTF-8 前導位元組落在 E4–E9,被當成 Latin-1 讀一次後會變成測試這種ä–é開頭的長相,只收Ã會漏掉最常見的中文亂碼。iconv就跳過編碼那一項,都不中斷整支腳本;少抓一項好過整支 hook 死掉。測試結果
sh -n語法檢查,三支皆 exit 0:hooks/hooks.json通過 JSON 解析:simp.txtfffd.txttrad.txtcode.cfalsepos.txtlatin1.txtdbl.txt測試dbl2.txtaccent.txtcâmara、crème、naïve誤報防線那一列的實際輸出:
sweep:sh tools/wire-cli.sh smoke claude完整輸出:exit 42搭配必定命中的工作區(先確認lang-guard.sh sweep對該工作區回 2):/root/plugins底下所有*.sh、*.md、*.json、*.txt逐檔整檔掃描(複製到非 git 暫存目錄以強制整檔掃,不是只掃 diff):兩筆命中逐一說明:
sdlc/references/cpm-chart.md是已知的真實簡體字(「只會出现在這一步」),不在本存取庫;meta/tools/ste100-lint.sh命中的是它自己的SIMPLIFIED_FALLBACK備援字表那一行,屬於「被討論的對象」而非被使用,與本存取庫跳過simplified.txt是同一個道理,同樣不在本存取庫。本存取庫 28 個檔案 0 命中。補充驗證(非必做七項,一併附上):以假執行檔加暫存
HOME實跑 codex 與 kiro 接線,兩份設定寫入後status全部present:本存取庫自我檢查:
comment-scope.sh sweep .與lang-guard.sh sweep .皆 exit 0。前置 Push Request