- 註解範圍腳本的路徑少一層目錄,照著寫會找不到檔案,補上 hooks 那一層。 - 建置測試跑完卻失敗時沒有分支可走,只寫得出「通過」。現在要指出指令、結束碼,並判斷失敗是不是本次改動造成的。 - 取得差異失敗時原本會安靜跳過,現在原樣帶出錯誤並停手。判不出範圍不等於沒有東西要審。 - 禁止清單原本抄了兩份,改成只指向註解範圍的正本,兩份不會各自演化。 - 第二組不再重掃樣式判得出來的項目。hook 關掉或沒接上時就沒有最後一道網,這件事寫進技能文件,不讓它默默消失。 - 差異只算一次,落成一份快照檔給六組共讀;檔名帶執行代號,同一台機器平行跑不會互相覆蓋。註解清理改成一個檔案一個 sub agent 平行跑。
8.7 KiB
name, description
| name | description |
|---|---|
| code-review | 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
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
- When a file change is complete: one file or one related group of files is done.
- 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
- This skill covers the Refactoring smells, the comment contract (group 5), and shallow modules (group 6).
- 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
-
Snapshot the review scope once, to a path that belongs to this run. In one command, expand
${TMPDIR:-/tmp}/jsc-code-review.$$.diffinto a variable, redirectgit diff(uncommitted changes) orgit 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:0the 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. -
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 group 1 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.mdis the single source of the banned and allowed lists — read it, never restate it here. Pattern-detectable items belong tojsc-hooks/hooks/comment-scope.shand stay outside this group (see Notes for what that leaves uncovered)3 Couplers smells.md group 3 4 Dispensables & Others smells.md group 4 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 group 6 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 diffagain; check every changed line and its enclosing function or class against the group's definitions and detection signals in smells.md.Each sub agent returns its 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 smells.md scale4 group this group's number, 1to65 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.shdeduplicates 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 「無發現」.
-
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:0take its stdout as the merged list — deduplicated by location and sorted by severity, 高 first and 低 last;1no group returned a row, so report the literal 「無發現」 and stop;2environment or input error, so report the script's stderr verbatim and stop, and never hand-merge as a substitute;3a 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 second3, 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. -
Report the finding list in Traditional Chinese, one line per merged row. This skill never modifies code.
jsc-hooks/hooks/write-guard.shinreviewmode 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 aPreToolUsehook, 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 olderjsc-hookstreatsreleaseas an unknown mode and exits0without clearing anything: the lock then lifts by itself once the file is older thanJSC_WRITE_GUARD_TTL(900 seconds by default), andJSC_WRITE_GUARD=offopens 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=offescape hatch are stated, and the fix decision is left to them.
Notes
jsc-hooks/hooks/comment-scope.shalready 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=offturnscomment-scope.shoff entirely, and a CLI with nocomment-scope.shwiring 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. Runjsc-review:comment-cleanupbefore 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.