Commit Graph
122 Commits
Author SHA1 Message Date
admin 6b272974b5 Merge pull request 'fix/codex-model-gate' (#48) from fix/codex-model-gate into develop
Reviewed-on: #48
2026-08-28 10:12:31 +00:00
admin ca68de8540 Merge pull request 'fix(codex-hooks): 避免 Claude manifest 在 Codex 變成空路徑' (#50) from fix/codex-claude-hook-manifest into develop
Reviewed-on: #50
2026-08-28 10:09:31 +00:00
jiantw83 955a36adb9 fix(codex-hooks): 避免 Claude manifest 在 Codex 變成空路徑 2026-08-28 18:07:20 +08:00
admin 1e728e3729 Merge pull request 'fix/report-error-confirm' (#49) from fix/report-error-confirm into develop
Reviewed-on: #49
2026-08-28 10:00:49 +00:00
Jeffery a0a15b6ce5 fix(report-error): 寫入前先確認 2026-08-28 17:58:52 +08:00
jiantw83 83f6bdf21c fix(model-gate): 支援 Codex 可驗證模型來源 2026-08-28 16:45:58 +08:00
admin 9785af335a Merge pull request 'fix(wire-cli): purge 容忍標記行空白' (#47) from fix/hook-purge-marker-whitespace into develop
Reviewed-on: #47
2026-08-28 08:07:28 +00:00
jiantw83 49101fefdb fix(wire-cli): purge 容忍標記行空白 2026-08-28 16:03:25 +08:00
admin 2d1c8bbc06 Merge pull request 'feat/plugin-dependencies/main' (#45) from feat/plugin-dependencies/main into develop
Reviewed-on: #45
2026-08-28 04:05:00 +00:00
admin a2a8e8b837 Merge pull request 'feat/plugin-dependencies/declare-requires' (#44) from feat/plugin-dependencies/declare-requires into feat/plugin-dependencies/main
Reviewed-on: #44
2026-08-28 04:03:03 +00:00
jiantw83 8a1d9d116e feat(manifest): 宣告 hooks 相依版本 2026-08-28 11:59:16 +08:00
admin d5867623df Merge pull request 'fix/hook-stable-runtime-path' (#42) from fix/hook-stable-runtime-path into develop
Reviewed-on: #42
2026-08-28 03:27:49 +00:00
jiantw83 4dc879e202 chore(release): 發布 jsc-hooks 0.2.8 2026-08-28 11:22:13 +08:00
jiantw83 4e23e49159 fix(hooks-install): 使用穩定路徑寫入 hook 接線 2026-08-28 11:22:08 +08:00
admin de6b012356 Merge pull request 'feat/change-requests/main' (#40) from feat/change-requests/main into develop
Reviewed-on: #40
2026-08-28 01:51:14 +00:00
admin e37506dffb Merge pull request 'feat/change-requests/state-and-comment-scope' (#39) from feat/change-requests/state-and-comment-scope into feat/change-requests/main
Reviewed-on: #39
2026-08-28 01:46:55 +00:00
jiantw83 1477ba7b29 feat(comment-scope): 偵測審查流程痕跡 2026-08-28 09:30:18 +08:00
jiantw83 f6335180fa fix(version-guard): 依 CLI 分離遠端版本快取 2026-08-28 09:30:12 +08:00
admin 852d8553b4 Merge pull request 'fix(hooks): 重啟閘門狀態檔改為一支 CLI 一份,修並行覆寫與全域解除' (#37) from fix/restart-gate-per-cli-state into develop
Reviewed-on: #37
2026-08-27 10:51:20 +00:00
jiantw83 0556512ebf chore(hooks): 三份 manifest 版本升到 0.2.6
What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 的 `version` 從 0.2.5 升到 0.2.6,三份同步,description 不動。

Why:這一輪改掉重啟閘門的狀態檔路徑與範圍,是使用者裝上去就會拿到的行為變更。版本不升,`version-guard.sh` 的版本前置檢查與 `jsc-cli:deploy` 的落後判定都看不出本機還是舊版,機器上就不會被提示更新。

How:只改版號一個欄位。三份必須一致:`plugin.json` 給 marketplace、`.claude-plugin` 給 claude、`.codex-plugin` 給 codex,任一份落後都會讓那一路的版本比對抓錯。修的是既有行為的缺陷、沒有新增子命令也沒有改變呼叫介面,所以走修訂號。

Who:`jsc-hooks` 的三份 plugin manifest,配合這一輪重啟閘門的修正發佈。
2026-08-27 18:49:58 +08:00
jiantw83 c0c944085c docs(restart-gate): 說明文件同步一支 CLI 一份的狀態檔
What:`README.md` 改三處——Hooks 表的 `restart-gate.sh` 與 `session-timer.sh` 兩列、覆蓋範圍的引言、以及「部署後重啟狀態檔」整節。整節改寫後寫明狀態檔路徑是 `$JSC_HOME/restart-required.d/{CLI 代號}`、一支 CLI 一份的理由、`report` 的輸出格式,以及舊檔相容與它可移除的時機。`skills/hooks-install/SKILL.md` 第 45 項改為 `restart-required.d/{cli}`,補上「每支 CLI 只讀自己那一份」、「重啟只清自己那一份」、「`report` 一支 CLI 一行」與舊檔過渡說明。

Why:這兩份是接線與判讀閘門時唯一會被讀到的說明。路徑改了而文件沒改,看文件的人會去看一個不存在的檔案,也會沿用「重啟一支就全清」的舊認知去判斷閘門有沒有生效。一支 CLI 一份的理由(並行覆寫與全域解除兩個實測缺陷)要留在文件裡,下一個人才不會為了少一個目錄又改回單一檔案。

How:只改說明,不動任何判定邏輯。路徑與格式的唯一來源仍是 `hooks/restart-gate.sh`,文件只轉述,舊檔相容的取捨與可移除清單也指回檔頭那一節,兩邊不各自維護一份。覆蓋範圍的引言照舊據實寫:非 claude 的四支 CLI 沒有 pre-tool hook,這道閘門一次都擋不下來,改成一支一份也不會變。

Who:`jsc-hooks` 的存取庫說明與 `jsc-hooks:hooks-install` 技能文件,對齊部署後重啟閘門的實作。
2026-08-27 18:49:58 +08:00
jiantw83 9586e63005 test(restart-gate): 冒煙測試加驗一支 CLI 一份的範圍
What:`tools/wire-cli.sh` 的 `smoke` 重啟閘門段從 9 條情境擴為 16 條,新增 `smoke_rs_file()` 比對狀態檔在不在。新增的情境是:只有別支 CLI 有狀態檔時放行、`require` 寫出的是當前 CLI 那一份、新工作階段開始後只清掉自己那一份、清除不動別支那一份、清除後放行、舊格式單一狀態檔存在時擋下、`clear` 一併刪掉舊檔、舊檔清除後放行。原有的豁免技能、逃生門與取不到技能名三類情境照舊,情境名稱從「狀態檔存在」改寫為「當前 CLI 那份存在」。檔頭的判定路徑清單同步改寫。

Why:只比對結束碼看不出「一支 CLI 一份」有沒有真的成立——別支那一份要留著、自己那一份要刪掉,兩件事都反映在檔案在不在,不反映在結束碼。舊版的冒煙只驗得到「狀態檔不存在」與「狀態檔存在」兩條,正好漏掉這次修掉的兩個缺陷所在的範圍:並行部署的覆寫與清除的全域解除,兩者在單一檔案的年代都跑得出 exit 0 與 exit 2 的正確值。

How:情境沿用暫時的 `$JSC_HOME`,不動使用者真正的狀態目錄——冒煙測試不該把別人的閘門拆掉。別支 CLI 的代號取 `smoke-other` 這個不在五支之列的固定值,才不會跟這一輪的 `$cli` 撞在一起。`smoke_rs_file()` 只比 `exist` 與 `absent` 兩種預期,失敗訊息直接講明「一支 CLI 一份的範圍壞了」,看訊息就知道是範圍出錯,不是腳本跑不動。舊檔那兩條情境走 `session-timer.sh restart` 觸發清除,跟真實路徑一致。

Who:`jsc-hooks` 接線工具的冒煙測試,覆蓋部署後重啟閘門的判定與清除範圍。
2026-08-27 18:49:58 +08:00
jiantw83 7517566156 fix(restart-gate): 重啟閘門狀態檔改為一支 CLI 一份
What:`hooks/restart-gate.sh` 的狀態檔從全機器單一檔案 `$JSC_HOME/restart-required` 改為狀態目錄 `$JSC_HOME/restart-required.d/{CLI 代號}`,一支 CLI 一份,檔名就是 CLI 代號,四行 key=value 的格式不變。新增 `cli_code()` 取當前 CLI 代號(含不能當檔名的髒值防護)與 `state_line()` 統一 `report` 的輸出格式;`require` 只寫自己那一份、hook 判定只讀自己那一份、`clear` 只刪自己那一份、`report` 一支 CLI 一行印出還沒重啟的是哪幾支。舊格式的單一檔案存在時一律擋,訊息標明是舊格式紀錄,`clear` 會一併刪掉它。檔頭註解同步改寫,新增「report 輸出格式」與「舊檔相容(過渡用)」兩節。`hooks/session-timer.sh` 程式碼不動,兩處註解補上「清除範圍只有跑到這支腳本的那一支 CLI」。

Why:這道閘門 2026-08-27 才上線,部署現場實測抓到兩個缺陷。第一,`require` 用 `>` 覆寫單一檔案,並行部署互相覆蓋——kiro 寫入 9 秒後被 codex 蓋掉,`domains=` 與 `cli=` 不再代表 kiro。第二,`clear` 用 `rm -f` 刪整個檔案,任一支 CLI 重啟就解除全部五支的閘門,其餘四支沒重啟卻不再被擋。這台機器就裝了五支 CLI,閘門在多 CLI 環境等於半失效。閘門管的是「這一支 CLI 的行程還在跑舊版」,那是每支 CLI 各自的事實,狀態檔本來就不該共用。

How:寫入、判定、清除三件事都只碰自己那一份,別支那幾份一律不看,`clear` 的範圍也就收斂成呼叫端那一支。取不到 CLI 代號時比照既有的「查不到就放行」原則:hook 模式放行,`require` 回 exit 2 並講明這次沒有掛上閘門——寫到讀不到的檔名等於沒掛,不能讓部署以為掛上了。代號會直接拿去當檔名,所以含 `/`、以 `.` 開頭、或出現 `[A-Za-z0-9._-]` 以外字元的值一律當成取不到,狀態檔就寫不到目錄外面去。舊檔沒有 per-CLI 資訊,分不出是哪一支寫的,判定寧可擋多不擋少;`clear` 只在新工作階段被呼叫,呼叫到就代表確實有一支重新啟動過了,舊檔留著會讓五支一路被擋到有人手動刪,所以一併刪掉。這段相容邏輯與可移除的時機(所有機器都跑過一次寫狀態目錄的部署與重啟之後)寫在檔頭「舊檔相容」與 README。`session-timer.sh` 不必跟著改:狀態檔的路徑、範圍與格式只留在 `restart-gate.sh`,那裡只負責判斷新舊工作階段。

Who:`jsc-hooks` 的部署後強制重啟閘門(R15),以及它與 `session-timer.sh` 的清除分工。
2026-08-27 18:49:58 +08:00
admin d808b05d45 Merge pull request 'feat(hooks): 新增部署後強制重啟閘門,接線與冒煙同步到八支 hook' (#35) from feat/skillset-governance/main into develop
Reviewed-on: #35
2026-08-27 08:54:38 +00:00
admin dea775fea5 Merge pull request 'docs(hooks-install): 豁免清單補齊為九支,與實作和準則對齊' (#34) from feat/skillset-governance/exempt-list-sync into feat/skillset-governance/main
Reviewed-on: #34
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-27 08:51:31 +00:00
jiantw83 7d67538a2a docs(hooks-install): 豁免清單補齊為九支,與實作和準則對齊
What:`skills/hooks-install/SKILL.md` 的 Notes 段落,重啟閘門的豁免技能清單從六支補到九支,補上 `jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`,並寫明「閘門認技能名不認呼叫鏈」以及清單的唯一來源在 `hooks/restart-gate.sh`。

Why:這三支是使用者在同一輪追問後才裁定加入的,實作 `hooks/restart-gate.sh` 與準則 `guidelines.md`「部署後重啟閘門」都已經是九支,只有這份技能文件還停在六支。技能文件是接線時唯一會被讀到的說明,少列三支會讓人以為 `deploy` 問模式、收尾開 PR 都會被擋,反而去下逃生門。

How:只改那一行,補上三支與兩句說明,並指向清單的唯一來源,避免下次又各自維護一份。三份 manifest 版本同步升到 0.2.5。

Who:`jsc-hooks:hooks-install` 技能文件,以及部署後重啟閘門這條規則的說明一致性。
2026-08-27 16:49:02 +08:00
admin fe7a3abe2a Merge pull request 'feat(hooks): 新增部署後強制重啟閘門,接線與冒煙同步到八支 hook' (#33) from feat/skillset-governance/restart-gate into feat/skillset-governance/main
Reviewed-on: #33
2026-08-27 08:38:50 +00:00
jiantw83 4f92c48e10 chore(manifest): 三份 manifest 版本升到 0.2.4,描述補齊八支 hook
What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 的 `version` 由 0.2.3 改為 0.2.4,`description` 的 hook 清單從五支補齊到八支,補上註解範圍守門、繁中編碼守門與部署後強制重啟。

Why:本次新增第八支 hook `restart-gate.sh` 並接進 `hooks.json`,`session-timer.sh` 多了清除閘門這件事,接線與冒煙也跟著改,屬於行為變更,版本要跟著往上走,各 CLI 才知道要更新。`description` 原本只列到「版本前置檢查」,而註解範圍與繁中編碼兩支早就在跑了,`README.md` 與 `AGENTS.md` 也都寫了八支——manifest 是各 CLI 安裝時唯一看得到的說明,落後就會讓人以為這個外掛只有五支 hook。

How:三份只改 `version` 與 `description` 兩個欄位,其餘內容不動,三份保持同一版號與同一段描述。

Who:`jsc-hooks` 外掛的套件描述檔。
2026-08-27 16:34:17 +08:00
jiantw83 c1f2a5d728 docs(hooks): README 與 AGENTS 補上部署後重啟閘門與狀態檔格式
What:`README.md` 五處增修:hook 表新增 `hooks/restart-gate.sh` 一列,`session-timer.sh` 那一列補上會清除閘門、`wire-cli.sh` 那一列補上冒煙新增的判定路徑;覆蓋範圍那段的「七支」改「八支」並補上四個 CLI 接不上重啟閘門的後果;新增「部署後重啟狀態檔」一節,用表列出四個欄位與範例,寫明誰寫誰讀;環境變數表新增 `JSC_RESTART_GATE` 一列;`hooks-install` 段落同步。`AGENTS.md` 的 domain 一句話說明補上「部署後強制重啟閘門」。

Why:狀態檔的格式是 `jsc-hooks` 與 `jsc-cli` 兩邊的介面。介面只寫在腳本註解裡,另一邊改的時候看不到,格式一走鐘閘門就掛不上——這次就真的發生過:`jsc-cli` 寫四欄 TSV、這邊讀 `key=value`,狀態檔存在卻解不出欄位。覆蓋範圍也要據實寫,八支 hook 只有 claude 全接得上。

How:格式壓到最簡的純文字 `key=value`,一行一欄位,順序不拘,不認得的鍵一律忽略,與工作包狀態檔同一套寫法,兩邊各自實作也對得上。表只寫欄位與範例,判定規則寫在表底下:判定看檔案在不在、欄位只用在擋人訊息上、寫的一律是 `jsc-cli:deploy`、清的一律是 `session-timer.sh`,而且判準留在 `session-timer.sh`、狀態檔留在 `restart-gate.sh`,兩邊都不抄對方那一半。

Who:讀 `jsc-hooks` 說明的人,以及 `jsc-cli` 那一側寫 `deploy.sh` 的人。
2026-08-27 16:34:17 +08:00
jiantw83 7f8615a9ed feat(hooks-install): 技能同步到八支 hook 與重啟閘門的降級說法
What:`skills/hooks-install/SKILL.md` 四處增修。`description` 與目標段落的 hook 清單加入 `restart-gate.sh`,七支改八支;降級說明補上「四個 CLI 連部署後重啟閘門也接不上」與後果一句;第 4 步的冒煙說明補上重啟閘門的每條判定路徑;備註新增 `restart-gate.sh` 一條,並在 `session-timer.sh` 那一條補上「`start` 與 `restart` 會清除閘門」。

Why:技能是接線這件事的對外說法。hook 加了一支、技能還寫七支,回報就會少一項,而且降級說法不補會暗示每個 CLI 都擋得下來——這正是準則明文禁止的。`session-timer.sh` 那條也要補:清除閘門掛在那兩個事件上,接線少了它們,閘門會一路擋到使用者自己下逃生門。

How:降級那一段把後果講明白,不只說「接不上」:在那四個 CLI 上一次技能呼叫都擋不下來,狀態檔照樣寫、下一個工作階段開始照樣清,重啟本身只靠 `jsc-cli:deploy` 的收尾訊息。備註那一條寫出豁免清單與逃生門,並講明豁免的理由是異動報告與工作日誌要寫得完,讓接線的人知道哪些技能在閘門升起時仍然叫得動。

Who:`/jsc-hooks:hooks-install` 的接線流程與對使用者的回報。
2026-08-27 16:34:17 +08:00
jiantw83 e370729720 feat(wire-cli): 接線腳本納入部署後重啟閘門,七支改八支
What:`tools/wire-cli.sh` 四處增修。`smoke` 加跑 `restart-gate.sh`,另外自備一份暫時的 `$JSC_HOME`,把八條判定路徑(狀態檔不存在、狀態檔存在且技能為 `jsc-sdlc:implement`、四支豁免技能各一條、逃生門、取不到技能名)各跑一次並比對結束碼,再驗一次清除機制真的清得掉;claude 的接線驗證與 `status` 盤點各加一個 `restart-gate` 檢查點;codex、copilot、antigravity、kiro 四支的 degraded 說法補上「部署後重啟閘門也接不上」;檔頭與各處「七支」一律改為「八支」。

Why:新的 hook 接進 `hooks.json` 只代表宣告在檔案裡。接線驗證不點名就漏得掉——`comment-scope.sh` 與 `lang-guard.sh` 當初就是為同一個原因各列一項。冒煙測試也一樣:不自備狀態檔,只走得到「狀態檔不存在」與「取不到技能名」兩條捷徑,擋人與豁免那幾條一次都跑不到,判定寫了卻沒驗等於沒寫。四個 CLI 的降級說法不補,回報就會暗示每個 CLI 都擋得下來。

How:驗的是判定結果本身,不只是腳本跑得完——每一條路徑都給定預期結束碼,對不上就計入失敗並印出前 200 字的輸出。狀態檔放在暫時目錄,冒煙測試不該把使用者真正的 `$JSC_HOME/restart-required` 拆掉。清除機制那一段刻意走 `session-timer.sh start` 這條真實路徑,不直接呼叫 `clear`:要驗的是「新工作階段會不會清」,不是「`clear` 這個子命令能不能刪檔」。每次呼叫都接 `</dev/null`:hook 模式會讀標準輸入,管線沒人關閉時整支會卡死。建不出暫存目錄也算失敗,不能靜悄悄跳過。四支非 claude 的 CLI 除了 degraded 一句,另外逐支印一行講明後果:一次技能呼叫都擋不下來,狀態檔照樣寫、下次工作階段開始照樣清,只是中間沒有判定點,重啟要靠 `/jsc-cli:deploy` 收尾的提示自己動手。

Who:`jsc-hooks:hooks-install` 的接線、冒煙與盤點三個子命令。
2026-08-27 16:34:17 +08:00
jiantw83 dbf9aac25e feat(restart-gate): 新增部署後強制重啟閘門,並接上新工作階段自動清除
What:新增第八支 hook `hooks/restart-gate.sh`。hook 模式在 `$JSC_HOME/restart-required` 存在時以 exit 2 擋下 jsc 技能呼叫,另有三個子命令:`require {模式} [{domain}...]` 寫入狀態檔掛上閘門、`clear` 清除狀態檔、`report` 印出狀態檔內容。`hooks/hooks.json` 把它接到 PreToolUse(Skill),排在 `version-guard.sh` 前面。`hooks/session-timer.sh` 的 `start` 與 `restart` 在判定為新工作階段時轉呼叫 `clear`。

Why:部署換掉的是磁碟上的技能檔,正在跑的 CLI 行程載入的還是舊版——SKILL.md、hook 腳本與 tools 都在啟動當下讀進記憶體。這段落差期間跑技能,改動看起來沒生效,人會以為部署失敗又重跑一次。所以部署收尾要求重新啟動,這道閘門負責讓「還沒重啟就繼續用技能」擋在門外。

How:判定看的是「檔案在不在」,欄位只用在擋人訊息上——欄位缺了只讓訊息少幾個字,不影響判定。狀態檔用純文字 `key=value`(`at`、`mode`、`domains`、`cli`),格式與工作包狀態檔同一套,`jsc-hooks` 與 `jsc-cli` 兩邊各自實作也對得上。放行原則比照 `version-guard.sh`:只擋確定違規,狀態檔讀不到、技能名取不到、工具名不是 `Skill`、技能不是 `jsc-*:*` 一律 exit 0,沒有證據時擋下等於停掉每一次技能呼叫。九支豁免(`jsc-cli:deploy`、`jsc-hooks:hooks-install`、`jsc-gitea:wiki`、`jsc-log:worklog`、`jsc-log:learn`、`jsc-meta:*`、`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)的理由是同一件事:部署後還要寫得完技能組異動報告與工作日誌,整批擋下去「先重啟」與「先寫完報告」會互相打死。後三支自己不是收尾規則的主體,是為了讓前六支走得完才補進來的——清單認的是技能名,不是呼叫鏈。清除交給 `session-timer.sh`,不由本檔自己判:新舊工作階段的判準(`sessions/{sid}.start` 在不在)只有那支腳本知道,兩邊各寫一份就會漂移;續接同一階段走不到那一段,閘門就一路留到真的重新啟動。`require`、`clear`、`report` 都不讀標準輸入,只有 hook 模式讀,工具端呼叫一律再補 `</dev/null`,理由與 `sdlc-gate.sh` 相同。接線排在 `version-guard.sh` 前面:還沒重啟的舊版比落後一個版號更該先攔。不認得的子命令一律安靜 exit 0,不中斷宿主 CLI。擋人訊息依實際 CLI 給重啟方式,並附上豁免清單與逃生門 `JSC_RESTART_GATE=off`。

Who:部署後的第一次技能呼叫,以及 `jsc-cli:deploy` 收尾寫入狀態檔的那一端。
2026-08-27 16:34:17 +08:00
jiantw83 e714c6ade2 Merge pull request 'feat(hooks): 工作包閘門加上歸屬比對與領取、交回子指令' (#31) from feat/sdlc-flow-rules/main into develop 2026-08-27 03:39:37 +00:00
admin e53dd802b8 Merge pull request 'feat(hooks): 工作包歸屬比對與狀態檔格式' (#30) from feat/sdlc-flow-rules/wp-scope-gate into feat/sdlc-flow-rules/main
Reviewed-on: #30
2026-08-27 03:26:10 +00:00
jiantw83 be75cc4bed chore(manifest): 三份 manifest 版本升到 0.2.3
What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 的 `version` 由 0.2.2 改為 0.2.3。

Why:本次新增 `wp-claim`、`wp-unclaim` 兩個子命令並改了鎖檔格式,屬於行為變更,版本要跟著往上走,各 CLI 才知道要更新。

How:三份只改 `version` 一個欄位,其餘內容不動,三份保持同一版號。

Who:`jsc-hooks` 外掛的套件描述檔。
2026-08-27 11:20:28 +08:00
jiantw83 29facd385c docs(hooks): README 補上工作包歸屬狀態檔格式
What:README 新增「工作包歸屬狀態檔」一節,用表列出鎖檔與領取檔的檔名、欄位與範例,寫明誰寫誰讀、查無歸屬一律放行、舊版 TSV 鎖檔照樣讀得動;`sdlc-gate.sh` 與 `wire-cli.sh` 兩列的說明同步補上新子命令與冒煙測試的新內容。

Why:狀態檔的格式是 `jsc-hooks` 與 `jsc-sdlc` 兩邊的介面。介面只寫在腳本註解裡,另一邊改的時候看不到,格式一走鐘歸屬就全部誤判。

How:格式壓到最簡的純文字 `key=value`,一行一欄位,順序不拘,不認得的鍵一律忽略,兩邊各自實作也對得上。表只寫欄位與範例,判定規則寫在表底下的三段短說明。

Who:讀 `jsc-hooks` 說明的人,以及 `jsc-sdlc` 那一側寫 `wp-gate.sh` 的人。
2026-08-27 11:20:28 +08:00
jiantw83 3b6dd1b901 test(wire-cli): 冒煙測試補驗工作包歸屬的四條判定路徑
What:`tools/wire-cli.sh` 的 `smoke` 新增一段,用一份暫時的 `$JSC_HOME` 狀態檔把「狀態檔不存在」「PR 屬於領取中的工作包」「PR 屬於別的工作包」「逃生門 `JSC_WP_GATE=off`」四種情境各跑一次,逐一比對結束碼;收尾那句 `status=ok` 的說明也跟著補上這一段。

Why:原本兩支 `wp-check` 冒煙都把技能名清空,只走得到「沒有未結清 PR」與「取不到技能名」兩條捷徑,歸屬比對整段一次都沒跑到。判定寫了卻沒驗,等於沒寫。

How:驗的是判定結果本身,不只是腳本跑得完——每一種情境都給定預期結束碼,對不上就計入失敗並印出前 200 字的輸出。狀態檔放在暫時目錄,冒煙測試不該在使用者真正的 `$JSC_HOME/wp/` 留下痕跡。每次呼叫都接 `</dev/null`:`wp-check` 會讀標準輸入,管線沒人關閉時整支會卡死。建不出暫存目錄也算失敗,不能靜悄悄跳過。

Who:`jsc-hooks:hooks-install` 接線後跑的冒煙測試,以及改動 `sdlc-gate.sh` 歸屬判定的人。
2026-08-27 11:20:28 +08:00
jiantw83 870f52fbaa feat(sdlc-gate): 工作包閘門加上歸屬比對與領取紀錄
What:`hooks/sdlc-gate.sh` 新增 `wp-claim {owner}/{repo} {工作包代號} [{PR 編號}] [{分析頁}]` 與 `wp-unclaim {owner}/{repo}` 兩個子命令,`wp-lock` 新增第四個參數收工作包代號,鎖檔改為純文字 `key=value`,`wp-report` 多印第四欄工作包代號。`wp-check` 逐筆判歸屬:`prompt` 多注入一行點名不屬於這裡的 PR,`skill` 擋下 `plan`、`analyze`、`maintain` 時一併點名,`implement` 仍放行但收到同一則提醒。

Why:同一份分析常有好幾包平行進行,每包各自的 worktree 與 PR。原本的鎖檔只記存取庫與 PR 編號,看不出那支 PR 是誰的,於是任何一個工作階段都可能去改別包的程式碼、回別包的留言。

How:歸屬只比一件事——鎖檔的 `wp` 與同一個存取庫領取檔的 `wp` 取數字比一次,兩邊都有值且不相等就是別包的。`WP-03`、`WP-3`、`3` 先正規化再比,不然同一包的幾種寫法會被當成不同包。任何一邊查不到(沒有分析頁、沒寫代號、領取檔不存在)一律當查無歸屬並放行,理由與 `version-guard.sh` 一致:只擋確定違規,否則會把技能組維護自己鎖死。舊版單行 TSV 鎖檔照樣讀得動,讀出來是查無歸屬,換格式不會讓既有的鎖失效。`implement` 一律放行,連別包的 PR 未結清也放行——結清 PR 正是 `implement` 的步驟,擋它會把流程鎖死。分類迴圈用 here-document 餵資料而不用管線,管線右邊是子 shell,判到的違規會在迴圈結束時全部消失。

Who:跑 SDLC 實作階段的每個工作階段,以及 `jsc-sdlc/tools/wp-gate.sh` 的 `claim`、`lock` 與 `owns`。
2026-08-27 11:20:28 +08:00
admin 9875f917bf Merge pull request 'feat/lang-guard-traditional-chinese-encoding' (#28) from feat/lang-guard-traditional-chinese-encoding into develop
Reviewed-on: #28
2026-08-27 01:58:37 +00:00
jiantw83 667b60c8e9 feat(manifest): 三份 manifest 版本升到 0.2.2
What:三份 manifest 由 0.2.1 升到 0.2.2。

Why:字表擴充後偵測能力大幅改變,本機沒跟著升版,version-guard.sh 就判不出落後,
使用者不會收到更新提示,繼續用覆蓋率只有兩成的舊字表。

How:以 jsc-meta 的 sync-skill-manifest.sh 統一 bump,三份同步成同一個值。

Who:非程式碼輸出的繁中無亂碼檢查。
2026-08-27 09:53:55 +08:00
jiantw83 75fba4715c feat(simplified): 字表擴充到 1006 字並補齊跳過清單
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:非程式碼輸出的繁中無亂碼檢查。
2026-08-27 09:53:55 +08:00
jiantw83 aee2f19699 feat(manifest): 三份 manifest 版本升到 0.2.1
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 支援。
2026-08-27 09:50:03 +08:00
jiantw83 fe3c5723a5 feat(hooks-install): 文件同步到七支 hook
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。
2026-08-27 09:50:03 +08:00
jiantw83 bbad6566f7 feat(wire-cli): 把 lang-guard.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 執行。
2026-08-27 09:50:03 +08:00
jiantw83 e3a9c67781 feat(lang-guard): 新增第七支 hook,強制非程式碼輸出繁中無亂碼
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 使用。
2026-08-27 09:50:03 +08:00
admin 2f74d6473c Merge pull request 'feat/comment-scope-sweep-all-clis' (#27) from feat/comment-scope-sweep-all-clis into develop
Reviewed-on: #27
2026-08-27 01:28:48 +00:00
jiantw83 542f0aab2c feat(manifest): 三份 manifest 版本升到 0.2.0
What:把 plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三份
manifest 的版本由 0.1.9 升到 0.2.0。

Why:0.1.9 已隨前一支 PR 發佈出去,本次新增的 sweep 模式與四支 CLI 的接線
沒有跟著升版,version-guard.sh 就看不出本機落後,使用者不會收到更新提示。

How:以 jsc-meta 的 sync-skill-manifest.sh 統一 bump,minor 進位、patch 歸零,
三份同步成同一個值。

Who:註解範圍檢查 hook 的跨 CLI 支援。
2026-08-27 09:20:44 +08:00
jiantw83 b23c546079 docs(hooks-install): 據實寫明各 CLI 的註解範圍掃描時機
What:`skills/hooks-install/SKILL.md`、`README.md`、`AGENTS.md` 三份文件一併改寫註解範圍的覆蓋範圍說明:`comment-scope.sh` 由兩種模式改為三種,並以表格列出五個 CLI 各自的掃描時機——claude 逐檔即時(PostToolUse)、codex 每輪結束(`notify`)、kiro 每輪提示送出時(`userPromptSubmit`,掃的是上一輪寫的檔)、copilot 與 antigravity 只有工作階段結束時由 `tools/jsc-wrap.sh` 收尾掃一次。README 的 `tools/jsc-wrap.sh` 那列補上收尾 sweep 與「不影響結束碼」的約定,`smoke` 例外說明改成掃描模式通用。

Why:舊文件寫的是「四個 CLI 只剩規則提示」,接上 sweep 之後那句話已經不實。但也不能倒過來寫成五支一樣:時機差一輪或差一整個工作階段,操作者要知道自己現在用的 CLI 什麼時候才會收到警告。文件不同步,操作者會對保護程度有錯誤預期。

How:SKILL.md 全份維持英文,正文改用一張 CLI 對掃描時機的表格,並註明 sweep 讀的是 `git diff HEAD`、涵蓋範圍與 claude 相同、不在 git 工作區內就安靜 exit 0,frontmatter 的 `description` 不動;README.md 維持 STE100 繁中,hook 一覽表那列補上三種模式與各 CLI 時機,原本的降級段落換成同一張表;AGENTS.md 的第 5 條補上三種模式與「不得寫成五支一樣」的要求。

Who:`jsc-hooks` 的文件層與 `hooks-install` 技能,供操作者與後續 sub agent 依循。
2026-08-27 09:16:32 +08:00
jiantw83 0bebdef46f feat(wire-cli): 把 comment-scope sweep 接到其餘四個 CLI
What:`tools/wire-cli.sh` 把 `comment-scope.sh sweep` 接進 codex 的 `config.toml` 根層 `notify` 與 kiro 的 `.kiro/hooks/jsc-hooks.json` 的 `userPromptSubmit`;`tools/jsc-wrap.sh` 在 CLI 結束後、回傳結束碼之前跑一次 sweep,涵蓋 copilot、antigravity 與同樣走包裝器的 codex。`smoke` 多跑一輪 sweep,`status` 多盤點 codex 的 `notify-sweep` 與 kiro 的 `comment-scope-sweep` 兩項。

Why:這四個 CLI 沒有 post-tool hook,接不到逐檔即時掃描,先前只寫得進規則提示,違規註解寫進去沒有人叫。接上 sweep 之後五個 CLI 都掃得到,差別只剩回饋速度。`smoke` 與 `status` 不同步補上,就驗不出來接線少了哪一支。

How:codex 的 notify 串成 `start; mark; sweep`,位置仍由 `replace_block_toml` 保證落在第一個表頭之前,寫入後除了既有的根層鍵檢查,再 grep 一次確認 sweep 真的串進那一行。kiro 的 `run` 尾端接上 sweep,`prompt` 與 `sweep` 各驗一個 grep,只驗腳本名會漏掉少接的那一個。`jsc-wrap.sh` 的 sweep 加 `|| true` 接住 exit 2,包裝器一律原樣回傳 CLI 自己的結束碼——包裝器改掉結束碼,呼叫端的 `cmd && next` 就會誤判。`smoke` 的 exit 2 白名單由「無參數模式」放寬到「所有掃描模式」,因為 sweep 在髒工作區本來就會回 2,那是 hook 正常工作,不是 hook 壞掉。四個 CLI 的 `reason` 與 `[jsc]` 說明改寫成各自真正的掃描時機。

Who:`jsc-hooks` 的接線工具層,供 `jsc-hooks:hooks-install` 與 `jsc-hooks:repair` 呼叫,最終服務 codex、copilot、antigravity、kiro 的使用者。
2026-08-27 09:16:15 +08:00
jiantw83 8ede74e9e2 feat(comment-scope): 新增 sweep 模式,掃整個 git 工作區
What:`hooks/comment-scope.sh` 由兩種模式變三種,新增 `sweep [dir]`:掃整個 git 工作區這次改過的所有檔案,命中就把報告與最多三行證據送到 stderr 並 exit 2,找不到 git 就安靜 exit 0。`prompt` 與無參數單檔掃描兩個模式的判定邏輯一行不動。

Why:只有 claude 有 PostToolUse,拿得到「剛剛寫了哪個檔」。codex、copilot、antigravity、kiro 四個 CLI 都沒有 post-tool 事件,註解範圍檢查在那邊只剩規則提示,違規註解寫進去了不會有人叫。改掃整個工作區的 git diff,時機晚一點,涵蓋範圍一樣。

How:單檔判定抽成 `scan_file()`,兩種掃描模式共用同一份禁止樣式與白名單,不會各自漂移。`sweep` 以 `git rev-parse --show-toplevel` 找庫根,所以在子目錄跑也掃得到整個庫;逐檔報告累積在暫存檔再一次輸出,因為迴圈跑在管線的子行程裡,變數帶不回本 shell。

Who:`jsc-hooks` 的 hook 實作層,供 `tools/wire-cli.sh` 接線給 codex、copilot、antigravity、kiro 四個 CLI 使用。
2026-08-27 09:15:56 +08:00