Install any skill in seconds. Free to start, no credit card required.
Get Started Free →使用 Plankton 实现编写时代码质量强制执行 —— 通过钩子在每次文件编辑时进行自动格式化、代码检查,并由 Claude 驱动自动修复。
.claude/skills/xu-xiang-plankton-code-quality/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 304% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 134% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 76% | 0% |
Plankton(感谢 @alxfazio)的集成参考,这是一个针对 Claude Code 的编写时(Write-time)代码质量强制执行系统。Plankton 通过工具调用后钩子(PostToolUse hooks)在每次文件编辑时运行格式化程序和 Linter,然后启动 Claude 子进程(Subprocess)来修复智能体(Agent)未捕捉到的违规项。
每当 Claude Code 编辑或写入文件时,Plankton 的 multi_linter.sh 工具调用后钩子(PostToolUse hook)就会运行:
阶段 1: 自动格式化 (静默)
├─ 运行格式化程序 (ruff format, biome, shfmt, taplo, markdownlint)
├─ 静默修复 40-50% 的问题
└─ 不向主智能体输出任何内容
阶段 2: 收集违规项 (JSON)
├─ 运行 Linter 并收集无法自动修复的违规项
├─ 返回结构化 JSON: {line, column, code, message, linter}
└─ 仍不向主智能体输出任何内容
阶段 3: 委派 + 验证
├─ 启动带有违规 JSON 的 claude -p 子进程
├─ 根据违规复杂性路由到不同层级的模型:
│ ├─ Haiku: 格式化、导入、样式 (E/W/F 代码) — 120s 超时
│ ├─ Sonnet: 复杂性、重构 (C901, PLR 代码) — 300s 超时
│ └─ Opus: 类型系统、深度推理 (unresolved-attribute) — 600s 超时
├─ 重新运行阶段 1+2 以验证修复结果
└─ 如果清理完成则 Exit 0,如果仍存在违规项则 Exit 2(报告给主智能体)| 场景 | 智能体看到的内容 | 钩子退出码 | |----------|-----------|-----------| | 无违规项 | 无 | 0 | | 子进程修复了所有问题 | 无 | 0 | | 子进程处理后仍存在违规 | [hook] N violation(s) remain | 2 | | 建议性信息 (重复、旧工具) | [hook:advisory] ... | 0 |
主智能体只看到子进程无法修复的问题。大多数质量问题都会被透明地解决。
LLM 有时会尝试修改 .ruff.toml 或 biome.json 来禁用规则,而不是修复代码。Plankton 通过三层防护来阻止这种情况:
protect_linter_configs.sh 在修改发生前阻止对所有 Linter 配置的编辑。stop_config_guardian.sh 在会话结束时通过 git diff 检测配置更改。.ruff.toml, biome.json, .shellcheckrc, .yamllint, .hadolint.yaml 等。Bash 上的工具调用前钩子(PreToolUse hook)会阻止使用旧版包管理器:
pip, pip3, poetry, pipenv → 已阻止 (请使用 uv)npm, yarn, pnpm → 已阻止 (请使用 bun)npm audit, npm view, npm publishbash# 将 Plankton 克隆到你的项目(或共享位置) # 注: Plankton 由 @alxfazio 开发 git clone https://github.com/alexfazio/plankton.git cd plankton # 安装核心依赖 brew install jaq ruff uv # 安装 Python linter uv sync --all-extras # 启动 Claude Code — 钩子将自动激活 claude
无需安装命令,无需插件配置。当你向在 Plankton 目录中运行 Claude Code 时,.claude/settings.json 中的钩子会自动被加载。
要在你自己的项目中使用 Plankton 钩子:
.claude/hooks/ 目录复制到你的项目。.claude/settings.json 中的钩子配置。.ruff.toml, biome.json 等)。| 语言 | 必需 | 可选 | |----------|----------|----------| | Python | ruff, uv | ty (类型), vulture (死代码), bandit (安全) | | TypeScript/JS | biome | oxlint, semgrep, knip (死导出) | | Shell | shellcheck, shfmt | — | | YAML | yamllint | — | | Markdown | markdownlint-cli2 | — | | Dockerfile | hadolint (>= 2.12.0) | — | | TOML | taplo | — | | JSON | jaq | — |
| 关注点 | ECC | Plankton | |---------|-----|----------| | 代码质量强制执行 | 工具调用后钩子 (Prettier, tsc) | 工具调用后钩子 (20+ Linter + 子进程修复) | | 安全扫描 | AgentShield, security-reviewer 智能体 | Bandit (Python), Semgrep (TypeScript) | | 配置保护 | — | 工具调用前钩子阻止 + 停止钩子检测 | | 包管理器 | 检测 + 设置 | 强制执行 (阻止旧版包管理器) | | CI 集成 | — | 用于 git 的 Pre-commit 钩子 | | 模型路由 | 手动 (/model opus) | 自动 (违规复杂性 → 相应层级) |
如果同时运行 ECC 和 Plankton 钩子:
Plankton 的 .claude/hooks/config.json 控制所有行为:
json{ "languages": { "python": true, "shell": true, "yaml": true, "json": true, "toml": true, "dockerfile": true, "markdown": true, "typescript": { "enabled": true, "js_runtime": "auto", "biome_nursery": "warn", "semgrep": true } }, "phases": { "auto_format": true, "subprocess_delegation": true }, "subprocess": { "tiers": { "haiku": { "timeout": 120, "max_turns": 10 }, "sonnet": { "timeout": 300, "max_turns": 10 }, "opus": { "timeout": 600, "max_turns": 15 } }, "volume_threshold": 5 } }
关键设置:
volume_threshold — 违规数超过此值将自动升级到更高级别的模型。subprocess_delegation: false — 完全跳过阶段 3(仅报告违规项)。| 变量 | 用途 | |----------|---------| | HOOK_SKIP_SUBPROCESS=1 | 跳过阶段 3,直接报告违规项 | | HOOK_SUBPROCESS_TIMEOUT=N | 覆盖模型层级的超时时间 | | HOOK_DEBUG_MODEL=1 | 记录模型选择决策 | | HOOK_SKIP_PM=1 | 绕过包管理器强制执行 |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | pass→pass | 9,638 | 2,852 | -70% | 1 | 1 | 0% | 1,818 | 2,726 | +50% | 0 | 0 | — |
case-11 | pass→pass | 7,849 | 2,238 | -71% | 1 | 1 | 0% | 1,578 | 2,567 | +63% | 0 | 0 | — |
case-01 | fail→pass | 31,874 | 14,176 | -56% | 1 | 1 | 0% | 1,276 | 5,150 | +304% | 0 | 0 | — |
case-02 | fail→pass | 13,894 | 12,027 | -13% | 1 | 1 | 0% | 3,065 | 4,982 | +63% | 0 | 0 | — |
case-03 | fail→pass | 18,566 | 15,771 | -15% | 1 | 1 | 0% | 3,753 | 5,601 | +49% | 0 | 0 | — |
case-04 | fail→pass | 6,133 | 2,453 | -60% | 1 | 1 | 0% | 1,128 | 2,642 | +134% | 0 | 0 | — |
case-06 | fail→pass | 8,173 | 2,803 | -66% | 1 | 1 | 0% | 1,564 | 2,755 | +76% | 0 | 0 | — |
case-07 | pass→pass | 23,452 | 2,488 | -89% | 1 | 1 | 0% | 2,213 | 2,572 | +16% | 0 | 0 | — |
case-08 | fail→pass | 13,971 | 11,346 | -19% | 1 | 1 | 0% | 2,420 | 4,651 | +92% | 0 | 0 | — |
case-09 | pass→pass | 6,094 | 1,875 | -69% | 1 | 1 | 0% | 1,137 | 2,439 | +115% | 0 | 0 | — |
case-10 | pass→pass | 8,135 | 2,217 | -73% | 1 | 1 | 0% | 1,489 | 2,538 | +70% | 0 | 0 | — |
case-12 | fail→pass | 5,433 | 1,572 | -71% | 1 | 1 | 0% | 932 | 2,411 | +159% | 0 | 0 | — |
case-13 | pass→pass | 8,618 | 2,962 | -66% | 1 | 1 | 0% | 1,708 | 2,668 | +56% | 0 | 0 | — |
case-14 | fail→pass | 10,142 | 1,943 | -81% | 1 | 1 | 0% | 2,091 | 2,526 | +21% | 0 | 0 | — |
case-15 | fail→pass | 13,097 | 1,266 | -90% | 1 | 1 | 0% | 2,254 | 2,361 | +5% | 0 | 0 | — |
case-16 | fail→pass | 10,840 | 1,947 | -82% | 1 | 1 | 0% | 2,040 | 2,480 | +22% | 0 | 0 | — |
case-17 | fail→pass | 6,836 | 2,682 | -61% | 1 | 1 | 0% | 1,231 | 2,630 | +114% | 0 | 0 | — |
case-18 | fail→pass | 11,607 | 2,582 | -78% | 1 | 1 | 0% | 2,152 | 2,665 | +24% | 0 | 0 | — |
case-19 | pass→pass | 5,677 | 1,773 | -69% | 1 | 1 | 0% | 1,001 | 2,390 | +139% | 0 | 0 | — |
case-20 | pass→pass | 1,965 | 2,020 | +3% | 1 | 1 | 0% | 429 | 2,616 | +510% | 0 | 0 | — |
case-21 | pass→pass | 2,070 | 2,578 | +25% | 1 | 1 | 0% | 433 | 2,678 | +518% | 0 | 0 | — |
case-22 | pass→pass | 8,035 | 2,962 | -63% | 1 | 1 | 0% | 612 | 2,713 | +343% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +55 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.