Sandbox
@alexfazio/plankton

Claude Code hooks for write-time code checks

Plankton sits in a Claude Code workspace and intercepts file edits. It formats code first, runs a stack of linters, then hands the remaining issues to dedicated Claude subprocesses that try to fix them. The repo is built around hook files in `.claude/hooks/` plus language and tool configs for Python, TypeScript, Markdown, YAML, shell, Dockerfiles, and security checks. It is meant to block bad edits early, before commits and review.

365 stars21 forksShellUpdated 6mo ago
Who it's for

Builders who want Claude Code to enforce formatting, linting, and security checks as they edit code.

What it delivers

You can catch and fix code quality problems during generation instead of after the fact.

What it does

Write-time enforcement

Blocks the agent from proceeding until edited files pass the configured checks.

Automatic formatting

Runs formatters first, including ruff, shfmt, biome, taplo, and markdownlint.

Structured lint pass

Collects remaining violations from 20+ linters as JSON for follow-up handling.

Claude-powered fixes

Delegates unresolved issues to dedicated Claude subprocesses that reason about the fixes.

Protected config defense

Blocks edits to protected linter and config files before they happen.

Language-aware setup

Detects the project stack and can install or guide setup for the needed tools.

How to get it

  1. 1Run
    git clone https://github.com/alexfazio/plankton.git
    cd plankton
    python3 scripts/setup.py  # interactive setup wizard (recommended)
    claude                   # hooks activate automatically
  2. 2If you prefer non-interactive setup, use
    bash scripts/setup.sh   # non-interactive installer (macOS + Linux)
  3. 3Recommended: disable Claude Code auto-updates. Plankton depends on undocumented CC…
    # 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 stable
  4. 4macOS
    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 tools

README

plankton

Plankton mascot

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.

quick start

git clone https://github.com/alexfazio/plankton.git
cd plankton
python3 scripts/setup.py  # interactive setup wizard (recommended)
claude                   # hooks activate automatically

That'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 stable

scripts/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 tools

Linux:

# 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-project

See docs/SETUP.md for per-language details and download URLs.

what is plankton

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.

  1. 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 uv is already installed, this also works:

    uv run --no-project scripts/setup.py
    

    Non-interactive alternative:

    bash scripts/setup.sh
    
  2. 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.

verify

# 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-test

how it works

Three 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.

what it enforces

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.

configuration

.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.

faq

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.

todos

  • 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.sh is ~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.

star history

Star History ChartStar History Chart

license

MIT

Files in the repo

Repository payload35 top-level entries
  • .claude
  • .github
  • assets
  • benchmark
  • docs
  • scripts
  • src
  • tests
  • .flake8
  • .gitignore
  • .hadolint.yaml
  • .jscpd.json
  • .markdownlint-cli2.jsonc
  • .markdownlint.jsonc
  • .oxlintrc.json
  • .pre-commit-config.yaml
  • .ruff.toml
  • .semgrep.yml
  • .shellcheckrc
  • .yamllint
  • biome.json
  • bun.lock
  • docker-compose.yml
  • Dockerfile
  • LICENSE
  • Makefile
  • package-lock.json
  • package.json
  • pyproject.toml
  • README.md
  • taplo.toml
  • tsconfig.json
  • ty.toml
  • uv.lock
  • vulture_whitelist.py

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More hooks

CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies

80k

Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.

5k
bahni-m/
code-with-quran

Read the Qur'an while Claude Code works. Start a session with 'claude --cwq' and a reader beside it walks forward through the Qur'an one ayah per prompt, resuming where you left off — in a terminal pane or a browser tab. Zero dependencies, fully offline.

48
zachahn/
vomit

Clean up Claude's token vomit with a separate LLM. Save your tokens, Opus is hopeless

193

A pre-execution guard for AI coding agents. It blocks destructive Git and file system commands, plus common attempts to access sensitive files, before a tool call runs. Supports Amp Code, Antigravity CLI, Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, Grok Build, Hermes Agent, Kimi Code, OpenClaw, OpenCode, and Pi.

1.5k