Author SHA1 Message Date
admin 9c51958d24 Merge pull request '補上規則正文的十六進位色碼允許項,與守門腳本白名單對齊' (#33) from docs/comment-scope/main into develop
Reviewed-on: #33
2026-09-08 07:08:26 +00:00
jiantw83andClaude Opus 5 da361a18e6 chore(version): 三份 manifest 升版至 0.1.4
註解規範正文多了一列允許項,行為跟著變,版號要動。三份 manifest 的版號
必須一致,版本前置檢查是拿它們互相比對的。

由 0.1.3 升到 0.1.4,末位加一。改動只在版號那一行,描述、技能路徑與其他
欄位一個字都沒碰。

影響部署與版本前置檢查:升上去之後,各 CLI 端才抓得到帶新規則的這一版。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 15:04:21 +08:00
jiantw83andClaude Opus 5 87e4555a90 docs(comment-scope): 色碼列進允許項,不再當編號看
「允許寫進註解」那張表加一列:十六進位色碼。理由寫在同一列——它指的是顏
色,不是編號;只有純十進位的才當議題編號看。

守門腳本那一邊已經多剪一條,把井號後面含十六進位字母的色碼先剪掉。正文
是規則的唯一真實來源,腳本剪了一條而正文不動,下一個讀正文的人會以為色
碼仍然違規,於是把註解裡的色碼手動刪掉,或反過來覺得腳本放水。兩邊對不
起來,規則就沒有人信。

補的是一列,不是一段。既有六列的欄位次序照舊,允許項、理由、範例三格都
填滿,範例挑樣式表註解裡標主色那種常見寫法,讀的人一眼看得出這條規則在
講哪種場景。

影響註解範圍的判準本身,也就是所有讀這份正文的人與所有引用它的技能:註
解裡寫色碼從此明文放行,議題編號、需求編號那些照舊禁止。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 15:04:21 +08:00
admin 201038ca0c Merge pull request '收尾寫一筆 skill-end 事件,執行狀態才回報得到助理' (#31) from feat/status-report into develop
Reviewed-on: #31
2026-09-02 08:04:36 +00:00
jiantw83 cd84419317 chore(plugin 版本): 三份 manifest 升版至 0.1.3 2026-09-02 16:01:17 +08:00
jiantw83 d472f81cd2 feat(狀態回報): 收尾寫一筆 skill-end 事件
現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就
中止的技能,在紀錄裡長得一模一樣。

start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾
步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在
原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。

status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜
跳過,回報失敗一律不改變技能自己的結論。
2026-09-02 16:01:17 +08:00
admin 484357feae Merge pull request '放行 jsc-assist 的 marketplace 條目到預設分支' (#29) from chore/marketplace-assist-registry/main into develop
Reviewed-on: #29
2026-09-01 04:55:36 +00:00
admin 06ded45fb1 Merge pull request 'chore/marketplace-assist-registry/sync-copies' (#28) from chore/marketplace-assist-registry/sync-copies into chore/marketplace-assist-registry/main
Reviewed-on: #28
2026-09-01 04:53:51 +00:00
jiantw83 9e34c32d92 chore(marketplace): 把 jsc-assist 登錄進統一 marketplace
What:
- 兩份 marketplace 檔各加一個 jsc-assist 條目,來源網址指向 assist 存放庫。

Why:
- 準則要求每個 domain 存放庫都帶同一份 marketplace 檔,任何一個存放庫都能當註冊入口。副本之間只要有一份沒跟上,稽核就會報出不一致。
- 正本少了這個條目,各 CLI 的安裝指令就找不到 jsc-assist,這個 domain 等於發佈不出去。

How:
- 條目由 meta 的 sync-marketplace.sh 產生,同時寫進正本與每個 domain 存放庫的副本,寫完逐檔比對位元組。這一支存放庫的兩份副本就是那一輪的產物。
- 條目依名稱排序,縮排與非 ASCII 描述的處理都交給同一支腳本,不手改 JSON。
- 這一批是從最新的預設分支重新產生的。前一輪的分支基底早於監控頁型別那批改動,直接合併會把那些改動回退掉,所以整批重做而不是解衝突。

Who:
助理 domain 落地的註冊步驟在這個存放庫的同步。
2026-09-01 12:50:04 +08:00
jiantw83 f017c28d13 Merge pull request '收攏 api-doc 技能的 frontmatter 語法修正' (#25) from feat/cli-hook-rewire/main into develop 2026-09-01 00:58:43 +00:00
jiantw83 2838e6104f Merge pull request '修正 api-doc 技能 SKILL.md frontmatter 的 YAML 純量語法' (#24) from feat/cli-hook-rewire/quote-description into feat/cli-hook-rewire/main 2026-09-01 00:56:12 +00:00
jiantw83 5e7de7a792 fix(frontmatter): 修正 api-doc 技能 SKILL.md frontmatter 的 YAML 純量語法錯誤
What:
- 修正 skills/api-doc/SKILL.md frontmatter 裡 description 欄位的 YAML 語法錯誤。
- 整串 description 加上單引號,內部撇號改寫成兩個單引號,內容文字一個字都沒變。
- 同步更新 plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三個 manifest 版本號,從 0.1.1 進到 0.1.2。

Why:
- description 內含「冒號加空白」,屬於未加引號的 YAML plain scalar,違反 YAML 語法規定。
- Antigravity 解析 frontmatter 時當場中斷,整支技能被靜默丟棄,沒有任何錯誤訊息;磁碟上 34 支技能,Antigravity 只認得 28 支。
- 準則要求 description 用英文撰寫,不能把「: 」改成全形冒號迴避語法問題,只能加引號修正。

How:
- 整串 description 值加上單引號,內部撇號寫成兩個單引號跳脫,其餘字元不動。
- 用 git show HEAD: 取出改前的原始值,把改後的單引號純量還原後做字串相等比對,確認逐字相同、字元數一致。
- 執行 ste100-lint.sh、check-behaviors.sh、lint-frontmatter.sh 三支檢查腳本,退出碼皆為 0;git diff --numstat 顯示只動了 frontmatter 那一行。

Who:
- 本次修到 review 技能組的 api-doc 技能,屬稽核 API 專案 Swagger/OpenAPI 文件的功能。
2026-08-31 19:04:02 +08:00
jiantw83 2e4ce92626 Merge pull request '收攏 review 三支技能的行為清單,功能主幹併回 develop' (#22) from feat/skill-behaviors-and-version-block/main into develop 2026-08-31 08:10:39 +00:00
jiantw83 46cb8fc053 Merge pull request '建立 api-doc、code-review、comment-cleanup 三支技能的行為清單' (#21) from feat/skill-behaviors-and-version-block/behavior-list into feat/skill-behaviors-and-version-block/main 2026-08-31 08:09:26 +00:00
jiantw83 469f4d3445 chore(manifest): 三份 manifest 升版到 0.1.1
What:
把 `.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、`plugin.json` 的版本從 0.1.0 改成 0.1.1。
三份檔案只動 version 一個欄位,其餘內容不變。

Why:
本次新增行為清單,屬於外顯內容的變動。
各 CLI 靠 manifest 版本判斷要不要更新外掛。
版本沒升,安裝端就拿不到新內容。

How:
三份 manifest 一起改成同一個版號。
用 grep 比對三份檔案的 version 欄位,確認完全一致。

Who:
安裝或更新 jsc-review 外掛的每一台機器。
執行 version-guard 版本檢查的流程。
2026-08-31 13:46:35 +08:00
jiantw83 d4fea890c7 docs(review): 新增三支技能的行為清單
What:
在既有的 `references/` 目錄新增 `behaviors.md`。
文件分 api-doc、code-review、comment-cleanup 三節。
每節一張五列表:觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象。

Why:
技能驗證缺一份共同的比對基準。
以前只能重讀技能本文推敲行為,判斷因人而異。
清單放在本 repo,技能改動與清單就落在同一個 PR,不會漂移。
也不用為了一次改動跨 repo 開兩條 PR 互卡。

How:
逐支技能盤點行為,再把結果填進五列表。
稽核時修正 comment-cleanup 的外部呼叫敘述。
原本寫成 write-guard.sh 條件式涵蓋這支技能。
實際上那支腳本一律豁免它,而且這支技能根本不呼叫它,已據實改寫。
格式交由 `meta/tools/check-behaviors.sh` 在程式層檢查。

Who:
jsc-review 技能的維護者。
執行技能驗證的人。
日後異動這三支技能的人,都要同步更新這一頁。
2026-08-31 13:46:35 +08:00
admin 7c8712963f Merge pull request 'fix/skill-check-compliance-and-flow' (#19) from fix/skill-check-compliance-and-flow into develop
Reviewed-on: #19
2026-08-31 03:24:30 +00:00
jiantw83 4d15b618c7 chore(plugin): 提升外掛版號以帶出這批行為變更
- 這批改動含判準變更與兩支新腳本,屬於行為變更,不是純文件修飾。
- claude、codex 與共用的三份描述檔一起帶。版號不一致會讓安裝端拿到舊的技能。
2026-08-31 11:07:05 +08:00
jiantw83 1efebdfd22 docs(review): 說明檔對齊新的技能行為與工具清單
- 三支技能的說明改成與技能文件一致,讀說明檔的人不會拿到舊的判準。
- 補上兩支新腳本的用途、輸入輸出與結束碼。
- 偵測腳本的結束碼說明補上缺 grep 這個原因。
- 刻意移除的那道保護另寫一段提醒:hook 關掉或沒接上時,認可前要先跑註解清理。
2026-08-31 11:07:05 +08:00
jiantw83 ced541faa5 fix(review): 補上註解腳本路徑與缺漏的失敗分支
- 註解範圍腳本的路徑少一層目錄,照著寫會找不到檔案,補上 hooks 那一層。
- 建置測試跑完卻失敗時沒有分支可走,只寫得出「通過」。現在要指出指令、結束碼,並判斷失敗是不是本次改動造成的。
- 取得差異失敗時原本會安靜跳過,現在原樣帶出錯誤並停手。判不出範圍不等於沒有東西要審。
- 禁止清單原本抄了兩份,改成只指向註解範圍的正本,兩份不會各自演化。
- 第二組不再重掃樣式判得出來的項目。hook 關掉或沒接上時就沒有最後一道網,這件事寫進技能文件,不讓它默默消失。
- 差異只算一次,落成一份快照檔給六組共讀;檔名帶執行代號,同一台機器平行跑不會互相覆蓋。註解清理改成一個檔案一個 sub agent 平行跑。
2026-08-31 11:07:05 +08:00
jiantw83 f6cd1f9ef7 feat(api-doc): 範例只掛純量成員,類別型往下遞迴
- 使用者要求改判準。類別型參數與中間層的類別屬性只留說明,範例責任往下推給屬性,一路走到最內層的純量。
- 一個值只有一個出處。範例掛在父層,屬性一改就過期,讀的人拿到的是沒有屬性定義背書的一份資料。
- 集合看元素型別,不看外殼;字典看值型別。字串集合與字串判出來一樣,位址集合與位址判出來也一樣。
- 壞味道清單原本寫「輸入與輸出參數都必須有範例」,與新判準衝突,兩支技能會對同一段程式碼給出不同標準。一併改成同一套。
- 說明與範例的兩份檢核表併成一個 sub agent,同一批資料模型檔只讀一次。
- 順帶補上偵測腳本結束碼二漏掉的一個原因:環境缺 grep。原本只寫參數與路徑,遇到這個原因換路徑重跑永遠清不掉。
2026-08-31 11:07:05 +08:00
jiantw83 a0bd488ac8 feat(tools): 新增發現合併與變更註解兩支腳本
- 合併、去重、排序原本寫在兩支技能的步驟文字裡,各寫一份就會各自演化。改由腳本執行,並定下六欄 TSV 的回報格式。格式不對就擋下並指出是第幾列,不猜、不放行、不自行補欄。
- 另一支腳本列出本次改到的註解行,清理範圍不再靠肉眼判讀。
- 註解樣式與非程式碼副檔名清單沿用註解範圍腳本的同一份,不另立第二套判準。markdown 的標題行開頭就是井字號,判準不一致就會把整份文件當成註解。
2026-08-31 11:07:05 +08:00
admin 168113e429 Merge pull request 'feat/plugin-dependencies/main' (#17) from feat/plugin-dependencies/main into develop
Reviewed-on: #17
2026-08-28 04:05:16 +00:00
admin 554796aa22 Merge pull request 'feat/plugin-dependencies/declare-requires' (#16) from feat/plugin-dependencies/declare-requires into feat/plugin-dependencies/main
Reviewed-on: #16
2026-08-28 04:03:17 +00:00
jiantw83 b60e8c952e feat(manifest): 宣告 review 技能相依版本 2026-08-28 11:59:16 +08:00
admin 219259c354 Merge pull request 'feat/change-requests/main' (#14) from feat/change-requests/main into develop
Reviewed-on: #14
2026-08-28 01:51:20 +00:00
admin c1d8162066 Merge pull request 'feat/change-requests/comment-cleanup-skill' (#13) from feat/change-requests/comment-cleanup-skill into feat/change-requests/main
Reviewed-on: #13
2026-08-28 01:47:03 +00:00
jiantw83 b3d8337001 feat(comment-cleanup): 新增註解清理技能 2026-08-28 09:30:37 +08:00
jiantw83 aaa0227464 feat(comment-scope): 擴充審查流程痕跡規則 2026-08-28 09:30:33 +08:00
admin 5a2d5d1bab Merge pull request 'feat(review): 新增 API 文件稽核技能,第 5 組擴充註解的條列、導向與標示規則' (#11) from feat/api-doc-audit/main into develop
Reviewed-on: #11
2026-08-27 08:54:28 +00:00
admin 8eb330a62f Merge pull request 'feat(review): 新增 API 文件稽核技能,第 5 組擴充註解的條列、連結與標示規則' (#10) from feat/api-doc-audit/api-doc-and-comment-rules into feat/api-doc-audit/main
Reviewed-on: #10
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-27 07:45:15 +00:00
jiantw83 8b5cc602dc chore(manifest): 三份 manifest 同步升版至 0.0.6
What:plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三份
manifest 的 version 由 0.0.5 改為 0.0.6。

Why:這批新增了 api-doc 技能與 swagger-detect.sh,也擴充了第 5 組的審查項目,
屬於功能異動。版本沒跟著升,各 CLI 端的外掛版本護欄就分不出新舊,已安裝的
使用者也收不到更新。

How:三份 manifest 只改 version 欄位,其餘欄位維持原樣,三處版本號保持一致。

Who:jsc-review 外掛的安裝與更新流程。
2026-08-27 15:41:38 +08:00
jiantw83 51c2705667 feat(review): code-review 範圍改為 5.1 到 5.8 並補上與 api-doc 的分工
What:skills/code-review/SKILL.md 的審查範圍表,第 5 組由原本一句
「smells.md group 5」改成明確的 5.1 到 5.8,並點名 5.6、5.7、5.8 三個新項目;
Notes 加一行分工,Swagger 文件稽核歸 api-doc。README.md 新增 api-doc 的技能
小節,另補一張工具表格列出 swagger-detect.sh。

Why:sub agent 讀的是 SKILL.md 的審查範圍表,範圍表沒寫清楚,新增的 5.6 到
5.8 就不會被查。使用者讀的是 README.md,新技能與新工具沒列出來就找不到。
兩支技能都碰註解與文件,界線不寫明就會對同一個缺失重複回報。

How:範圍表的第 5 組直接列出三個新項目與各自的判準重點,完整清單仍指向
references/smells.md。Notes 寫明 api-doc 負責狀態碼的回覆類型、Swagger 參數
說明與範例,第 5 組只負責原始碼註解契約。README.md 的技能小節寫明偵測先行、
只裝套件沒掛接就停手;工具表格列出 swagger-detect.sh 的用途、輸出欄位與結束碼。

Who:jsc-review 的 code-review 技能與存取庫說明文件。
2026-08-27 15:41:38 +08:00
jiantw83 dfaa66feda feat(api-doc): 新增 API 文件稽核技能
What:新增 skills/api-doc/SKILL.md。六步流程:偵測 Swagger 支援、不支援就回報
並停手、列出稽核範圍、分三個面向各開一個 sub agent 稽核、彙整去重排序、回報
發現。三個面向分別是狀態碼的回覆類型、參數說明與範例、資料模型遞迴。本技能
只回報「檔案:行號」、嚴重度與建議修法,不改程式碼。

Why:支援 Swagger 的專案要把控制器文件補全:所有可能出現的狀態碼都宣告回覆
類型,輸入輸出都要有說明與真實資料範例,參數是資料模型就每個屬性都套用、內含
模型再往下遞迴。這是 code-review 六組沒碰過的領域,跟第 5 組的原始碼註解契約
也不是同一件事,所以獨立成一支技能,不塞進既有的六組裡。

How:第一步跑 tools/swagger-detect.sh,讀結束碼決定走下去還是停手,不支援時
一個 sub agent 都不開,直接回報未啟用。範例優先取專案的真實資料,資料庫、
種子資料、測試夾具都算;真的取不到才依邏輯推導,並在文件裡標上「推導值」,
讓後面讀的人知道這個值沒被觀察過。取自資料庫的範例一律去識別化,個人資料不
進 Swagger 文件。分工另立一節:本技能只管 Swagger 文件屬性,原始碼註解契約
歸 code-review 第 5 組,同一個缺失不會被回報兩次。

Who:jsc-review 新增的 api-doc 技能,由 jsc-sdlc 的 implement 收尾時呼叫。
2026-08-27 15:41:38 +08:00
jiantw83 57ac9cdcd9 feat(swagger-detect): 新增判斷專案有沒有啟用 Swagger 的工具
What:新增 tools/swagger-detect.sh,判斷一個專案有沒有真的啟用 Swagger 文件。
涵蓋 dotnet、nodejs、python 三種技術棧,輸出 support=、stack=、package=、
config= 四類欄位;結束碼 0 支援、1 不支援、2 參數個數不對或專案路徑不存在。

Why:API 文件稽核只對產得出 Swagger 文件的專案有意義。支不支援如果交給 agent
自己看程式碼判斷,同一個專案可能這次說支援、下次說不支援。判定寫成腳本,呼叫端
讀結束碼分支就好,不必自己猜。

How:雙重確認,套件與設定缺一不算支援。第一關在套件宣告檔裡找已知的 Swagger
套件,第二關在原始碼裡找真的把 Swagger 接上去的呼叫或裝飾子。設定關鍵字一律
挑接線動作,不挑 import 或 require——光是引入套件不代表文件真的掛上去了。裝了
套件卻沒啟用的專案很常見,只看套件會誤判,讓稽核跑在一個根本產不出文件的專案
上。輸出刻意做成一行一個 key=value,呼叫端逐行讀就好。

Who:jsc-review 的 api-doc 技能,以及 jsc-sdlc 實作階段的收尾稽核。
2026-08-27 15:41:38 +08:00
jiantw83 dbaf81005b feat(smells): 第 5 組擴充條列式步驟、導向連結與標示語法
What:references/smells.md 第 5 組擴充。5.1 的方法描述後面要接條列式的處理步驟
與規則,5.3 的回傳值是自訂資料模型時要附上型別定義的導向連結;新增 5.6 內含
功能的導向連結、5.7 XML 註解標籤各占一行、5.8 註解裡的專有名詞與變數依語言
慣例標示。文末的嚴重度分級補上一張表,逐項標明這五個新項目的級別與理由。

Why:使用者提出的產出物文件品質規則裡,有三條講的都是原始碼註解要寫到什麼
程度。動手前先比對過既有內容,第 5 組已經涵蓋大部分,缺的是條列步驟、導向
連結與標示語法這三塊。另開一支技能會跟 code-review 的目標重疊,所以直接擴充
第 5 組。新項目多半屬於可讀性層級,混在原本「註解缺漏」一句話裡分不出輕重,
所以級別另外列。

How:5.1 與 5.3 在原有定義後面接上新要求,偵測訊號與建議重構手法同步補列,
原本的判準一個都不動。5.6 到 5.8 照既有小節的四段結構寫:定義、偵測訊號、
建議重構手法、注意事項。5.8 的標示語法用表格對照 XML 與 JSDoc、docstring
兩類格式。5.6 與 5.7 都寫明註解格式不支援時不適用,避免硬造連結字串,也避免
把 XML 的排版規則套到沒有結束標籤的格式上。

Who:jsc-review 的壞味道參考清單,第 5 組註解契約。
2026-08-27 15:41:38 +08:00
admin 8b8f23a17b Merge pull request 'feat/comment-scope-rule' (#8) from feat/comment-scope-rule into develop
Reviewed-on: #8
2026-08-27 00:56:41 +00:00
jiantw83 0df99db512 feat(review): 三份 manifest 同步升版至 0.0.5
What:plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json
三份 manifest 的 version 由 0.0.4 改為 0.0.5。

Why:本次新增了註解內容界線規則,屬於功能異動。版本沒跟著升,各 CLI 端的
外掛版本護欄就分不出新舊,已安裝的使用者也收不到更新。

How:三份 manifest 只改 version 欄位,其餘欄位維持原樣,三處版本號保持一致。

Who:jsc-review 外掛的安裝與更新流程。
2026-08-26 19:00:38 +08:00
jiantw83 df81eec8fb feat(review): 技能與說明文件納入文件編號夾帶審查
What:skills/code-review/SKILL.md 的第 2 組 Obscurity 審查範圍納入 2.5,
Notes 補上與 jsc-hooks 的 comment-scope.sh 分工說明;README.md 的技能說明
與參考檔表格同步。

Why:規則正文放進 references 還不夠,sub agent 讀的是 SKILL.md 的審查範圍表。
範圍表沒寫,第 2 組就不會查這一項。使用者讀的是 README.md,參考檔沒列出來就找不到。

How:審查範圍表的第 2 組直接列出六類命中樣式,並指向 references/comment-scope.md
取完整清單。Notes 寫明 comment-scope.sh 負責樣式判定得了的項目,第 2 組負責
樣式判定不了的專案代號與客戶名稱,兩邊不重複回報。README.md 表格新增一列。

Who:jsc-review 的 code-review 技能與存取庫說明文件。
2026-08-26 19:00:38 +08:00
jiantw83 fd70ce3bc2 feat(review): 新增程式碼註解禁止夾帶文件資訊的規則正文
What:新增 references/comment-scope.md 規則正文,並在 references/smells.md
第 2 組加入 2.5 文件編號夾帶,嚴重度分級「低」列補上本項。

Why:註解寫「這件事記在哪份文件」,讀程式碼的人查不到。編號會過期、會搬家、
會落在存取權限外,最後只剩一串無意義的代號。註解該寫的是「為什麼這樣寫」。

How:規則正文限定適用範圍只到程式碼註解,docstring、README、commit 訊息不受限。
禁止清單三十項分四組:追蹤系統編號、jsc wiki 頁面編號、需求與規格編號、
流程與人事資訊。白名單七項:日期與時間戳、需求變更歷程、RFC 與 ISO 標準、CVE、
第三方套件 issue 連結、授權標頭與 SPDX、語言原生標記。

Who:jsc-review 的 code-review 技能,第 2 組可讀性審查。
2026-08-26 19:00:38 +08:00
admin 1990476b25 Merge pull request 'fix/skillset-audit-compliance-and-guard-fixes' (#7) from fix/skillset-audit-compliance-and-guard-fixes into develop
Reviewed-on: #7
2026-08-25 07:15:21 +00:00
jiantw83andClaude Opus 5 150f0cc07c chore(review): 三份 manifest 同步升版並同步 marketplace 正本
What:三份 plugin manifest 版本同步 bump,兩份 marketplace 檔與 plugins/meta 正本對齊。

Why:準則要求技能異動必須同步升版;marketplace 副本必須與正本完全一致。

How:以 jsc-meta 的 tools/sync-skill-manifest.sh 升版,marketplace 檔由正本複製。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
jiantw83andClaude Opus 5 d9d0f6936f docs(review): 同步文件與參考資料
What:更新 README、AGENTS.md、templates 與 references,讓文件敘述與實際行為一致。

Why:稽核發現多處文件與程式行為分歧,違反「每個意義只有單一真實來源」。

How:以實際程式行為為準改寫敘述,重複的規則收成單一來源並以一行指引指過去。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
jiantw83andClaude Opus 5 9313dea6ee fix(review): 補齊稽核缺失並修掉護欄失效
What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、
把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。

Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。
完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。

How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有
documented exit codes,並以真實執行驗證每條路徑。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:58:54 +08:00
admin f0f6d049ca Merge pull request '發佈 jsc-review 0.0.2:marketplace 正本移至 plugins/meta' (#6) from develop into master
Reviewed-on: #6
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 10:19:47 +00:00
admin 51210bc42d Merge pull request 'chore(marketplace 正本): 正本移至 plugins/meta(升版 0.0.2)' (#5) from chore/marketplace-canon-to-meta into develop
Reviewed-on: #5
2026-08-24 10:14:11 +00:00
jiantw83andClaude Opus 5 a78b990344 chore(plugin 版本): 三份 manifest 升版至 0.0.2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 18:00:35 +08:00
jiantw83andClaude Opus 5 ba2d61667f docs(README): 安裝入口改為 plugins/meta 並補上遷移說明
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 18:00:35 +08:00
admin 70fde94290 Merge pull request '發佈 jsc-review:marketplace 同步(尚未升版,見 PR 說明)' (#4) from develop into master
Reviewed-on: #4
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 08:28:15 +00:00
admin 595270eaac Merge pull request 'fix/sync-marketplace-registry' (#3) from fix/sync-marketplace-registry into develop
Reviewed-on: #3
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-24 07:58:20 +00:00
jiantw83 6f3d99182c fix(marketplace): 同步 marketplace.json 至 canonical 版本
What: 更新 .claude-plugin/marketplace.json 與 .agents/plugins/marketplace.json 兩份 marketplace 登錄檔,補齊缺漏或不一致的 description 與 owner 欄位。
Why: 兩份登錄檔與 canonical 的 plugins/jsc marketplace.json 不一致,.agents/plugins 版本缺少多個外掛的 description 與 owner 欄位,.claude-plugin 版本有一處用字不一致(「新建/更新/刪除」應為「新建、更新、刪除」)。
How: 直接以修正後的 canonical plugins/jsc marketplace.json 內容覆蓋這兩份登錄檔,使三份檔案完全一致。
Who: jsc-meta:skill-check 例行合規稽核 — jsc-review 網域自身技能(code-review)已零缺失通過稽核,僅剩共用 marketplace 登錄檔需要同步。
2026-08-24 14:47:59 +08:00
admin 2aad771dad Merge pull request 'feat(review): 匯入 jsc-review 技能組並統一 marketplace 為 jsc' (#2) from develop into master
Reviewed-on: #2
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
2026-08-21 06:41:16 +00:00
jiantw83andClaude Fable 5 0a5ad4f75f chore(review): 加入統一 jsc marketplace 副本(與正本一致,任一 repo 可作註冊入口)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:37:50 +08:00
jiantw83andClaude Fable 5 3692c9e915 style(review): 中文並列改頓號
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:29:47 +08:00
jiantw83andClaude Fable 5 ccf83badbb docs(review): translate SKILL.md into English per guidelines
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:29:47 +08:00
jiantw83andClaude Fable 5 2c03779e41 style(review): 依 STE100 擬人台灣感規則改寫語感
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:17:32 +08:00
jiantw83andClaude Fable 5 5896f09f68 docs(review): AGENTS 語言規則改指向 jsc-meta 的 references/ste100.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:13:27 +08:00
16 changed files with 1046 additions and 58 deletions
+97
View File
@@ -0,0 +1,97 @@
{
"name": "jsc",
"description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。",
"owner": {
"name": "JSC"
},
"plugins": [
{
"name": "jsc-ask",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
},
"description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)"
},
{
"name": "jsc-assist",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/assist.git"
},
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_* wiki 頁)"
},
{
"name": "jsc-cli",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/cli.git"
},
"description": "CLI 偵測、模型能力標籤與技能庫批次部署"
},
{
"name": "jsc-git",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/git.git"
},
"description": "Commit 分組認可與 Push Request 建立"
},
{
"name": "jsc-gitea",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/gitea.git"
},
"description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步"
},
{
"name": "jsc-hooks",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
},
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
},
{
"name": "jsc-log",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/log.git"
},
"description": "工作日誌(LOG_* wiki 頁)與技能使用統計"
},
{
"name": "jsc-meta",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
},
"description": "技能組自我管理:新建、更新、刪除技能與技能準則"
},
{
"name": "jsc-pkg",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/pkg.git"
},
"description": "套件批次更新(nodejs/python/dotnet),失敗還原"
},
{
"name": "jsc-review",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
},
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
},
{
"name": "jsc-sdlc",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
},
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
}
]
}
+97
View File
@@ -0,0 +1,97 @@
{
"name": "jsc",
"description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。",
"owner": {
"name": "JSC"
},
"plugins": [
{
"name": "jsc-ask",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
},
"description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)"
},
{
"name": "jsc-assist",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/assist.git"
},
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_* wiki 頁)"
},
{
"name": "jsc-cli",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/cli.git"
},
"description": "CLI 偵測、模型能力標籤與技能庫批次部署"
},
{
"name": "jsc-git",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/git.git"
},
"description": "Commit 分組認可與 Push Request 建立"
},
{
"name": "jsc-gitea",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/gitea.git"
},
"description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步"
},
{
"name": "jsc-hooks",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
},
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
},
{
"name": "jsc-log",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/log.git"
},
"description": "工作日誌(LOG_* wiki 頁)與技能使用統計"
},
{
"name": "jsc-meta",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
},
"description": "技能組自我管理:新建、更新、刪除技能與技能準則"
},
{
"name": "jsc-pkg",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/pkg.git"
},
"description": "套件批次更新(nodejs/python/dotnet),失敗還原"
},
{
"name": "jsc-review",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
},
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
},
{
"name": "jsc-sdlc",
"source": {
"source": "url",
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
},
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
}
]
}
+7 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.1", "version": "0.1.4",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills", "skills": "./skills",
"author": { "author": {
@@ -13,5 +13,10 @@
"review", "review",
"skills", "skills",
"cross-tool" "cross-tool"
] ],
"jsc": {
"requires": {
"jsc-hooks": ">=0.2.8"
}
}
} }
+7 -2
View File
@@ -1,6 +1,11 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.1", "version": "0.1.4",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills" "skills": "./skills",
"jsc": {
"requires": {
"jsc-hooks": ">=0.2.8"
}
}
} }
+2 -2
View File
@@ -1,10 +1,10 @@
# jsc-review — 給 AI 助理的指引 # jsc-review — 給 AI 助理的指引
本 repo 是 jsc 技能組的 `review` domain(程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 本 repo 是 jsc 技能組的 `review` domain(程式碼審查:Refactoring 壞味道六組、註解規範、淺模組),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。
## 規則 ## 規則
1. 所有交談與輸出內容基於 STE100 使用繁體中文:短句、一句一指令、主動語態、術語一致、UTF-8 無亂碼。 1. 所有交談與輸出內容使用 STE100 繁體中文,帶擬人台灣感:短句、一句一指令、台灣用語、全形標點、去 AI 味、直接講重點。完整規則的唯一來源:`plugins/meta` 的 `references/ste100.md`。
2. 技能位於 `skills/{name}/SKILL.md`;處理任務前先比對需求與各技能的 `description`,相符就載入並依其步驟執行。 2. 技能位於 `skills/{name}/SKILL.md`;處理任務前先比對需求與各技能的 `description`,相符就載入並依其步驟執行。
3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。 3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。
4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。 4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。
+28 -7
View File
@@ -2,20 +2,22 @@
jsc 技能組的 review domain:基於《Refactoring》壞味道目錄的六組審查(結構與體積、可讀性與命名、耦合與設計、邏輯與壞習慣、註解規範、淺模組),套用在檔案變更完成與實作完成兩個時機。 jsc 技能組的 review domain:基於《Refactoring》壞味道目錄的六組審查(結構與體積、可讀性與命名、耦合與設計、邏輯與壞習慣、註解規範、淺模組),套用在檔案變更完成與實作完成兩個時機。
## 安裝 / 更新 / 移除 ## 安裝、更新、移除
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安裝 token 為 `jsc-review@jsc`。每個指令一行: Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 `jsc-review@jsc`。每個指令一行:
| CLI | 安裝 | 更新 | 移除 | | CLI | 安裝 | 更新 | 移除 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && claude plugin install jsc-review@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-review@jsc` | `claude plugin uninstall jsc-review@jsc` | | claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && claude plugin install jsc-review@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-review@jsc` | `claude plugin uninstall jsc-review@jsc` |
| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && codex plugin add jsc-review@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-review@jsc` | | codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && codex plugin add jsc-review@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-review@jsc` |
| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && copilot plugin install jsc-review@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-review@jsc` | `copilot plugin uninstall jsc-review@jsc` | | copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && copilot plugin install jsc-review@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-review@jsc` | `copilot plugin uninstall jsc-review@jsc` |
| antigravity | `git clone https://gitea.jsc.idv.tw/plugins/review.git ~/plugins/review && agy plugin install ~/plugins/review` | `git -C ~/plugins/review pull && agy plugin uninstall jsc-review && agy plugin install ~/plugins/review` | `agy plugin uninstall jsc-review` | | antigravity | `git clone https://gitea.jsc.idv.tw/plugins/review.git ~/plugins/review && agy plugin install ~/plugins/review` | `git -C ~/plugins/review pull && agy plugin uninstall jsc-review && agy plugin install ~/plugins/review` | `agy plugin uninstall jsc-review` |
| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/jsc.git && kiro-cli plugin install jsc-review@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-review@jsc` | `kiro-cli plugin uninstall jsc-review@jsc` | | kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && kiro-cli plugin install jsc-review@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-review@jsc` | `kiro-cli plugin uninstall jsc-review@jsc` |
> antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。 > antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。
> 舊入口 `plugins/jsc` 已移除,marketplace 正本移到 `plugins/meta`。marketplace 名稱仍是 `jsc`(取自 marketplace.json 的 `name` 欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行 `claude plugin marketplace remove jsc`,再依上表重新 add。
## Skills 目錄 ## Skills 目錄
呼叫方式:Claude / Antigravity `/jsc-review:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。 呼叫方式:Claude / Antigravity `/jsc-review:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
@@ -24,7 +26,17 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
### `code-review` ### `code-review`
對 git diff 進行六組壞味道審查,每組一個 sub agent 平行執行;回報 `檔案:行號` + 嚴重度 + 建議重構手法,修正與否由呼叫端決定。安全性與 bug 審查交給 CLI 內建 review,不重複。 對 git diff 進行六組壞味道審查,每組一個 sub agent 平行執行;回報 `檔案:行號`、嚴重度、建議重構手法,修正與否由呼叫端決定。步驟 1 先把 diff 落成一份快照檔,六組讀同一份,git 只算一次,六組的結論也一致。第 2 組的註解範圍只管樣式判不出來的部分:專案代號、客戶名稱與情境相關的審查痕跡;禁止清單與允許清單的唯一來源是 `references/comment-scope.md`,這裡不再抄一份。樣式判得出來的項目由 `jsc-hooks/hooks/comment-scope.sh` 擋。diff 是空的就直接回報「無發現」,不開任何 sub agent;六組全部回覆才交給 `tools/merge-findings.sh` 彙整,沒東西可報的那組也要回「無發現」。安全性與 bug 審查交給 CLI 內建 review,不重複。
> 刻意移除的保護:第 2 組本來連樣式判得出來的項目也重掃一遍,所以 `JSC_COMMENT_SCOPE=off` 或該 CLI 沒接上 `comment-scope.sh` 時,第 2 組是最後一道網。現在那道網沒有了。hook 關掉或沒接線時,議題編號、wiki 頁編號、工作包編號、commit hash 留在註解裡不會有人擋,commit 前請改跑 `/jsc-review:comment-cleanup`。
### `api-doc`
稽核 API 專案的 Swagger 文件:每個可能回傳的 HTTP 狀態碼都要宣告回覆類型,每個輸入與輸出都要有說明;範例只掛在純量成員上,類別型成員只留說明,範例責任往下推給它的屬性,一路遞迴到最內層的純量。集合看元素型別判斷:元素是純量就比照純量,附一份列出幾個元素的範例;元素是類別就比照類別,只留說明,遞迴改走進元素型別。控制器改完或實作完成時執行,例如由 `jsc-sdlc:implement` 呼叫,與 `jsc-review:code-review` 並列為兩關收尾稽核。先跑 `tools/swagger-detect.sh` 偵測,套件與掛接設定要雙重命中才算支援;只裝套件沒掛接就回報未啟用並停手,不開任何 sub agent。稽核分兩個面向,各一個 sub agent 平行執行:狀態碼一個,說明與範例連同巢狀資料模型的遞迴合成一個,同一批資料模型檔只讀一次,兩份檢核表分段列出。兩個面向都回覆才交給 `tools/merge-findings.sh` 彙整。原始碼的註解契約歸 `jsc-review:code-review` 第 5 組,這支只管 Swagger 文件屬性與範例,兩支不重複回報。回報 `檔案:行號`、嚴重度與建議修法,本技能不改程式碼。
### `comment-cleanup`
清理本次變更新增或修改的註解,把文件追蹤資訊與審查流程痕跡移除,只留下程式邏輯的實質理由。使用者要求「清一下註解」、「不要留 review 痕跡」,或 commit 前發現註解夾帶流程資訊時執行。判斷準則只看 `references/comment-scope.md`,本技能負責清理,`comment-scope.sh` 負責擋,`code-review` 第 2 組負責指出人工判讀案例。改寫每個檔案一個 sub agent 平行跑,建置與測試留到最後統一跑一次。未明確要求時不動未變更的舊註解。
<!-- JSC-SKILLS:END --> <!-- JSC-SKILLS:END -->
@@ -33,6 +45,15 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
| 檔案 | 用途 | | 檔案 | 用途 |
| --- | --- | | --- | --- |
| `references/smells.md` | 六組壞味道完整清單:定義、偵測訊號、建議重構手法、嚴重度分級;範例資料必須去識別化 | | `references/smells.md` | 六組壞味道完整清單:定義、偵測訊號、建議重構手法、嚴重度分級;範例資料必須去識別化 |
| `references/comment-scope.md` | 程式碼註解內容界線:禁止寫進註解的文件編號、審查流程痕跡、允許項目與白名單、命中時的改法 |
## 工具
| 檔案 | 用途 |
| --- | --- |
| `tools/swagger-detect.sh` | 判斷專案有沒有真的啟用 Swagger。套件與設定雙重確認,缺一不算支援。輸出 `support=`、`stack=`、`package=`、`config=`;結束碼 0 支援、1 不支援、2 參數個數不對、路徑不存在或環境缺 grep |
| `tools/merge-findings.sh` | 合併 `code-review` 六組與 `api-doc` 兩個面向的發現。輸入是六欄 TSV(檔案、行號、嚴重度、組別、發現名稱、說明),同一個檔案與行號的發現併成一列,再依 高 → 中 → 低 排序。結束碼 0 有發現、1 無發現、2 參數或環境錯誤、3 輸入格式錯誤 |
| `tools/changed-comments.sh` | 列出本次變更新增或修改的註解行,供 `comment-cleanup` 決定清理範圍。不給參數比對工作區與 HEAD,給基準版本則比對 `{base}...HEAD`。輸出三欄 TSV(檔案、行號、內容);註解樣式與非程式碼副檔名清單沿用 `jsc-hooks/hooks/comment-scope.sh` 的同一份。結束碼 0 有結果、1 無結果、2 參數或環境錯誤 |
## 相關 domain ## 相關 domain
+7 -2
View File
@@ -1,6 +1,11 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.1", "version": "0.1.4",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills/" "skills": "./skills/",
"jsc": {
"requires": {
"jsc-hooks": ">=0.2.8"
}
}
} }
+33
View File
@@ -0,0 +1,33 @@
# jsc-review 技能行為清單
本頁記錄 jsc-review 每支技能的行為基準,供技能驗證比對。技能異動時,在同一個 PR 內一起更新這一頁。
## api-doc
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 控制器改完之後叫用,或工作包實作完成之後叫用。`jsc-sdlc:implement` 在收尾時與 `jsc-review:code-review` 並排叫用它。專案沒有啟用 Swagger 就不跑,這一點由步驟 1 的腳本判定,不由人判斷。原始碼註解合約歸 `jsc-review:code-review` 第 5 組,這支技能只看 Swagger 文件屬性。資安、邏輯錯誤、測試涵蓋率歸 CLI 內建審查。 |
| 關鍵步驟 | 跑 `tools/swagger-detect.sh` 偵測 Swagger 套件與掛載,讀退出碼 0、1、2 分流、退出碼 1 回報「本專案未啟用 Swagger 文件,略過 API 文件稽核」並停止、退出碼 2 照三種原因回報並停止、列出稽核範圍(全專案控制器,或 `git diff` 指定的變更控制器)、`git diff` 失敗就原文回報 git 的 stderr 並停止、開兩個 sub agent 平行稽核兩個面向(面向 1 狀態碼與回應型別宣告、面向 2 描述與範例並遞迴走訪資料模型)、每個面向回傳六欄 TSV 或「無發現」、把兩個面向的 TSV 依序餵給 `tools/merge-findings.sh` 合併、依退出碼 0、1、2、3 分流、繁體中文逐列回報、跑 `jsc-hooks/hooks/write-guard.sh release` 解鎖、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-review:api-doc {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過。 |
| 外部呼叫 | `tools/swagger-detect.sh`、`tools/merge-findings.sh`、`jsc-hooks/hooks/write-guard.sh`(`review` 模式擋寫入工具、`release` 模式解鎖)、`jsc-hooks/tools/report-status.sh skill-end`(收尾狀態事件)、`git diff`、`references/smells.md` 的嚴重度分級、2 個稽核 sub agent。沒有 Gitea API 呼叫。 |
| 完成條件 | 偵測結果、範圍清單、兩個面向的回傳、合併結果四段都有結論。合併清單每個位置只出現一次,或整輪以「無發現」收尾,或以腳本錯誤收尾。報告交給呼叫方,`release` 指令已經執行,報告內說明 `JSC_WRITE_GUARD_TTL` 逾時解鎖與 `JSC_WRITE_GUARD=off` 兩條退路,修不修由呼叫方決定。收尾一定要寫一筆 `skill-end` 狀態事件:兩個面向都回來且合併清單交出去是 `ok`(`merge-findings.sh` 回 1 的「無發現」也是 `ok`,那是稽核過而沒東西可報),偵測不到 Swagger 而跳過是 `blocked`(偵測腳本回 2、`git diff` 被拒也歸這裡),解鎖沒清掉 `lastskill` 是 `degraded`,`merge-findings.sh` 回 2 或連兩次回 3 是 `failed`,控制器清單是空的、根本沒有可審的變更是 `aborted`。 |
| 可驗證跡象 | 不改任何程式碼。唯一的環境寫入是解鎖:`$JSC_HOME/sessions/{sid}.lastskill` 被清掉,可以直接檢查該檔是否還在。舊版 `jsc-hooks` 不認得 `release`,該檔會留到逾時,這也是可檢查的狀態。另一個寫入跡象是狀態事件:跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-review:api-doc`,`status` 與 `exit` 兩欄對得上上一列講的判準;沒有 Swagger 那一輪看得到 `blocked`,沒有可審變更那一輪看得到 `aborted`,兩者和稽核過而無發現的 `ok` 一眼分得開;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完。除這兩處外沒有寫入跡象,只有回報內容。 |
## code-review
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 一個檔案或一組相關檔案改完之後叫用,或工作包的所有待辦做完之後叫用。`jsc-sdlc:implement` 在工作包最後一項待辦之後、提交與開 PR 之前叫用它。資安、邏輯錯誤、測試涵蓋率不在範圍內,那些歸 CLI 內建審查。Swagger 文件稽核歸 `jsc-review:api-doc`,兩支技能不重複回報同一個缺口。 |
| 關鍵步驟 | 用一道指令把 `git diff`(未提交變更)或 `git diff {base}...HEAD`(比對基底分支)導進 `${TMPDIR:-/tmp}/jsc-code-review.$$.diff` 快照檔並印出展開後的路徑、git 非零退出碼就原文回報 stderr 並停止、快照檔空的就刪檔並回報「無發現」、開六個 sub agent 平行審查六組氣味(1 膨脹、2 晦澀、3 耦合、4 冗贅與其他、5 註解合約、6 淺模組)、六組都只讀步驟 1 的同一份快照、每組回傳六欄 TSV 或「無發現」、六組全數回傳後才把 TSV 依組序餵給 `tools/merge-findings.sh`、依退出碼 0、1、2、3 分流、繁體中文逐列回報、刪掉快照檔、跑 `jsc-hooks/hooks/write-guard.sh release` 解鎖、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-review:code-review {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過。 |
| 外部呼叫 | `git diff`、`tools/merge-findings.sh`、`jsc-hooks/hooks/write-guard.sh`(`review` 模式擋寫入工具、`release` 模式解鎖)、`references/smells.md`、`references/comment-scope.md`(第 2 組的禁列與允列來源)、`jsc-hooks/tools/report-status.sh skill-end`(收尾狀態事件)、6 個審查 sub agent。沒有 Gitea API 呼叫。 |
| 完成條件 | 六組全部回傳,合併清單每個位置只出現一次,或整輪以「無發現」收尾,或以 git 或腳本的錯誤收尾。報告交給呼叫方,快照檔已刪除,`release` 指令已經執行,報告內說明 `JSC_WRITE_GUARD_TTL` 逾時解鎖與 `JSC_WRITE_GUARD=off` 兩條退路。修不修由呼叫方決定,實作流程內高與中通常必修,低看情況。收尾一定要寫一筆 `skill-end` 狀態事件:六組都回來且合併清單交出去是 `ok`(`merge-findings.sh` 回 1 的「無發現」也是 `ok`),`git diff` 被拒、審查範圍根本建不出來是 `blocked`,快照檔沒刪掉或解鎖沒清掉 `lastskill` 是 `degraded`,`merge-findings.sh` 回 2 或連兩次回 3 是 `failed`,快照檔是空的、沒有可審的變更是 `aborted`。 |
| 可驗證跡象 | 不改任何程式碼。過程中產生 `${TMPDIR:-/tmp}/jsc-code-review.$$.diff` 快照檔,收尾時刪除,跑完該檔不應該還在,殘留就代表沒收尾。解鎖後 `$JSC_HOME/sessions/{sid}.lastskill` 被清掉,可直接檢查。第三個檔案跡象是狀態事件:跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-review:code-review`,`status` 與 `exit` 兩欄對得上上一列講的判準;空 diff 那一輪看得到 `aborted`,和六組審完而無發現的 `ok` 一眼分得開;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完。除這三個檔案狀態外沒有寫入跡象,只有回報內容。 |
## comment-cleanup
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 使用者要求清註解、移除審查痕跡、不要留審查產物時叫用。提交前 `jsc-hooks` 回報註解範圍警告時叫用,或變更的註解明顯帶著流程細節時叫用。預設只處理這次變更碰到的註解,使用者明講要清舊註解才擴大範圍。 |
| 關鍵步驟 | 在待清的儲存庫內跑 `tools/changed-comments.sh` 取得範圍(不帶參數看未提交變更,帶 `{base}` 比對基底分支)、依退出碼 0、1、2 分流、退出碼 1 回報「無發現」並停止、退出碼 2 原文回報 stderr 並停止且不手讀 diff 代替、一個檔案開一個 sub agent 平行改寫、每個 sub agent 拿到自己的檔案路徑與該檔的註解列並比對 `references/comment-scope.md`、移除流程細節並保留程式碼存在的理由、只剩流程細節的註解整段刪除、每個 sub agent 回傳前重讀自己改過的區域確認句子完整且沒有殘句、只改註解與文件字串不動行為、全部 sub agent 回傳後跑一次最小的建置或測試指令、失敗就判斷是否本輪造成並依規則重跑一次或還原、依類別回報清理結果、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-review:comment-cleanup {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過。 |
| 外部呼叫 | `tools/changed-comments.sh`、`references/comment-scope.md`、專案自己的建置或測試指令(沒有就改跑受影響腳本的語法檢查)、`git diff`、每個檔案一個改寫 sub agent。這支技能不呼叫 `jsc-hooks/hooks/write-guard.sh`,也不在它的 `review` 模式擋下範圍內:那支腳本的檔頭寫明 comment-cleanup 一律放行,因為精確判定註解列要解析整份新內容再逐語言判斷,判錯會擋掉合法清理。寫入範圍改由步驟 4 與後續審查把關。收尾另外呼叫 `jsc-hooks/tools/report-status.sh skill-end` 寫狀態事件。沒有 Gitea API 呼叫。 |
| 完成條件 | 範圍內每一列註解都有結果:維持原樣並說明理由、改寫、或刪除。每個 sub agent 都確認過自己的檔案且沒有未解決的殘句。`git diff` 只顯示註解與文件字串的變更。驗證指令退出碼 0,或報告寫明指令、退出碼與失敗歸屬,或寫明缺哪一個指令。報告說明移除了哪些類別、動了哪些檔案、驗證有沒有過。收尾一定要寫一筆 `skill-end` 狀態事件:範圍內每一列都有結果且驗證退出碼 0 是 `ok`,`changed-comments.sh` 回 2 讓範圍算不出來、什麼都沒改是 `blocked`,改完了但沒有驗證指令可跑、失敗判給既有問題、或有殘句沒解決是 `degraded`,驗證失敗歸本輪且重跑一次仍失敗而還原了該檔是 `failed`,`changed-comments.sh` 回 1 代表這次變更沒動到任何註解、沒有可清的東西,是 `aborted`。 |
| 可驗證跡象 | 這是三支技能裡唯一會改檔的。跑完在工作區留下實際的檔案修改,`git diff` 看得到被清理的註解列,且變更只涵蓋註解與文件字串,不涉及識別字、控制流程、資料結構。還原情境下 `git diff` 會看到該檔的本輪修改被撤回。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-review:comment-cleanup`,`status` 與 `exit` 兩欄對得上上一列講的判準;沒有註解可清那一輪看得到 `aborted`,還原那一輪看得到 `failed`,兩者和清乾淨的 `ok` 一眼分得開;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完。除這一筆事件外,不寫 wiki 頁、不開 PR、不改其他狀態檔。 |
+106
View File
@@ -0,0 +1,106 @@
# 程式碼註解內容界線
註解寫「為什麼這樣寫」,不寫「這件事記在哪份文件」。文件編號會過期、會搬家、會在存取權限外,讀程式碼的人查不到,只留下一串無意義的代號。
`code-review` 第 2 組(可讀性)依本清單審查;`jsc-hooks` 的 `comment-scope.sh` 在寫檔後自動比對並發出警告。
有兩項樣式判定不了:專案代號、客戶名稱。hook 抓不到這兩項,只能靠 `code-review` 人工判讀。hook 只掃這次新增的註解行,不翻既有程式碼的舊帳。
## 適用範圍
| 對象 | 是否受限 |
| --- | --- |
| 程式碼註解(`//`、`#`、`/* */`、`--`、`<!-- -->` 等) | 受限 |
| docstring、API 文件註解(含 `@param`、`@return` 內容) | 不受限 |
| README、設計文件、其他 markdown | 不受限 |
| commit 訊息、PR 描述 | 不受限 |
## 禁止寫進註解
### 追蹤系統編號
| 項目 | 命中例 |
| --- | --- |
| 議題編號 | `// #123`、`// ABC-123` |
| PR 編號、MR 編號 | `// 見 PR !45` |
| 變更單編號、CR 編號 | `// CR-2026-07` |
| 議題留言引用 | `// 見 #123 第三則留言` |
### jsc wiki 頁面編號
| 項目 | 命中例 |
| --- | --- |
| 規劃頁編號與分頁編號 | `// PLAN_A1B2C3D4 第 2 頁` |
| 分析頁編號 | `// ANALYZE_A1B2C3D4` |
| 工作包編號 | `// WP-01` |
| TDD 待辦編號 | `// todo 3` |
| 交付頁編號 | `// DELIVER_A1B2C3D4` |
| 維運頁編號 | `// MAINTAIN_A1B2C3D4` |
| 異常頁編號 | `// ERROR_A1B2C3D4` |
| 問詢頁編號 | `// QUESTION_A1B2C3D4` |
| 工作日誌頁編號 | `// LOG_A1B2C3D4` |
| 教訓頁編號 | `// LEARN_A1B2C3D4` |
| 盤點頁編號 | `// REPO_A1B2C3D4` |
| wiki 頁面網址 | `// https://gitea.example/…/wiki/PLAN_A1B2C3D4` |
### 需求與規格編號
| 項目 | 命中例 |
| --- | --- |
| 使用者故事編號 | `// US-01` |
| 驗收條件編號 | `// AC-01` |
| 測試案例編號 | `// TC-01` |
| 規格文件章節編號 | `// 規格書 3.2.1 節` |
| 稽核檢查項編號 | `// guidelines 第 7 項` |
### 流程與人事資訊
| 項目 | 命中例 |
| --- | --- |
| 分支名稱、commit hash | `// 見 commit a1b2c3d` |
| 版本號、里程碑、Sprint 編號 | `// Sprint 12 加入` |
| 人名、認領者、`@` 提及、`@author` | `// @someone 認領` |
| 工時估算、CPM 數據 | `// 預估 3 小時` |
| 專案代號、客戶名稱 | `// 客戶 XX 專案` |
| 產生來源署名、AI 署名 | `// 本檔由 jsc-sdlc:implement 產生` |
| 外部文件連結 | `// 見 Confluence、Notion、Google Docs 連結` |
### 審查流程痕跡
這些資訊是討論當下的座標。沒有參與那場討論的人看不懂,討論結束後也會失效。註解要留下程式邏輯的原因,不留下審查流程的路徑。
| 項目 | 命中例 |
| --- | --- |
| `review`、`code review` 字樣用來描述審查流程 | `// code review 要求改成共用函式` |
| 輪次描述 | `// 第二輪追加檢查這個分支` |
| 問題與發現編號 | `// Finding1:避免空指標`、`// 問題3 已修` |
| 審查者代稱或工具名 | `// hermes 建議保留這個判斷`、`// CodeReview bot 標出這裡` |
| 審查狀態標籤 | `// 真缺陷,已解決`、`// BLOCKING` |
## 允許寫進註解
| 項目 | 允許的理由 | 例 |
| --- | --- | --- |
| 日期與時間戳 | 標示某個決定的時間點,不依賴外部系統 | `// 2026-08-26 起改用新費率` |
| 需求變更歷程 | 說明「為什麼不是更直覺的那個做法」 | `// 原本四捨五入,改成無條件捨去` |
| RFC、ISO 等標準規格編號與章節 | 指向公開且長期穩定的規格 | `// 依 RFC 7231 第 6.5.1 節` |
| CVE 編號 | 說明這段防護在擋什麼 | `// 修補 CVE-2026-1234` |
| 第三方套件的 issue 連結 | 說明繞道寫法的成因與解除條件 | `// 繞過 github.com/foo/bar/issues/88,修好後可移除` |
| 授權標頭、SPDX 標記 | 法律要求 | `// SPDX-License-Identifier: MIT` |
| 語言原生標記 | 編譯器或工具鏈直接解讀 | `@deprecated`、`@since` |
| 十六進位色碼 | 指的是顏色,不是編號;純十進位的才當議題編號看 | `/* 主色沿用 #7c3aed,別再調 */` |
`@author` 不在允許之列:它是人名,屬「流程與人事資訊」。
## 命中時怎麼改
| 原本 | 改成 |
| --- | --- |
| `// WP-03 要求這裡回傳空陣列` | `// 查無資料回傳空陣列,呼叫端不必再判 null` |
| `// 見 #123` | 把 `#123` 裡的原因寫進註解本身 |
| `// @someone 2026-08-26 修` | `// 2026-08-26 改用新費率` |
| `// PLAN_A1B2C3D4 第 2 頁的規則` | 把該頁的規則正文濃縮成一句寫進來 |
| `// Finding1:避免空指標` | `// 輸入來自外部系統,空值要直接略過` |
| `// 第二輪追加這個判斷` | `// 舊資料可能缺欄位,先補預設值再解析` |
原則一句話:把編號指向的內容**搬進註解**,再刪掉編號。搬不動就代表那件事不該用註解表達,改寫進文件。
+66 -19
View File
@@ -2,7 +2,7 @@
來源:《Refactoring》(Martin Fowler)與《A Philosophy of Software Design》(John Ousterhout,第 6 組)。 來源:《Refactoring》(Martin Fowler)與《A Philosophy of Software Design》(John Ousterhout,第 6 組)。
`code-review` 技能依此清單分六組審查,每組由一個 sub agent 執行。 `code-review` 技能依此清單分六組審查,每組由一個 sub agent 執行。
每項格式:**定義 / 偵測訊號 / 建議重構手法**。 每項格式:**定義、偵測訊號、建議重構手法**。
## 第 1 組:結構與體積問題(Bloaters) ## 第 1 組:結構與體積問題(Bloaters)
@@ -26,7 +26,7 @@
### 1.4 基本型態偏執(Primitive Obsession) ### 1.4 基本型態偏執(Primitive Obsession)
- **定義**:不用物件包裝,全部用字串或數字代替(金額、電話、狀態碼、範圍…)。 - **定義**:不用物件包裝,全部用字串或數字代替(金額、電話、狀態碼、範圍等)。
- **偵測訊號**:以 string/int 表達有格式或規則的概念;同一組基本型態欄位在多處一起出現;以字串常數當型別碼;到處重複的驗證邏輯。 - **偵測訊號**:以 string/int 表達有格式或規則的概念;同一組基本型態欄位在多處一起出現;以字串常數當型別碼;到處重複的驗證邏輯。
- **建議重構手法**:Replace Primitive with Object、Replace Type Code with Subclasses、Introduce Parameter Object、Extract Class。 - **建議重構手法**:Replace Primitive with Object、Replace Type Code with Subclasses、Introduce Parameter Object、Extract Class。
@@ -41,13 +41,13 @@
### 2.2 魔術數字(Magic Numbers) ### 2.2 魔術數字(Magic Numbers)
- **定義**:程式碼中直接出現沒人知道代表什麼的字面值。 - **定義**:程式碼中直接出現沒人知道代表什麼的字面值。
- **偵測訊號**:條件式或運算中出現裸數字/裸字串(0、1、-1、空字串與明顯單位換算除外需判斷);同一字面值在多處出現。 - **偵測訊號**:條件式或運算中出現裸數字、裸字串(0、1、-1、空字串與明顯單位換算除外需判斷);同一字面值在多處出現。
- **建議重構手法**:Replace Magic Literal(抽成具名常數或 enum);有行為就升級為 Replace Type Code with Class。 - **建議重構手法**:Replace Magic Literal(抽成具名常數或 enum);有行為就升級為 Replace Type Code with Class。
### 2.3 死碼(Dead Code) ### 2.3 死碼(Dead Code)
- **定義**:已經不用卻不刪除的註解掉程式碼、變數、函式或類別。 - **定義**:已經不用卻不刪除的註解掉程式碼、變數、函式或類別。
- **偵測訊號**:被註解掉的程式碼區塊;無人呼叫的函式/類別(grep 全庫零引用);永遠不成立的條件分支;未使用的 import、參數、變數。 - **偵測訊號**:被註解掉的程式碼區塊;無人呼叫的函式、類別(grep 全庫零引用);永遠不成立的條件分支;未使用的 import、參數、變數。
- **建議重構手法**:Remove Dead Code(直接刪除,歷史交給版本控制)。 - **建議重構手法**:Remove Dead Code(直接刪除,歷史交給版本控制)。
### 2.4 過度註解(Comments) ### 2.4 過度註解(Comments)
@@ -57,6 +57,13 @@
- **建議重構手法**:Extract Function(用函式名取代註解)、Rename(用命名取代註解)、Introduce Assertion;保留「為什麼」與外部限制類註解。 - **建議重構手法**:Extract Function(用函式名取代註解)、Rename(用命名取代註解)、Introduce Assertion;保留「為什麼」與外部限制類註解。
- **注意**:本項與「註解問題」(第 5 組)互補:刪除解釋性廢話註解,補齊第 5 組要求的介面契約註解,兩者不衝突。 - **注意**:本項與「註解問題」(第 5 組)互補:刪除解釋性廢話註解,補齊第 5 組要求的介面契約註解,兩者不衝突。
### 2.5 文件編號夾帶(Document Reference Leak)
- **定義**:註解寫的是「這件事記在哪份文件」,不是「為什麼這樣寫」。編號會過期、會搬家、會在存取權限外,讀程式碼的人查不到,只剩一串無意義的代號。
- **偵測訊號**:註解含議題編號(`// #123`、`// ABC-123`)、wiki 頁編號(`// PLAN_A1B2C3D4`)、工作包編號(`// WP-01`)、commit hash(`// 見 commit a1b2c3d`)、`@` 提及(`// @someone 認領`)、外部文件連結(Confluence、Notion、Google Docs)。完整禁止清單、允許清單與適用範圍看 `references/comment-scope.md`。
- **建議重構手法**:把編號指向的內容搬進註解,再刪掉編號;搬不動就代表那件事不該用註解表達,改寫進文件。
- **注意**:本項與 2.4、第 5 組分工明確:2.4 刪解釋性廢話,第 5 組補介面契約,2.5 刪文件編號。
## 第 3 組:耦合與設計問題(Couplers) ## 第 3 組:耦合與設計問題(Couplers)
### 3.1 依賴嫉妒(Feature Envy) ### 3.1 依賴嫉妒(Feature Envy)
@@ -68,7 +75,7 @@
### 3.2 散彈式修改(Shotgun Surgery) ### 3.2 散彈式修改(Shotgun Surgery)
- **定義**:每當要修改一個小功能,就必須同時修改多個不同的檔案。 - **定義**:每當要修改一個小功能,就必須同時修改多個不同的檔案。
- **偵測訊號**:本次 diff 為了單一需求橫跨多檔做同質小改動;同一常數/規則散落多處;歷史上同組檔案總是一起被改。 - **偵測訊號**:本次 diff 為了單一需求橫跨多檔做同質小改動;同一常數或規則散落多處;歷史上同組檔案總是一起被改。
- **建議重構手法**:Move Function / Move Field 集中職責、Combine Functions into Class、Inline Function 後重新抽取。 - **建議重構手法**:Move Function / Move Field 集中職責、Combine Functions into Class、Inline Function 後重新抽取。
### 3.3 發散式變化(Divergent Change) ### 3.3 發散式變化(Divergent Change)
@@ -100,13 +107,13 @@
### 4.3 誇誇其談未來性(Speculative Generality) ### 4.3 誇誇其談未來性(Speculative Generality)
- **定義**:為了解決「未來可能」會用到的功能,寫了一堆現在用不到的複雜架構。 - **定義**:為了解決「未來可能」會用到的功能,寫了一堆現在用不到的複雜架構。
- **偵測訊號**:只有一個實作的抽象層/介面;從未被覆寫的 hook 方法;只在測試中使用的參數或彈性;「以後可能會需要」的註解。 - **偵測訊號**:只有一個實作的抽象層或介面;從未被覆寫的 hook 方法;只在測試中使用的參數或彈性;「以後可能會需要」的註解。
- **建議重構手法**:Collapse Hierarchy、Inline Function / Inline Class、Remove Dead Code、移除未用參數(Change Function Declaration)。 - **建議重構手法**:Collapse Hierarchy、Inline Function / Inline Class、Remove Dead Code、移除未用參數(Change Function Declaration)。
### 4.4 吞掉異常(Swallowed Exceptions) ### 4.4 吞掉異常(Swallowed Exceptions)
- **定義**:try-catch 裡面留白,發生錯誤時直接隱瞞,導致難以 Debug。 - **定義**:try-catch 裡面留白,發生錯誤時直接隱瞞,導致難以 Debug。
- **偵測訊號**:空的 catch 區塊;catch 後只留註解或 `// ignore`;catch 住廣義 Exception 後回傳 null/預設值而不記錄;錯誤訊息被丟棄後重包。 - **偵測訊號**:空的 catch 區塊;catch 後只留註解或 `// ignore`;catch 住廣義 Exception 後回傳 null 或預設值而不記錄;錯誤訊息被丟棄後重包。
- **建議重構手法**:最少要記錄(log)並保留原始例外鏈;能處理才 catch,不能處理就往上拋;以 Introduce Special Case 取代以 null 掩蓋錯誤。 - **建議重構手法**:最少要記錄(log)並保留原始例外鏈;能處理才 catch,不能處理就往上拋;以 Introduce Special Case 取代以 null 掩蓋錯誤。
## 第 5 組:註解問題(介面契約註解) ## 第 5 組:註解問題(介面契約註解)
@@ -115,9 +122,9 @@
### 5.1 方法描述 ### 5.1 方法描述
- **定義**:每個公開方法要有一句話描述,並標明所屬層級:**顯示層 / 邏輯層 / 存取層**。 - **定義**:每個公開方法要有一句話描述,並標明所屬層級:**顯示層、邏輯層、存取層**。描述後面要接**條列式的處理步驟與規則**,讀註解就知道這個方法做了哪幾件事、依哪些規則做。
- **偵測訊號**:公開方法無描述;描述未標層級;描述與方法實際行為不符。 - **偵測訊號**:公開方法無描述;描述未標層級;描述與方法實際行為不符;描述只有一句話,方法內部卻有多段流程或多條判斷規則,註解裡找不到對應的條列;條列步驟與程式碼實際順序對不上。
- **建議重構手法**:補上單句描述 + 層級標記;若一個方法橫跨多層,先依 Split Phase 拆分再各自標註。 - **建議重構手法**:補上單句描述與層級標記,再把處理流程拆成條列步驟、把判斷條件寫成條列規則;若一個方法橫跨多層,先依 Split Phase 拆分再各自標註;條列長到十項以上,代表方法本身太肥,同時列 1.2 並建議 Extract Function。
### 5.2 輸入參數說明 ### 5.2 輸入參數說明
@@ -127,22 +134,52 @@
### 5.3 輸出說明 ### 5.3 輸出說明
- **定義**:輸出(回傳值)要說明回傳的資料內容概要。 - **定義**:輸出(回傳值)要說明回傳的資料內容概要。回傳值是自訂資料模型,而註解格式支援導向時(XML 的 `<see cref>`、JSDoc 的 `{@link}`),說明要附上該資料模型的檔案連結,讀的人一鍵就跳到定義。
- **偵測訊號**:無回傳說明;未說明 null/空集合/錯誤時的回傳;回傳布林但未說明 true/false 意義。 - **偵測訊號**:無回傳說明;未說明 null、空集合、錯誤時的回傳;回傳布林但未說明 true/false 意義;回傳自訂資料模型卻只寫型別名稱純文字,沒有 `<see cref>` 或 `{@link}` 導向。
- **建議重構手法**:補回傳內容概要與邊界情況說明。 - **建議重構手法**:補回傳內容概要與邊界情況說明;自訂資料模型改用註解格式的導向標籤指到型別定義,泛型集合指到元素型別。
### 5.4 輸入與輸出範例 ### 5.4 輸入與輸出範例
- **定義**:輸入與輸出參數都必須有範例;範例內容**優先嘗試從資料庫取得真實資料,失敗才透過邏輯推理**產生。 - **定義**:輸入與輸出參數都必須有說明,範例則只掛在**純量**成員上。純量參數與純量屬性(字串、數值、布林、日期、列舉)要有範例。類別型參數與中間層的類別屬性只要說明,自己不掛範例,範例責任往下推給該類別的屬性。集合看元素型別判斷:元素是純量就比照純量,附一份列出幾個元素的範例;元素是類別就比照類別,只留說明,往下走進元素型別。字典看值型別,判準相同。範例內容**優先嘗試從資料庫取得真實資料,失敗才透過邏輯推理**產生。
- **偵測訊號**:註解缺範例;範例與型別不符;範例顯然是佔位假資料而環境可取得真實資料。 - **偵測訊號**:純量參數或純量屬性缺範例;範例與型別不符;範例顯然是佔位假資料而環境可取得真實資料;範例掛在類別型成員上,該類別的屬性卻一個範例都沒有。
- **建議重構手法**:以可用的連線查詢一筆代表性資料當範例(去識別化,不可含個資);無法連線才以邏輯推理造出合理範例並標明為推理值。 - **建議重構手法**:以可用的連線查詢一筆代表性資料當範例(去識別化,不可含個資);無法連線才以邏輯推理造出合理範例並標明為推理值;範例掛錯層就往下搬到該類別的每個純量屬性上。
- **注意**:型別判準與 `jsc-review:api-doc` 的檢核表 A 完全相同,同一個成員在兩邊判出來的答案一樣。分工不變:本項只看原始碼註解,Swagger 文件屬性歸 `api-doc`,同一個標的不重複回報。
### 5.5 巢狀結構註解 ### 5.5 巢狀結構註解
- **定義**:如果參數有巢狀結構(例如 class 內還有 class),就必須完全補齊每一層的註解。 - **定義**:如果參數有巢狀結構(例如 class 內還有 class),就必須完全補齊每一層的註解。每一層的純量屬性要有說明與範例,中間層的類別屬性只要說明;集合往元素型別走,遞迴走到「屬性全是純量」那一層為止。
- **偵測訊號**:DTO/ViewModel 僅頂層有註解;內層類別、集合元素型別的欄位無說明或無範例。 - **偵測訊號**:DTO/ViewModel 僅頂層有註解;內層類別、集合元素型別的欄位無說明;任一層的純量屬性缺範例;遞迴半途停住,某一層的屬性完全沒被走到。
- **建議重構手法**:逐層補齊 5.1–5.4;巢狀過深(≥ 3 層)時同時評估 Extract Class 是否被濫用。 - **建議重構手法**:逐層補齊 5.1–5.4;巢狀過深(≥ 3 層)時同時評估 Extract Class 是否被濫用。
### 5.6 內含功能的導向連結
- **定義**:一個功能內部呼叫了其他功能時,註解要交代呼叫了誰、為什麼呼叫。註解格式支援導向就用導向標籤(XML 的 `<see cref>`、JSDoc 的 `{@link}`),讓 IDE 直接跳過去。
- **偵測訊號**:方法內呼叫其他公開方法或服務,註解卻完全沒提;提了但只寫純文字方法名,沒有 `<see cref>` 或 `{@link}`;寫了連結卻沒寫用途,讀的人還是要自己點進去猜。
- **建議重構手法**:在條列步驟裡把被呼叫的功能改成導向標籤,後面接一句用途;被呼叫的功能多到列不完,代表這個方法在當協調中心,同時列 1.2 或 3.1 評估職責搬移。
- **注意**:註解格式不支援導向(例如純 `//` 行註解)就只寫用途,不硬造連結字串。
### 5.7 XML 註解標籤排版
- **定義**:註解採 XML 格式時,起始標籤與結束標籤**各自獨立一行**,內容夾在中間。這樣多行內容、條列與巢狀標籤才排得整齊,diff 也只動到真正改的那幾行。
- **偵測訊號**:起始標籤、內容、結束標籤擠在同一行(例:`/// <summary>取得訂單</summary>`);結束標籤跟在內容尾巴後面沒有換行(例:`/// 取得訂單</summary>`);`<param>`、`<returns>`、`<remarks>`、`<example>` 同樣擠成一行;巢狀標籤(`<list>` 內含 `<item>`)沒有逐層換行與縮排。
- **建議重構手法**:把每個標籤拆成三行——起始標籤一行、內容一行或多行、結束標籤一行;巢狀標籤逐層縮排;`<param>` 這類帶屬性的標籤,屬性留在起始標籤那一行。
- **注意**:只針對 XML 格式註解。JSDoc、docstring 沒有結束標籤,不適用本項。
### 5.8 專有名詞與變數標示
- **定義**:註解裡提到程式碼元素時,要用該語言註解格式的標示語法標起來,不能混在純文字裡。標示的目的是讓 IDE 與文件產生工具讀得懂,並在文件上呈現成程式碼樣式。
- **偵測訊號**:XML 註解裡直接以純文字寫參數名、型別名、成員名或程式碼字面;JSDoc 或 docstring 裡以純文字寫變數名與型別名,沒有加反引號;標錯類別(例如拿 `<c>` 標參數名、拿 `<paramref>` 標型別)。
- **建議重構手法**:依語言慣例補標示。
| 註解格式 | 對象 | 標示語法 |
| --- | --- | --- |
| XML | 變數、參數 | `<paramref name="orderId" />` |
| XML | 型別、成員 | `<see cref="OrderService.GetOrder" />` |
| XML | 程式碼字面、列舉值 | `<c>null</c>` |
| JSDoc、docstring | 變數、型別、程式碼字面 | 反引號 `` `orderId` `` |
- **注意**:標示規則跟著語言慣例走,不是跟著個人喜好走。IDE 的跳轉與文件工具的交叉引用都靠這些標籤,標錯等於沒標。
## 第 6 組:淺模組(Shallow Module) ## 第 6 組:淺模組(Shallow Module)
- **定義**:介面複雜度相對於功能深度過高的模組——使用它要懂的事,跟自己寫差不多(出自《A Philosophy of Software Design》:好模組要「介面簡單、實作深」)。 - **定義**:介面複雜度相對於功能深度過高的模組——使用它要懂的事,跟自己寫差不多(出自《A Philosophy of Software Design》:好模組要「介面簡單、實作深」)。
@@ -155,4 +192,14 @@
| --- | --- | --- | | --- | --- | --- |
| 高 | 會造成錯誤或已阻礙修改 | 吞掉異常、重複程式碼改漏、死碼誤導 | | 高 | 會造成錯誤或已阻礙修改 | 吞掉異常、重複程式碼改漏、死碼誤導 |
| 中 | 持續增加維護成本 | 巨型類別、臃腫函式、巢狀地獄、Couplers 全組 | | 中 | 持續增加維護成本 | 巨型類別、臃腫函式、巢狀地獄、Couplers 全組 |
| 低 | 可讀性與一致性 | 命名、魔術數字、註解缺漏、淺模組 | | 低 | 可讀性與一致性 | 命名、魔術數字、註解缺漏、文件編號夾帶、淺模組、XML 標籤排版、專有名詞未標示 |
第 5 組新增項目的級別另外標明,避免全部壓在「註解缺漏」一句話裡:
| 項目 | 級別 | 理由 |
| --- | --- | --- |
| 5.1 缺條列式步驟與規則 | 中 | 讀的人要重讀整個實作才知道規則,維護成本直接上升 |
| 5.3 回傳值缺資料模型連結 | 低 | 型別名稱還查得到,只是多花幾秒 |
| 5.6 缺內含功能的導向連結 | 低 | 同上,影響的是查找速度 |
| 5.7 XML 標籤排版擠在同一行 | 低 | 純排版,不影響語意 |
| 5.8 專有名詞未標示或標錯 | 低 | 影響 IDE 跳轉與文件呈現,不影響執行 |
+96
View File
@@ -0,0 +1,96 @@
---
name: api-doc
description: 'Audit an API project''s Swagger/OpenAPI documentation: every returnable HTTP status code declares a response type, every input and output carries a description, and a real data example sits on scalar members only — a data-model member takes a description and hands the example duty down to its own properties, recursively to the innermost scalar. Run after controller work or an implementation is complete, e.g. from jsc-sdlc implement, next to jsc-review:code-review. Detection runs first via tools/swagger-detect.sh; without both the Swagger package and its wiring, report unsupported and stop. The source comment contract belongs to jsc-review:code-review group 5; this skill covers Swagger document attributes only. Findings carry file:line, severity, and the fix; this skill never modifies code.'
---
# api-doc
Audit whether an API project's Swagger (OpenAPI) documentation is complete enough for a caller to integrate against it without reading the implementation.
## When to run
1. **After controller work is complete**: one controller or one related group of controllers is done.
2. **When an implementation is complete**: all todos of a work package that touched API endpoints are done. This is the call site in `jsc-sdlc:implement`, right next to `jsc-review:code-review`.
3. **Never on a project without Swagger**: step 1 below decides this in code, not by judgment.
## Division of labor
- This skill covers **Swagger document attributes only**: response type declarations, Swagger parameter descriptions, and Swagger examples.
- The comment contract — method description, layer tag, parameter description, return description, examples, nested-structure comments — belongs to `jsc-review:code-review` group 5 (`references/smells.md` 5.1 to 5.8). Never report the same gap twice; when a nested model already fails 5.5, leave it to `code-review`.
- Security, logic bugs, and test coverage belong to the CLI's built-in review.
## Steps
1. Detect Swagger support: run `tools/swagger-detect.sh {project path}` (defaults to the current directory). The script confirms package **and** wiring, so an installed-but-never-enabled project comes back unsupported. Read its exit code: `0` supported; `1` unsupported; `2` one of three causes — more than one argument, a project path that does not exist, or no `grep` in the environment. Completion condition: the exit code and the `support=`, `stack=`, `package=`, `config=` lines are captured.
2. On exit code `1`, report the literal 「本專案未啟用 Swagger 文件,略過 API 文件稽核」 plus the `stack=` and `package=` lines the script printed, and stop. Spawn no sub agent. On exit code `2`, report the script's stderr message verbatim, then route by which of the three causes that message names: the usage line means the call passed more than one argument, so re-run with at most one path; 「找不到專案路徑」 means the caller supplies an existing project path and re-runs; 「環境缺 grep」 means the environment itself is broken, so report that `grep` must be installed and stop — re-running with another path never clears this one. Spawn no sub agent in any of the three. Completion condition: the run has ended with the cause named and the matching next action stated, or exit code `0` moved it to step 3.
3. List the audit scope: every controller in the project, or only the controllers touched by `git diff` when the caller asked for a scoped run. On a scoped run, read git's exit code: `0` the changed controllers are the scope; any non-zero code means git refused (not a repository, unknown base revision, unreadable object) — report git's stderr verbatim and stop, spawn no sub agent. Never fall back to the whole project there: the caller asked for one scope, and quietly auditing a much larger one buries the git failure under a far longer run. `jsc-review:code-review` step 1 stops on the same failure, so both skills answer a broken `git diff` the same way. Completion condition: the controller list is non-empty and reported; an empty list ends the run with the literal 「無發現」, and a git failure ends it with git's error.
4. Audit in two aspects, and every aspect **MUST run as a sub agent**; the two may run in parallel:
| Aspect | Scope |
| --- | --- |
| 1 Status codes | For every action, every HTTP status code it can actually return — success, validation failure, authorization failure, not found, server error — has a declared response type and body schema (`ProducesResponseType`, `@ApiResponse`, FastAPI `responses=`, drf-spectacular `@extend_schema`). A status code the code can produce but the document never declares is a finding; so is a declared status code the code can never produce |
| 2 Description and example, down every data model | Two checklists over one pass of the controller's input and output types. Checklist A — description and example: every input parameter and every output field has a description, whatever its type, and the example requirement follows the type. A scalar member — string, number, boolean, date, enum — has an example as well. A data-model member has no example of its own; it keeps the description and hands the example duty down to its properties, where checklist B collects it. A collection is judged by its element type: scalar elements make the collection scalar, so it carries one example listing a couple of elements; model elements make the collection a data model, so it carries a description only and the walk enters the element type. A dictionary is judged by its value type on the same rule. Examples come from real project data first (the project's database, seed data, or fixtures), and only when real data is unreachable is an example derived by logic and marked as a derived value in the document itself. Checklist B — recursive walk: when a parameter or return value is a data model, or a collection of one, checklist A applies to every one of its properties, and a model containing another model recurses to the innermost layer. Every scalar property on every layer carries a description and an example; every intermediate model property carries a description only. Walk in from the controller, follow the input and output types down, and end each branch on the layer whose properties are all scalar. Run checklist A on each member as the walk in checklist B reaches it, so each data model file is opened once and both checklists are answered for it |
Checklists A and B ran as two sub agents before, and they opened the same data model files twice. Aspect 2 is one sub agent now. The checklists stay listed apart so no check goes vague once they share a pass: a missing description or example is reported under A, a member the recursive walk never reached is reported under B, and field 4 of every row says which one.
An example exists so the caller reads the literal payload straight off the document, and every literal value has exactly one owner. That is why a data-model member carries none: its payload is defined by its properties' own examples, and a second copy on the parent goes stale the day a property changes. A collection follows the same test on its element type — a list of scalars fits in one literal, so the example is complete where it stands, while a list of models only repeats what the element model already owns. Judge the element type, never the collection wrapper, so `List<string>` and `string` come out the same and `List<Address>` and `Address` come out the same.
Instructions for each sub agent: read only, change nothing. Return findings as TSV rows — one finding per row, six fields separated by a single tab, in this order:
| Field | Content |
| --- | --- |
| 1 file | path relative to the repository root |
| 2 line | line number, a positive integer |
| 3 severity | `高`, `中`, or `低` on the scale below |
| 4 group | the aspect: `1` status codes, `2A` description and example, `2B` recursive walk |
| 5 item | the finding name, taken from the Severity table below |
| 6 detail | one sentence of evidence plus the concrete fix — which attribute to add, on which member |
Fields 5 and 6 are written in Traditional Chinese. Free-form prose is not accepted: `tools/merge-findings.sh` deduplicates on fields 1 and 2, so any row off this format is rejected and its finding never reaches the report. An aspect with nothing to report returns the single line 「無發現」 instead of rows.
Completion condition: both aspects have returned, each with TSV rows or with 「無發現」.
5. Merge with `tools/merge-findings.sh`. Concatenate the two aspects' TSV rows in aspect order and pipe them into the script on stdin. Start this step only once both have returned. Read its exit code: `0` take its stdout as the merged list — deduplicated by location and sorted by severity, 高 first and 低 last; `1` no aspect returned a row, so report the literal 「無發現」 and stop; `2` environment or input error, so report the script's stderr verbatim and stop, and never hand-merge as a substitute; `3` an aspect returned a malformed row, and the stderr names which row, so ask that one aspect's sub agent to re-emit in the format above and run the merge again — after a second `3`, report the malformed rows verbatim and stop. Completion condition: the merged list is on hand with every location appearing exactly once, or the run ended with 「無發現」 or with the reported error.
6. Report the finding list in Traditional Chinese, one line per merged row. **This skill never modifies code.** `jsc-hooks/hooks/write-guard.sh` in `review` mode enforces that in code: while this skill runs, it blocks write tools. Which tools those are is declared in that script's own header; never restate the list here, because a stale copy of it reads as coverage the guard does not have. Only claude has a `PreToolUse` hook, so codex, copilot, antigravity, and kiro never reach that guard — on those four this rule and the sub agent instructions are the only constraint. The caller decides what to fix.
Then release the review lock: run `jsc-hooks/hooks/write-guard.sh release`, which clears `$JSC_HOME/sessions/{sid}.lastskill`. That file is how the guard recognizes the running skill, and no event tells the guard a skill ended: leave it in place and the caller's first fix — the fix this very report asked for — is blocked by the audit that just finished. State the fallback in the report either way, because an older `jsc-hooks` treats `release` as an unknown mode and exits `0` without clearing anything: the lock then lifts by itself once the file is older than `JSC_WRITE_GUARD_TTL` (900 seconds by default), and `JSC_WRITE_GUARD=off` opens it immediately.
Completion condition: the report is handed to the caller, the release command has been run and the wait plus the `JSC_WRITE_GUARD=off` escape hatch are stated, and the fix decision is left to them.
7. Record how this run ended, as the very last thing this skill does — after the release, so a lock that would not clear is still visible to it:
`jsc-hooks/tools/report-status.sh skill-end jsc-review:api-doc {status} {exit} "{detail}"`
Resolve that path the way step 6 already resolves `jsc-hooks/hooks/write-guard.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. This skill writes no code; it must not start failing over a line it could not write about itself.
| status | This skill's case |
| --- | --- |
| `ok` | Both aspects returned and the merged list reached the caller. A `merge-findings.sh` exit 1 is `ok` too: the audit ran and found nothing, which is a real answer and a different thing from never auditing |
| `blocked` | A precondition refused before any aspect ran: `swagger-detect.sh` exit 1 — the project has no Swagger package or no wiring, so the audit is skipped, which is exactly what step 2 reports and never a failure of this run. Exit 2 (project path missing, or no `grep` in the environment) and a `git diff` that refused belong here too |
| `degraded` | The finding list is reported but the close-out is short: `write-guard.sh release` did not clear `$JSC_HOME/sessions/{sid}.lastskill`, so the lock lingers until `JSC_WRITE_GUARD_TTL` and the caller's first fix is blocked by the audit that just finished |
| `failed` | The aspects ran and their findings never reached a report: `merge-findings.sh` exit 2, or a second exit 3 with malformed rows still on the table |
| `aborted` | There was nothing to audit — step 3's controller list came back empty, so a scoped run found no changed controller and no sub agent was spawned. Keep this apart from the `ok` above: both end in 「無發現」, and only the event tells a clean audit from an audit that never had a subject |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: controller and finding counts plus exit codes, never file paths, endpoint names, example payloads, or personal data.
Completion condition: the command has run, or the script was absent and this step was skipped.
## Severity
The 高、中、低 scale is the one in `references/smells.md`. Map this skill's findings onto it:
| Finding | Severity |
| --- | --- |
| A returnable status code has no declared response type | 高 |
| A declared response type does not match the body the code returns | 高 |
| An input or output has no description, whatever its type | 中 |
| A scalar input, output, or property has no example | 中 |
| An example sits on a data-model member while its own properties carry none | 中 |
| A data model property is missing from the recursive walk entirely | 中 |
| A placeholder example while real data was reachable | 低 |
| A derived example that is not marked as derived | 低 |
## Notes
- An example on a data-model member is a finding only when that model's properties carry none of their own. That is the example hung on the wrong layer: the caller holds one blob no property definition backs, and the layers below look answered while every one of them is empty. A model whose properties are all covered keeps any extra whole-object example it already has; this skill asks for no such example and reports none.
- Examples taken from a database must be de-identified. Never put personal data into a Swagger document.
- 「推導值」 is the literal marker for a derived example; keep it in the document text so the next reader knows the value was never observed.
- When there are no findings, report the literal 「無發現」 explicitly; never leave the report empty.
+63 -24
View File
@@ -3,39 +3,78 @@ name: code-review
description: Review changed code against the Refactoring smell catalog in six groups (bloaters, obscurity, couplers, dispensables, comment contract, shallow modules). Run when a file change is complete or an implementation is complete, e.g. from jsc-sdlc implement. Each group runs as a sub agent over the git diff; findings are reported with file:line, severity, and refactoring, and the caller decides whether to fix. Not a replacement for the CLI's built-in security or bug review. description: Review changed code against the Refactoring smell catalog in six groups (bloaters, obscurity, couplers, dispensables, comment contract, shallow modules). Run when a file change is complete or an implementation is complete, e.g. from jsc-sdlc implement. Each group runs as a sub agent over the git diff; findings are reported with file:line, severity, and refactoring, and the caller decides whether to fix. Not a replacement for the CLI's built-in security or bug review.
--- ---
# code-review — 壞味道分組審查 # code-review
依 `references/smells.md`(來自《Refactoring》)審查變更的程式碼。 Review changed code against `references/smells.md` (from the book *Refactoring*). The reference is written in Traditional Chinese; read it as-is.
## 審查時機 ## When to run
1. **檔案變更完成時**:單檔或一組相關檔案改完。 1. **When a file change is complete**: one file or one related group of files is done.
2. **實作完成時**:一個工作包的所有待辦完成(`jsc-sdlc:implement` 步驟 6 呼叫)。 2. **When an implementation is complete**: all todos of a work package are done. This is the call site in `jsc-sdlc:implement` — the end of a work package, after its last todo and before it is committed and turned into a PR.
## 分工 ## Division of labor
- 本技能專注《Refactoring》壞味道與註解契約(第 5 組)與淺模組(第 6 組)。 - This skill covers the *Refactoring* smells, the comment contract (group 5), and shallow modules (group 6).
- 安全性、邏輯 bug、測試涵蓋率交給 CLI 內建的 review 能力(例:claude 的 `/security-review`),不重複實作。 - Security, logic bugs, and test coverage belong to the CLI's built-in review (e.g. claude's `/security-review`); do not duplicate them.
- Swagger document auditing belongs to `jsc-review:api-doc`: response type declarations per HTTP status code, Swagger parameter descriptions, and Swagger examples down the nested data models. Group 5 here covers the source comment contract only; the two never report the same gap twice.
## 流程 ## Steps
1. 取得審查範圍:`git diff`(未 commit 變更)或 `git diff {base}...HEAD`(實作完成時對基準分支);列出變更檔案清單。 1. Snapshot the review scope once, to a path that belongs to this run. In one command, expand `${TMPDIR:-/tmp}/jsc-code-review.$$.diff` into a variable, redirect `git diff` (uncommitted changes) or `git diff {base}...HEAD` (against the base branch when an implementation is complete) into it, and print the expanded path. The `$$` is what keeps two runs on one machine apart; a fixed file name lets a second run overwrite the first one's snapshot mid-review, and the six groups in step 2 would then judge bytes that were never their own diff. Read git's exit code: `0` the snapshot is written; any non-zero code means git refused (not a repository, unknown base revision, unreadable object) — report git's stderr verbatim and stop, spawn no sub agent. An empty snapshot file → delete it, report the literal 「無發現」 and stop; spawn no sub agent. The six groups in step 2 all read this one file, so git computes the diff once and every group judges the same bytes. Completion condition: the expanded snapshot path is reported as a literal path — never as the unexpanded `$$` pattern — together with a non-empty changed-file list, or the run already ended with 「無發現」 or with git's error.
2. 六組檢查分組進行,**每組必須以 sub agent 執行**,六組可平行: 2. Review in six groups, and every group **MUST run as a sub agent**; the six groups may run in parallel:
| 組 | 範圍 | | Group | Scope |
| --- | --- | | --- | --- |
| 1 結構與體積(Bloaters) | smells.md 第 1 組 | | 1 Bloaters | smells.md group 1 |
| 2 可讀性與命名(Obscurity) | smells.md 第 2 組 | | 2 Obscurity | smells.md group 2. For 2.5 comment scope leaks, report only what a pattern cannot decide: project code names, customer names, and context-dependent review traces. `references/comment-scope.md` is the single source of the banned and allowed lists — read it, never restate it here. Pattern-detectable items belong to `jsc-hooks/hooks/comment-scope.sh` and stay outside this group (see Notes for what that leaves uncovered) |
| 3 耦合與設計(Couplers) | smells.md 第 3 組 | | 3 Couplers | smells.md group 3 |
| 4 邏輯與壞習慣(Dispensables & Others) | smells.md 第 4 組 | | 4 Dispensables & Others | smells.md group 4 |
| 5 註解問題(介面契約) | smells.md 第 5 組 | | 5 Comment contract | smells.md group 5, 5.1 to 5.8 — including 5.6 navigation links to the functions a method calls, 5.7 XML comment tag layout (opening and closing tag each on its own line), and 5.8 code-element markup by language convention (`<paramref>`, `<see cref>`, `<c>` in XML; backticks in JSDoc and docstrings) |
| 6 淺模組(Shallow Module) | smells.md 第 6 組 | | 6 Shallow Module | smells.md group 6 |
每個 sub agent 的指示:只讀不改;依該組的「定義 / 偵測訊號」逐檔檢查變更行與其所在函式/類別;每筆發現回報 `檔案:行號`、壞味道名稱、嚴重度(高/中/低,依 smells.md 分級)、一句話證據、建議重構手法。 Instructions for each sub agent: read only, change nothing; read the diff snapshot at the exact path step 1 reported, that one file only, and never run `git diff` again; check every changed line and its enclosing function or class against the group's definitions and detection signals in smells.md.
3. 彙整六組發現:去除重複(同位置多組命中時合併並列出所有壞味道)、依嚴重度排序。
4. 回報審查結果清單。**本技能不修改程式碼**;是否修正由呼叫端決定(實作流程中通常高、中必修,低擇要修)。
## 注意 Each sub agent returns its findings as TSV rows — one finding per row, six fields separated by a single tab, in this order:
- 第 5 組範例資料若查詢資料庫取得,必須去識別化,不可含個資。 | Field | Content |
- 無任何發現時明確回報「無發現」,不可留白。 | --- | --- |
| 1 file | path relative to the repository root |
| 2 line | line number, a positive integer |
| 3 severity | `高`, `中`, or `低` on the smells.md scale |
| 4 group | this group's number, `1` to `6` |
| 5 item | the smell name |
| 6 detail | one sentence of evidence plus the suggested refactoring |
Fields 5 and 6 are written in Traditional Chinese. Free-form prose is not accepted: `tools/merge-findings.sh` deduplicates on fields 1 and 2, so any row off this format is rejected and its finding never reaches the report. A group with nothing to report returns the single line 「無發現」 instead of rows.
Completion condition: all six groups have returned, each with TSV rows or with 「無發現」.
3. Merge with `tools/merge-findings.sh`. Concatenate the six groups' TSV rows in group order and pipe them into the script on stdin. Start this step only once all six groups have returned; a group still running means the merge waits. Read its exit code: `0` take its stdout as the merged list — deduplicated by location and sorted by severity, 高 first and 低 last; `1` no group returned a row, so report the literal 「無發現」 and stop; `2` environment or input error, so report the script's stderr verbatim and stop, and never hand-merge as a substitute; `3` a group returned a malformed row, and the stderr names which row, so ask that one group's sub agent to re-emit in the format above and run the merge again — after a second `3`, report the malformed rows verbatim and stop. Completion condition: the merged list is on hand with every location appearing exactly once, or the run ended with 「無發現」 or with the reported error.
4. Report the finding list in Traditional Chinese, one line per merged row. **This skill never modifies code.** `jsc-hooks/hooks/write-guard.sh` in `review` mode enforces that in code: while this skill runs, it blocks write tools. Which tools those are is declared in that script's own header; never restate the list here, because a stale copy of it reads as coverage the guard does not have. Only claude has a `PreToolUse` hook, so codex, copilot, antigravity, and kiro never reach that guard — on those four this rule and the sub agent instructions are the only constraint. The caller decides what to fix (inside the implementation flow, 高 and 中 are normally mandatory, 低 is judgment).
Then close the run down in two moves. Delete the step 1 snapshot file; nothing else ever reads it, and left behind it accumulates one stale diff per review. Release the review lock by running `jsc-hooks/hooks/write-guard.sh release`, which clears `$JSC_HOME/sessions/{sid}.lastskill`. That file is how the guard recognizes the running skill, and no event tells the guard a skill ended: leave it in place and the caller's first fix — the fix this very report asked for — is blocked by the audit that just finished. State the fallback in the report either way, because an older `jsc-hooks` treats `release` as an unknown mode and exits `0` without clearing anything: the lock then lifts by itself once the file is older than `JSC_WRITE_GUARD_TTL` (900 seconds by default), and `JSC_WRITE_GUARD=off` opens it immediately.
Completion condition: the report is handed to the caller, the snapshot is deleted, the release command has been run and the wait plus the `JSC_WRITE_GUARD=off` escape hatch are stated, and the fix decision is left to them.
5. Record how this run ended, as the very last thing this skill does — after the snapshot deletion and the release, so a cleanup that did not complete is still visible to it:
`jsc-hooks/tools/report-status.sh skill-end jsc-review:code-review {status} {exit} "{detail}"`
Resolve that path the way step 4 already resolves `jsc-hooks/hooks/write-guard.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. This skill writes no code; it must not start failing over a line it could not write about itself.
| status | This skill's case |
| --- | --- |
| `ok` | All six groups returned and the merged list reached the caller, the snapshot is deleted and the lock is clear. A `merge-findings.sh` exit 1 is `ok` too: six groups read the diff and none had anything to report, which is a real answer |
| `blocked` | The review scope never existed, so no sub agent ran: step 1's `git diff` refused — not a repository, an unknown base revision, or an unreadable object. Nothing was reviewed and nothing could be |
| `degraded` | The report is handed over but the close-out is short: the step 1 snapshot is still on disk, or `write-guard.sh release` did not clear `$JSC_HOME/sessions/{sid}.lastskill` and the lock lingers until `JSC_WRITE_GUARD_TTL` |
| `failed` | The six groups ran and their findings never reached a report: `merge-findings.sh` exit 2, or a second exit 3 with malformed rows still on the table |
| `aborted` | There was nothing to review — the step 1 snapshot came back empty, so the diff holds no changed line, the file was deleted and no sub agent was spawned. Keep this apart from the `ok` above: both end in 「無發現」, and only the event tells a clean review from a review that never had a subject |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: changed-file and finding counts plus exit codes, never file paths, code excerpts, branch names, or personal data.
Completion condition: the command has run, or the script was absent and this step was skipped.
## Notes
- `jsc-hooks/hooks/comment-scope.sh` already matches the pattern-detectable comment scope items after every file write. Group 2 covers what patterns cannot decide — project code names, customer names, and context-dependent review traces — plus the overall judgment; the two never report the same finding twice.
- **Removed protection, on purpose.** Group 2 used to re-scan the pattern-detectable items as well, which made it the last net whenever the hook was not running: `JSC_COMMENT_SCOPE=off` turns `comment-scope.sh` off entirely, and a CLI with no `comment-scope.sh` wiring never runs it at all. That net is gone. On such a run, an issue id, a wiki page id, a work package id, or a commit hash left in a comment now reaches the commit with nothing catching it. Run `jsc-review:comment-cleanup` before commit whenever the hook is off or unwired.
- If group 5 examples are fetched from a database, they must be de-identified; never include personal data.
- When there are no findings, report the literal 「無發現」 explicitly; never leave the report empty.
+50
View File
@@ -0,0 +1,50 @@
---
name: comment-cleanup
description: Clean review traces and document-tracking noise from comments touched by the current change. Use when the user asks to clean comments, remove review traces, or when a pre-commit check finds process details in changed comments. It rewrites comments only, keeps code behavior unchanged, and uses references/comment-scope.md as the single source of banned and allowed content. Do not use for untouched legacy comments unless the user explicitly asks for that wider scope.
---
# comment-cleanup
Clean comments changed in the current diff so they explain why the code exists, without carrying review traces, issue coordinates, wiki page ids, or other process-only details.
## When to run
1. **User asks to clean comments**: examples include "clean the comments", "remove review traces", or "do not leave review artifacts".
2. **Before commit**: `jsc-hooks` reports comment scope warnings, or a changed comment clearly carries process-only details.
3. **Not for untouched legacy comments**: only widen the scope when the user explicitly asks for legacy cleanup.
## Single Source
Use `references/comment-scope.md` for the banned list, allowed list, and rewrite rules. Do not copy those lists into this skill.
## Division of Labor
- `jsc-hooks/hooks/comment-scope.sh` blocks pattern-detectable violations after writes.
- `jsc-review:code-review` group 2 reports judgment-based cases that patterns cannot decide.
- This skill cleans the changed comments. Do not report the same finding again when a hook or code review already reported it; either clean it or explain why it is outside this skill's scope.
## Steps
1. Define the scope with `tools/changed-comments.sh`, run from the repository being cleaned: no argument for uncommitted work, `tools/changed-comments.sh {base}` when an implementation is complete and the base branch is the comparison point. It prints one row per changed comment line — file, line number, content — so this step never judges by eye which lines are comments; that judgment lives in the script, next to the one `jsc-hooks/hooks/comment-scope.sh` already uses. Read its exit code: `0` its stdout is the cleanup scope, and step 2 hands each file its own rows; `1` this change added or modified no comment line, so report the literal 「無發現」 and stop, spawn no sub agent; `2` a parameter or environment error, so report the script's stderr verbatim and stop, spawn no sub agent, and never substitute a hand-read diff — a scope that could not be computed is not an empty scope. Widen to untouched legacy comments only when the user explicitly asked for that, and say so in the report. Completion condition: the cleanup scope is listed by file, or the run ended with 「無發現」 or with the script's error.
2. Rewrite one file per sub agent, and every file **MUST run as a sub agent**. One file's comments never depend on another file's, so the sub agents run in parallel. Each sub agent is handed one file path and that file's scoped comment lines from step 1, and it compares each of them with `references/comment-scope.md`, removes process-only details, and keeps the real reason for the code. A comment that is only process detail with no real reason left is deleted whole. Each sub agent returns the file path, the categories it removed, and the line numbers it touched. Completion condition: every file's sub agent has returned, and every scoped comment is either unchanged with a stated reason, rewritten, or deleted.
3. Each sub agent re-reads its own changed area before it returns. It confirms every sentence is complete, the logic still reads naturally, and no dangling fragment remains after a deletion; a fragment it cannot resolve is reported instead of left behind. Completion condition: every sub agent has confirmed its file, and no returned report names an unresolved fragment.
4. Change comments and documentation strings only. Do not change executable behavior, identifiers, control flow, data shape, or tests except when a test fixture literally asserts the old comment text. `jsc-hooks/hooks/write-guard.sh` in `review` mode is the code-level backstop, but it covers this skill only as far as jsc-hooks can decide a comment line precisely: where that decision is not precise, the guard is limited to `jsc-review:code-review` and `jsc-review:api-doc`, which write nothing at all, and this skill runs unguarded. Only claude has a `PreToolUse` hook in the first place, so on codex, copilot, antigravity, and kiro this step's prose is the only constraint. Completion condition: `git diff` shows comment-only or documentation-string-only edits.
5. Run the smallest relevant build or test command once for the whole changed project, after every sub agent in step 2 has returned. Read the exit code. `0` — verification passed. Non-zero — the command ran and failed, so name the command, its exit code, and the failing output, then decide whether this run caused it: a failure that names a file this run touched is treated as caused here, so restore that file's comment syntax and re-run the command once; if it fails the same way again, revert this run's edits in that file and report the revert. A failure that names no file this run touched is reported as pre-existing, and the cleanup edits stay. If no project command is available, run syntax checks for the touched scripts and report the gap. Completion condition: the command exited `0`, or the report states the command, its exit code and whether the failure belongs to this run, or the exact missing command is reported.
6. Report the cleanup by category, not by full diff. Completion condition: the report names which categories were removed, which files were touched, and whether verification passed.
7. Record how this run ended, as the very last thing this skill does — after the verification of step 5, whose result decides most of the status below:
`jsc-hooks/tools/report-status.sh skill-end jsc-review:comment-cleanup {status} {exit} "{detail}"`
Resolve that path the way this file already names `jsc-hooks/hooks/comment-scope.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. This is the one skill of the three that edits files, so the rule matters more here: rewritten comments are already on disk, and a recorder that is not installed must not turn that into a failed run.
| status | This skill's case |
| --- | --- |
| `ok` | Every scoped comment is unchanged with a stated reason, rewritten, or deleted, no sub agent reported an unresolved fragment, and step 5's verification command exited 0 |
| `blocked` | `changed-comments.sh` exit 2 — a parameter or environment error left the scope uncomputed, so no file was read and none was rewritten. A scope that could not be computed is not an empty scope, and this status is what keeps the two apart |
| `degraded` | The comments are cleaned but nothing confirmed them: no project build or test command exists so only syntax checks ran, or step 5 failed on a file this run never touched and the failure was recorded as pre-existing while the edits stayed. A sub agent that returned an unresolved fragment lands here too |
| `failed` | Step 5 failed on a file this run touched, the one retry failed the same way, and this run's edits in that file were reverted. The cleanup did not stand, and the revert is the fact the caller has to see |
| `aborted` | `changed-comments.sh` exit 1 — this change added or modified no comment line, so there is nothing to clean and no sub agent was spawned. The user asking to stop before step 2 lands here as well |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: file and comment-line counts plus exit codes, never comment text, file paths, branch names, or personal data — the text this skill removes is exactly the text that must not be copied into an event line.
Completion condition: the command has run, or the script was absent and this step was skipped.
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env sh
# changed-comments.sh — 列出本次變更裡新增或修改的註解行,供 comment-cleanup 決定清理範圍。
#
# 用法:
# changed-comments.sh 比對工作區與 HEAD(git diff HEAD),即尚未提交的變更
# changed-comments.sh <base> 比對基準版本(git diff {base}...HEAD),實作完成時用
# 一律在要檢查的存取庫目錄下執行;本腳本不吃路徑參數,也不切換目錄。
#
# 輸出(TSV,一列一行註解,三欄,欄位之間一個 tab,只走 stdout):
# 1 檔案 路徑,相對存取庫根目錄
# 2 行號 該註解行在新版檔案裡的行號,正整數
# 3 內容 該行原文;行內的 tab 換成一個空白,欄位才不會錯位
#
# 結束碼:
# 0 有結果,至少印出一列
# 1 沒有結果,本次變更沒有新增或修改的註解行(呼叫端回報「無發現」)
# 2 參數或環境錯誤:參數超過一個、目前目錄不在 git 工作區、基準版本無效、缺 git 或 awk
#
# 判準的來源:註解行樣式與「不受本規則限制的非程式碼副檔名」兩份判準,都沿用 jsc-hooks 的
# hooks/comment-scope.sh(scan_file 裡的那兩份),一字不差地照抄。那支 hook 是規則實作的
# 正本,這裡只是把同一份判準搬到 git diff 上;哪天正本改了樣式,這裡要跟著改,不得各自演化。
# 跳過非程式碼檔不是可有可無的:markdown 的標題行開頭就是 #,不跳過就會把整份文件當成註解。
set -u
usage() {
echo '用法:changed-comments.sh [base](不給基準時比對工作區與 HEAD)' >&2
exit 2
}
[ "$#" -le 1 ] || usage
for cmd in git awk; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "錯誤:環境缺 $cmd,無法列出變更的註解行。" >&2
exit 2
}
done
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo '錯誤:目前目錄不在 git 工作區內,取不到變更範圍。' >&2
exit 2
}
# git diff 的失敗一律原樣往上帶:判不出範圍卻回「無發現」,等於把環境問題講成沒有東西要清。
if [ "$#" -eq 1 ]; then
base=$1
case "$base" in
-*) usage ;;
esac
git rev-parse --verify --quiet "$base^{commit}" >/dev/null 2>&1 || {
echo "錯誤:基準版本「$base」不是有效的 commit。" >&2
exit 2
}
DIFF=$(git diff --no-color -U0 "$base...HEAD" 2>&1) || {
printf '%s\n' "$DIFF" >&2
exit 2
}
else
DIFF=$(git diff --no-color -U0 HEAD 2>&1) || {
printf '%s\n' "$DIFF" >&2
exit 2
}
fi
# 樣式走環境變數交給 awk:走 -v 的話 awk 會先解釋字串裡的跳脫序列,樣式中的 \* 會被吃成
# 量詞 *,整條 ERE 的意思就變了。ENVIRON 不做這層處理,樣式進到 awk 時與正本一字不差。
COMMENT_RE='^[[:space:]]*(//|#|--|\*|/\*|<!--|;|%)|[[:space:]](//|#)[[:space:]]'
OUT=$(printf '%s\n' "$DIFF" | COMMENT_RE="$COMMENT_RE" awk '
BEGIN { OFS = "\t"; re = ENVIRON["COMMENT_RE"]; skip = 1 }
# 非程式碼檔沒有「程式碼註解」,整檔跳過(副檔名清單同 comment-scope.sh)。
function skipped(p) {
return (p ~ /\.(md|markdown|txt|rst|json|csv|tsv|svg|lock|log)$/ || p ~ /COMMIT_EDITMSG$/)
}
/^\+\+\+ / {
f = substr($0, 5) # 去掉開頭的「+++ 」
sub(/^b\//, "", f)
skip = (f == "" || f == "/dev/null" || skipped(f))
next
}
# 區塊標頭 @@ -a,b +c,d @@:第三欄的 +c 就是這一段在新版檔案裡的起始行號。
/^@@ / {
t = $3
sub(/^\+/, "", t)
sub(/,.*$/, "", t)
ln = t + 0
next
}
/^\+/ {
if (skip || ln < 1) next
line = substr($0, 2)
if (line ~ re) {
out = line
gsub(/\t/, " ", out)
print f, ln, out
}
ln++ # 只有新增行會佔掉新版檔案的行號,刪除行不會
}
')
[ -n "$OUT" ] || exit 1
printf '%s\n' "$OUT"
exit 0
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env sh
# merge-findings.sh — 合併多組審查發現,依位置去重,再依嚴重度排序。
# 用法:merge-findings.sh [發現檔…]
# 給檔名就讀那幾個檔,不給檔名就讀 stdin。code-review 六組與 api-doc 兩個面向
# 共用這一支,兩支技能都不再自己描述合併與排序規則。
#
# 輸入格式(TSV,一列一筆發現,六欄,欄位之間一個 tab):
# 1 file 檔案路徑,相對專案根目錄,不得留空
# 2 line 行號,正整數
# 3 severity 嚴重度,只收 高、中、低
# 4 group 組別或面向代號,例如 2 或 1 狀態碼
# 5 item 發現名稱,壞味道名稱或稽核項目名稱
# 6 detail 一句證據加上建議修法
# 空白列、開頭是 # 的列、第一欄為「無發現」的列一律略過。
#
# 輸出(TSV,同樣六欄,一個位置一列,印到 stdout):
# file 與 line 相同的發現合併成一列。
# severity 取該位置最高的一級。group、item、detail 依輸入順序以「|」相連。
# 排序:嚴重度 高 → 中 → 低;同一級再依 file 字典序、line 數值遞增。
#
# 結束碼:
# 0 合併成功,至少印出一列
# 1 沒有任何發現(輸入沒有可用的資料列),呼叫端回報「無發現」
# 2 參數或環境錯誤(找不到輸入檔,或缺 awk、sort、cut)
# 3 輸入格式錯誤(欄數不是 6、行號不是正整數,或嚴重度不在 高、中、低)
#
# 護欄:
# 格式錯誤一律 exit 3 並在 stderr 指出是第幾個檔的第幾列,不猜、不放行、不自行補欄。
# 錯誤訊息一律印繁中到 stderr,正常輸出只走 stdout。
set -u
for cmd in awk sort cut; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "錯誤:環境缺 $cmd,無法合併發現。" >&2
exit 2
}
done
for f in "$@"; do
case "$f" in
-*) echo "用法:merge-findings.sh [發現檔…](不給檔名時讀 stdin)" >&2; exit 2 ;;
esac
[ -f "$f" ] || { echo "錯誤:找不到輸入檔 $f。請確認路徑後重試。" >&2; exit 2; }
done
TAB=$(printf '\t')
TMP="${TMPDIR:-/tmp}/jsc-merge-findings.$$"
trap 'rm -f "$TMP" "$TMP.sorted"' EXIT INT TERM
# 第一欄先放排序用的嚴重度序位,排完再切掉。
awk -F"$TAB" '
BEGIN { OFS = "\t"; n = 0; bad = 0 }
{
sub(/\r$/, "", $0)
if ($0 ~ /^[ \t]*$/) next
if ($0 ~ /^#/) next
if ($1 == "無發現") next
where = (FILENAME == "" || FILENAME == "-") ? "stdin" : FILENAME
if (NF != 6) {
printf("錯誤:%s 第 %d 列有 %d 欄,應為 6 欄。\n", where, FNR, NF) > "/dev/stderr"
bad = 1; next
}
if ($1 == "") {
printf("錯誤:%s 第 %d 列的檔案路徑是空的。\n", where, FNR) > "/dev/stderr"
bad = 1; next
}
if ($2 !~ /^[0-9]+$/ || $2 + 0 < 1) {
printf("錯誤:%s 第 %d 列的行號「%s」不是正整數。\n", where, FNR, $2) > "/dev/stderr"
bad = 1; next
}
if ($3 != "高" && $3 != "中" && $3 != "低") {
printf("錯誤:%s 第 %d 列的嚴重度「%s」不在 高、中、低。\n", where, FNR, $3) > "/dev/stderr"
bad = 1; next
}
rank = ($3 == "高") ? 1 : (($3 == "中") ? 2 : 3)
key = $1 SUBSEP $2
if (!(key in seen)) {
seen[key] = 1
order[++n] = key
kfile[key] = $1; kline[key] = $2; krank[key] = rank
kgroup[key] = $4; kitem[key] = $5; kdetail[key] = $6
} else {
if (rank < krank[key]) krank[key] = rank
kgroup[key] = kgroup[key] "|" $4
kitem[key] = kitem[key] "|" $5
kdetail[key] = kdetail[key] "|" $6
}
}
END {
if (bad) exit 3
if (n == 0) exit 1
for (i = 1; i <= n; i++) {
k = order[i]
sev = (krank[k] == 1) ? "高" : ((krank[k] == 2) ? "中" : "低")
print krank[k], kfile[k], kline[k], sev, kgroup[k], kitem[k], kdetail[k]
}
}
' "$@" > "$TMP"
rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
LC_ALL=C sort -t"$TAB" -k1,1n -k2,2 -k3,3n "$TMP" > "$TMP.sorted" || {
echo "錯誤:排序失敗。" >&2
exit 2
}
cut -f2- "$TMP.sorted" || {
echo "錯誤:輸出失敗。" >&2
exit 2
}
exit 0
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env sh
# swagger-detect.sh — 判斷一個專案有沒有真的啟用 Swagger(OpenAPI)文件。
# 用法:swagger-detect.sh [專案路徑](預設目前目錄)
#
# 規則(雙重確認,缺一不算支援):
# 1. 套件:在套件宣告檔裡找得到已知的 Swagger 套件。
# 2. 設定:在原始碼裡找得到實際把 Swagger 接上去的呼叫或裝飾子。
# 只找到套件不算支援。裝了沒啟用的專案很常見,只看套件會誤判,
# 讓 api-doc 稽核跑在一個根本產不出文件的專案上。
# 設定關鍵字一律挑「接線動作」,不挑 import 或 require。光是引入套件
# 不代表文件真的掛上去了。
#
# 涵蓋範圍:
# dotnet 套件 Swashbuckle.AspNetCore、NSwag.AspNetCore
# 設定 AddSwaggerGen、UseSwagger、AddOpenApiDocument、UseOpenApi
# nodejs 套件 swagger-ui-express、@nestjs/swagger、fastify-swagger(含 @fastify/swagger)
# 設定 SwaggerModule.setup、swaggerUi.setup、swaggerUi.serve、
# register 進 fastify 的 swagger 外掛
# python 套件 fastapi、flasgger、drf-spectacular
# 設定 FastAPI( 建立 app、Swagger( 掛上 flasgger、SPECTACULAR_SETTINGS、
# SpectacularAPIView
#
# 輸出(純文字,一行一個 key=value,呼叫端逐行讀就好):
# 第一行永遠是 support=yes 或 support=no。
# 之後每一組偵測到套件的技術棧輸出三行:
# stack=dotnet|nodejs|python
# package=命中的套件名,多個以半形逗號相連
# config=命中的設定關鍵字,多個以半形逗號相連;沒命中就是空字串
# 一個套件都沒找到時只有 support=no 一行。
# 範例:
# support=yes
# stack=dotnet
# package=Swashbuckle.AspNetCore
# config=AddSwaggerGen,UseSwagger
#
# 結束碼:
# 0 支援(至少一個技術棧同時命中套件與設定)
# 1 不支援(沒有任何技術棧同時命中)
# 2 參數個數不對、專案路徑不存在,或環境缺 grep
#
# 護欄:
# 掃描一律跳過 node_modules、.git、bin、obj、dist、build、venv、.venv、
# __pycache__、packages、vendor,避免把相依套件自己的原始碼當成專案設定。
# 錯誤訊息一律印繁中到 stderr,正常輸出只走 stdout。
set -u
if [ "$#" -gt 1 ]; then
echo "用法:swagger-detect.sh [專案路徑]" >&2
exit 2
fi
DIR="${1:-.}"
[ -d "$DIR" ] || { echo "錯誤:找不到專案路徑 $DIR。請確認路徑後重試。" >&2; exit 2; }
command -v grep >/dev/null 2>&1 || { echo "錯誤:環境缺 grep,無法掃描。" >&2; exit 2; }
EX1=--exclude-dir=node_modules
EX2=--exclude-dir=.git
EX3=--exclude-dir=bin
EX4=--exclude-dir=obj
EX5=--exclude-dir=dist
EX6=--exclude-dir=build
EX7=--exclude-dir=venv
EX8=--exclude-dir=.venv
EX9=--exclude-dir=__pycache__
EX10=--exclude-dir=packages
EX11=--exclude-dir=vendor
# scan <延伸正規表示式> <副檔名樣式…>:命中回 0,沒命中回 1。
scan() {
pattern="$1"
shift
for inc in "$@"; do
if grep -R -l -E -i \
"$EX1" "$EX2" "$EX3" "$EX4" "$EX5" "$EX6" \
"$EX7" "$EX8" "$EX9" "$EX10" "$EX11" \
--include="$inc" -e "$pattern" "$DIR" >/dev/null 2>&1; then
return 0
fi
done
return 1
}
# append <既有清單> <新項目>:以半形逗號相連後印出。
append() {
if [ -z "$1" ]; then
printf '%s' "$2"
else
printf '%s,%s' "$1" "$2"
fi
}
SUPPORT=no
RECORDS=""
# collect <技術棧> <套件清單> <設定清單>:有套件才留紀錄,兩者都有才算支援。
collect() {
[ -n "$2" ] || return 0
RECORDS="${RECORDS}stack=$1
package=$2
config=$3
"
[ -n "$3" ] && SUPPORT=yes
return 0
}
# ── dotnet ────────────────────────────────────────────────────────────────
DOTNET_PKG=""
DOTNET_CFG=""
for pkg in 'Swashbuckle\.AspNetCore:Swashbuckle.AspNetCore' 'NSwag\.AspNetCore:NSwag.AspNetCore'; do
if scan "${pkg%%:*}" '*.csproj' '*.fsproj' '*.vbproj' '*.props' 'packages.config'; then
DOTNET_PKG=$(append "$DOTNET_PKG" "${pkg##*:}")
fi
done
if [ -n "$DOTNET_PKG" ]; then
for cfg in AddSwaggerGen UseSwagger AddOpenApiDocument UseOpenApi; do
if scan "$cfg" '*.cs' '*.fs' '*.vb'; then
DOTNET_CFG=$(append "$DOTNET_CFG" "$cfg")
fi
done
fi
collect dotnet "$DOTNET_PKG" "$DOTNET_CFG"
# ── nodejs ────────────────────────────────────────────────────────────────
NODE_PKG=""
NODE_CFG=""
for pkg in swagger-ui-express @nestjs/swagger fastify-swagger @fastify/swagger; do
if scan "\"$pkg\"" 'package.json'; then
NODE_PKG=$(append "$NODE_PKG" "$pkg")
fi
done
if [ -n "$NODE_PKG" ]; then
# 每一項都是「接線動作」:掛路由或註冊外掛,不是 import。
for cfg in 'SwaggerModule\.setup:SwaggerModule.setup' \
'swaggerUi\.setup:swaggerUi.setup' \
'swaggerUi\.serve:swaggerUi.serve' \
'register\(.*swagger:register(swagger)'; do
if scan "${cfg%%:*}" '*.js' '*.mjs' '*.cjs' '*.ts'; then
NODE_CFG=$(append "$NODE_CFG" "${cfg##*:}")
fi
done
fi
collect nodejs "$NODE_PKG" "$NODE_CFG"
# ── python ────────────────────────────────────────────────────────────────
PY_PKG=""
PY_CFG=""
for pkg in fastapi flasgger drf-spectacular; do
if scan "$pkg" 'requirements*.txt' 'pyproject.toml' 'Pipfile' 'setup.py' 'setup.cfg'; then
PY_PKG=$(append "$PY_PKG" "$pkg")
fi
done
if [ -n "$PY_PKG" ]; then
for cfg in 'FastAPI\(:FastAPI(' \
'Swagger\(:Swagger(' \
'SPECTACULAR_SETTINGS:SPECTACULAR_SETTINGS' \
'SpectacularAPIView:SpectacularAPIView'; do
if scan "${cfg%%:*}" '*.py'; then
PY_CFG=$(append "$PY_CFG" "${cfg##*:}")
fi
done
fi
collect python "$PY_PKG" "$PY_CFG"
# ── 輸出 ──────────────────────────────────────────────────────────────────
echo "support=$SUPPORT"
[ -n "$RECORDS" ] && printf '%s' "$RECORDS"
[ "$SUPPORT" = yes ] && exit 0
exit 1