Write-time code quality enforcement for AI coding agents, built on Claude Code hooks.
Caution
Research project under active development. Hooks are tested against
Claude Code >= 2.1.50 (see badge). Newer CC versions usually work
but are not guaranteed. Disable CC auto-updates to prevent
silent breakage (see Quick Start). If you encounter issues, file a
report including the output of claude --version. To pin to a
specific Plankton version: git checkout v0.1.0.
git clone https://github.com/alexfazio/plankton.git
cd plankton
python3 scripts/setup.py # interactive setup wizard (recommended)
claude # hooks activate automaticallyThat's it. Plankton works by being the directory you run Claude Code from.
The hooks in .claude/hooks/ are picked up automatically — no install
command, no plugin, no config. Clone, setup, claude.
If you prefer non-interactive setup, use:
bash scripts/setup.sh # non-interactive installer (macOS + Linux)Note
Windows is not supported. Use WSL2 and follow the Linux instructions inside the WSL environment.
Tip
You can work on any codebase from inside plankton. Just tell Claude: "work on /path/to/my-project" — it will apply the same quality enforcement to that codebase while the hooks stay active.
Note
Existing codebases: when the agent edits a file, Plankton runs the configured checks for that file, including pre-existing issues. Some issues are auto-fixed on first touch; unresolved ones come back as feedback. Scope down only the Python security scanners here if needed:
"security_linter_exclusions": [".venv/", "vendor/", "node_modules/"]Recommended: disable Claude Code auto-updates. Plankton depends on undocumented CC internals — a silent auto-update can break hooks without warning.
# Option A: disable auto-updates entirely (most reliable)
echo 'export DISABLE_AUTOUPDATER=1' >> ~/.zshrc && source ~/.zshrc
# Option B: use the stable channel (~1 week behind latest, fewer regressions)
curl -fsSL https://claude.ai/install.sh | bash -s stablescripts/setup.sh installs all tools automatically. On macOS it uses
Homebrew; on Linux it downloads prebuilt binaries (no cargo or go
required). See docs/SETUP.md for manual per-language
setup.
Manual install (if you prefer not to use the setup script)
macOS:
brew install jaq ruff uv shellcheck shfmt hadolint taplo
brew install oven-sh/bun/bun
bun install # biome, oxlint
uv sync --all-extras --no-install-project # Python linting toolsLinux:
# Core tools (ruff + uv via Astral installers)
curl -LsSf https://astral.sh/ruff/install.sh | sh
curl -LsSf https://astral.sh/uv/install.sh | sh
# Binary tools — see docs/SETUP.md for download URLs
# jaq, shellcheck, shfmt, hadolint, taplo
# JS runtime + tools
curl -fsSL https://bun.sh/install | bash
bun install # biome, oxlint
# Python linting tools
uv sync --all-extras --no-install-projectSee docs/SETUP.md for per-language details and download URLs.
Code quality gate enforcement at write-time, using Claude Code hooks. The agent is blocked from proceeding until its output passes your checks — style, types, security, complexity — all enforced before commits and code review.
-
Run the Interactive Setup Wizard:
python3 scripts/setup.py
It auto-detects project languages, checks dependencies, and can guide/install missing required tools (
jaq,ruff,uv) step-by-step.If
uvis already installed, this also works:uv run --no-project scripts/setup.py
Non-interactive alternative:
bash scripts/setup.sh
-
Start a Claude Code session. Hooks activate automatically.
- Higher pass rate — write-time feedback catches bugs, type errors, and anti-patterns that would otherwise cause test failures.
- Behavioral shift — with Plankton active, the model learns from write-time feedback and produces better code during generation, not just through post-hoc formatting.
- Compound quality — improvements across multiple dimensions compound into code that is more likely to be functionally correct.
For the full motivation and design story, read the original writeup.
# Install pre-commit hooks (recommended). This makes the strict
# runtime-parity gate run automatically on git commit and enables the
# commit-message policy hook.
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg
# Install the pre-push hook. Use -f to replace any existing local
# pre-push hook instead of chaining legacy all-files runners.
uv run pre-commit install --hook-type pre-push -f
# Repo-wide baseline sweep (skip the staged-file strict runtime hook;
# it is designed for real commits and targeted reruns, not --all-files)
SKIP=plankton-strict-runtime-commit uv run pre-commit run --all-files
# Run the non-benchmark Plankton hook test suite directly
./scripts/pre_push_plankton_hooks.sh
# Manually re-run the same strict gate on currently staged files
make strict
# For intentional protected config edits, use the protected override for
# the manual check:
make strict-protected FILES=".semgrep.yml"
# ...or for an actual commit:
PLANKTON_STRICT_ALLOW_PROTECTED=1 git commit
# Run the hook self-test suite
.claude/hooks/test_hook.sh --self-testThree phases run on every file edit: auto-format first (ruff, shfmt, biome, taplo, markdownlint), then collect remaining violations as structured JSON from 20+ linters, then delegate what's left to dedicated Claude subprocesses that reason about each fix. Config files are tamper-proof — a PreToolUse hook blocks linter config edits before they happen. Model routing right-sizes intelligence to problem complexity so tokens aren't wasted on easy fixes.
See docs/REFERENCE.md for the full architecture, message flows, and configuration reference. For the motivation and design story, read the original writeup.
Style enforcement covers formatting, import ordering, naming conventions, docstring format, quote style, indentation, trailing commas, modern syntax idioms (Python 3.11+ f-strings, modern type annotations). Most of this is handled silently by auto-formatting in Phase 1. You never see these violations because they're fixed before they're reported.
Correctness checks catch unused variables, type errors (ty), dead code (vulture, Knip), security vulnerabilities (bandit, Semgrep), async anti-patterns (flake8-async), Pydantic model validation, duplicate code detection (jscpd), ShellCheck semantic analysis with all optional checks enabled, Dockerfile best practices (hadolint at maximum strictness), YAML strictness with all 23 yamllint rules configured. Phase 2 linters catch these; Phase 3 Claude instances fix them.
Architectural constraints are emerging: complexity limits (cyclomatic complexity, max arguments, max nesting depth, max statements per function), package manager compliance (blocks pip/npm/yarn, enforces uv/bun), config file protection with tamper-proof defense. These shape how code is organized rather than how it looks.
.claude/hooks/config.json controls everything: language toggles, phase
control, model routing patterns, protected files, security-linter exclusions, jscpd
thresholds, package manager enforcement modes. If the file is missing, all
features are enabled with sensible defaults. Environment variables
(HOOK_SKIP_SUBPROCESS=1, HOOK_SUBPROCESS_TIMEOUT) override config values
for quick session-level tweaks. Every rule is customizable. Configure what
gets enforced, how strictly, and for which languages. Full configuration
reference in docs/REFERENCE.md.
See docs/FAQ.md for answers to common questions: how this differs from pre-commit hooks, whether models will make this unnecessary, why agents modify linting rules, and more.
- should have an install wizard instead of manual setup, a guided script that detects your stack and configures everything
- one-click install via Claude Code marketplace would be nice
- a Claude Code skill for configuration and troubleshooting from inside a session
- Swift and Go are next
- model routing currently assumes Anthropic models, need to support any model Claude Code supports (Qwen, DeepSeek, Gemini, etc.) with a generic three-tier system users map to their provider
- per-directory rule overrides, team config profiles
- extend beyond code to catch AI writing slop in docs and READMEs (slop-guard integration)
multi_linter.shis ~1,300 lines and should split into one file per hook type- 103-test integration suite exists but needs work; Claude subprocess stochasticity makes deterministic assertions hard
- measuring LLM+Plankton vs LLM-alone would be useful but needs benchmarking expertise, contributions welcome here
Contributions are welcome.
MIT
