Cut your Claude Code bill by 50%.
git-status, but it tells you what to do next.
Saves tokens. Saves money. Saves turns. Works the same in interactive sessions and autonomous runs — humans pair-programming with Claude Code use it every day, not just Kevin-style headless agents. One Python file, zero deps, Python 3.9+.
Why • Four pillars • Receipt • Batching • Parallel • Input forms • Validators • Expand it • Install
# 7 ops, 1 round-trip, parallel where safe
supertool 'read:src/Module.py' 'read:src/Auth.py' 'grep:TODO:src/:20' 'map:src/'Hammer in 2026. Claude Code's default toolbelt is 1995 unix: cat one file, grep one pattern, git status returns 200 bytes of porcelain. Every tool call re-sends the entire conversation cache — system prompt, CLAUDE.md, rules, every prior turn — at 10% of input price. Read 7 files? Pay that prefix 7 times. Run git status then realize you needed ahead/behind too? Pay it twice for one decision. The bill compounds turn over turn.
Drill in 2026. supertool gives the agent variants that pack the next question into the current call:
git-status— branch + tracking + ahead/behind + dirty files + open MR/PR + suggested next step. One call, decision ready.gl-mr:NUMBER/gh-pr:NUMBER— full MR/PR dashboard: branch, pipeline, reviewer, approval, diff stat, comments. Replaces 4-5glab/ghcalls.claude-log-summary:UUID— model, duration, tool calls, tokens, cache hit %, errors-by-tool. Audit your own runs.
That's a sample. supertool ships ~40 ops out of the box (built-ins + gitlab / github / git / claude-log presets) — add your own and you're past 60 fast.
The variant is the lever. A turn saved isn't free time — it's a cached prefix you didn't re-pay.
| Pillar | What it does |
|---|---|
| Right tool | Variants pack state + guards + next-step into one call. Less to remember. |
| Batched | 7 ops, 1 round-trip. The cached prefix gets re-paid once, not seven times. |
| Parallel | Read-only ops in a batch run concurrently — ~3-5× faster on cold I/O. |
| Expandable | Add a custom op in 4 lines of JSON. Presets ship gitlab, github, git, claude-log. |
| Mode | Cache reads | Output | Turns | Savings |
|---|---|---|---|---|
| Hammer (no batching) | 436K | 1,400 | 10 | — |
| supertool | 133K | 750 | 3 | 50% |
| Pre-computed + supertool | 85.5K | 600 | 2 | 56% |
50% fewer tokens, 3-4× faster wall time. Fewer turns = fewer prefix re-reads. Multiply by task count and team size — the bill cut is real.
Three things happen once you ship variants instead of raw shell:
1. You build your own ops. Digital Process Tools built a stack on top — none ship with supertool, all written in 5-15 lines of JSON: git-commit (stage + commit + receipt), mr (push + MR + reviewer), mysql_read/mysql_write, verify_staged (phpstan + phpmd + phplint on the staged diff). Every project has its own "what's the next question I always ask" — bake the answer in, save the round-trip forever.
2. The op holds the guards. mysql_write refuses UPDATE/DELETE without WHERE. mysql_read auto-LIMIT 50s. mr can enforce branch policy and reviewer. Every guard is a class of mistake the agent can't make. Tokens saved, yes — but the session that didn't get derailed cleaning up "oops, emptied the user table" is the expensive one.
3. The agent thinks less. A variant that returns everything in one shot is a variant the agent doesn't have to think through. Thinking tokens bill at output rate. Every "let me also check..." that becomes "the op already told me" is output cost saved on top of round-trip cost.
From the DPT marketplace:
/plugin marketplace add Digital-Process-Tools/claude-marketplace
/plugin install supertool@dpt-plugins
This auto-registers the session-start hook via the plugin's hooks/hooks.json — no manual settings.json editing.
Or directly — clone the repo and symlink supertool.py onto your $PATH as supertool:
git clone https://github.com/Digital-Process-Tools/claude-supertool.git
ln -s "$(pwd)/claude-supertool/supertool.py" /usr/local/bin/supertool
chmod +x /usr/local/bin/supertoolVerify:
supertool 'read:README.md'Standalone install doesn't wire up the session-start hook (no plugin system). You get the binary; the marketplace install adds the session-start prompt that primes the model on your project's ops.
Just install. The session-start hook runs ./supertool 'introduction' 'output-format' 'ops-compact' to output the project-specific operations reference from .supertool.json. The model learns what's available and how to batch. Falls back to native Grep/Read when those are better.
Heads-up — hook output cap. Claude Code truncates hook stdout around 7KB; over that, only a ~2KB preview reaches the model and the rest is silently saved to disk. With many ops, the tail of the listing gets hidden until rediscovered mid-task.
The session-start hook uses
ops-compactto stay under the cap: examples are dropped on self-explanatory ops, and only kept on ops marked"hint": truein.supertool.json. If the body still exceeds the cap,ops-compactprepends a warning telling the model to fetch the full listing via./supertool 'ops'. Plain'ops'always returns everything.
If you want to force the model to batch via supertool — typical for autonomous / Kevin-style runs — block the competing tools at the Claude Code layer. Two paths:
Settings (interactive sessions): add a permissions.deny block to .claude/settings.json:
{
"permissions": {
"deny": ["Grep", "Glob", "LS", "Bash(find:*)", "Bash(cat:*)", "Bash(grep:*)", "Bash(ls:*)", "Bash(sed:*)", "Bash(awk:*)", "Bash(tail:*)", "Bash(head:*)"]
}
}CLI flag (claude -p bypass mode):
claude -p "..." --permission-mode bypassPermissions \
--disallowedTools "Grep,Glob,LS,Bash(find:*),Bash(cat:*),Bash(grep:*),Bash(ls:*),Bash(sed:*),Bash(awk:*),Bash(tail:*),Bash(head:*)"--allowedTools is ignored in bypass mode — always use --disallowedTools when bypassing.
~40 ops across reads, search, edits, symbol mapping, and meta. The full reference lives in docs/operations/index.md with per-category pages and a dedicated map deep-dive.
Quick examples:
./supertool 'read:src/Foo.py' 'grep:TODO:src/' 'map:src/'Three ways to pass arguments. Full reference: docs/input-forms.md.
- Colon-CLI (default) —
read:PATH:OFFSET:LIMIT. Use:::when content contains colons:edit:::OLD:::NEW:::PATH. @fileroute — JSON payload foredit/replace_lines/paste/vimwhen content is multi-line or shell-hostile:edit:@.max/my-edit.json.batch:@file— mixed reads + writes in one round-trip:batch:@.max/ops.json(bare array or{continue_on_error, ops}wrapper).
Every mutating op (edit, replace, replace_lines, paste, vim) runs matching validators on the result. Syntax fail → atomic rollback. The model gets an immediate error receipt and retries cleanly.
Example: edit a .json file with a missing comma → jsonlint catches it → file reverts → receipt shows the parse error with line/col.
18 validators bundled out of the box (PHP, XML, JSON, YAML, INI, Python syntax + types, Bash, JS, TS, SCSS, Markdown, Ruby, Dockerfile, Go, Terraform, Rust, TOML). Graceful skip when toolchain missing.
Full reference: docs/validators.md — bundled list, how they hook in, adding your own.
Formatters run after every edit, before validators — edit → format → validate → rollback if validate fails. They mutate the file in place (prettier --write, gofmt -w) so validators always see canonical output.
prettier ships as the first bundled formatter. rollback_on_fail defaults to false — formatters are cosmetic; the validator is the safety net.
Full reference: docs/formatters.md — config shape, bundled formatters, adding your own (gofmt, black, rustfmt, phpcbf).
Supertool works with no configuration. The .supertool.json is optional — it enables self-documenting ops for LLM onboarding via ./supertool 'introduction' 'ops'. Create one in your project root; supertool walks up from cwd to find it. A starter template ships as .supertool.example.json.
Full reference (sections, builtin-ops overrides, custom ops, aliases, dispatch order, placeholders, env vars): docs/configuration.md.
8 presets ship out of the box (git, github, gitlab, claude-log, hashnode, devto, bluesky, xml). Each has a dedicated reference page in docs/presets/ covering ops, common workflows, env vars, and authoring notes.
Writing your own: see docs/contributing.md.
The check:PRESET:PATH op still works — it reads from the ops section first, then falls back to .supertool-checks.json for backward compatibility. New projects should use direct ops (mypy:file) instead of check:mypy:file.
Heuristic grep is fast but lies. A real language server (intelephense, pyright, typescript-language-server, gopls, rust-analyzer) knows where every symbol is defined — but spawning one per CLI call pays a 5-60s cold-index every time.
Supertool ships a long-lived MCP daemon as the mcp preset. The daemon owns one MCP server subprocess (typically cclsp wrapping an LSP), stays warm across supertool invocations, and answers resolve / refs / workspace via Unix socket in milliseconds.
# 1. install LSP + MCP↔LSP bridge
npm install -g intelephense cclsp
# 2. point cclsp at the LSP
cat > .claude/cclsp.json <<'EOF'
{ "servers": [{ "extensions": ["php"], "command": ["intelephense", "--stdio"] }] }
EOF
# 3. wire supertool: add "mcp" preset + mcp block in .supertool.json
# (see docs/mcp-integration.md for the JSON shape)
# 4. use it — first call auto-spawns the daemon, subsequent calls hit warm LSP
./supertool 'resolve:My\Namespace\TargetClass:src/Caller.php'
# → My\Namespace\TargetClass → /abs/path/src/My/Namespace/TargetClass.php (<1s warm)Ops shipped by the preset: mcp_daemon, mcp_status, mcp_stop, mcp_stop_all.
LSP-backed ops once wired: resolve, refs, diag, hover, rename, and per-section LSP routing inside workspace.
Full reference (architecture, all five LSP ops, recipe for adding a new MCP server / language, tool name reference, troubleshooting): docs/mcp-integration.md.
Notifiers are validators' read-friendly sibling. Same hooks_into / match shape, but spawn-and-forget — fire on reads as well as writes, never block the parent op, no rollback semantics. They exist so external tools can tap supertool's op stream: editor sync, Slack pings, audit logs, anything.
"notifiers": {
"my-observer": {
"cmd": "python3 observe.py {op} {file} {line} {line_end} {before_file}",
"match": "*",
"hooks_into": ["edit", "replace", "paste", "vim", "around_line", "between", "read"]
}
}Placeholders: {op}, {file}, {line}, {line_end}, {before_file} (pre-edit content path for mutating ops), {supertool_dir}. Unset values render as empty strings.
Full reference: docs/notifiers.md.
The flagship notifier consumer. A VSCode/Cursor extension listens on a Unix socket; supertool writes one JSON event per op. When the agent edits a file, Cursor opens it in a side-by-side diff view (before vs after). When the agent reads a range, the lines highlight in blue, the editor scrolls to center, and the highlight fades after 4 seconds. Status bar shows the recent op with op-type icons.
Closest thing to pair-programming with an autonomous agent: the agent's work becomes visible in your editor as it happens, no extra commands.
One script:
bash notifiers/cursor-witness/install.sh # Cursor (default)
bash notifiers/cursor-witness/install.sh --vscode # also VSCodeThe script checks Node ≥18, compiles the TypeScript extension, symlinks it into the editor's extensions directory, and prints the JSON snippet to drop into your project's .supertool.json notifier block. Reload your editor (Cmd+Shift+P → Developer: Reload Window) — status bar should show $(eye) Max: idle.
supertool's own .supertool.json already wires cursor-witness — every edit to supertool source surfaces in Cursor for whoever's running the agent locally. The simplest dogfood signal.
When the agent fires an op but Cursor doesn't react, enable the notifier debug logger:
SUPERTOOL_NOTIFIER_DEBUG=1 ./supertool 'edit:::OLD:::NEW:::FILE'
tail -f /tmp/supertool-notifier-debug.logOr set "notifier_debug": true in your .supertool.json for persistent traces. Override the log path with SUPERTOOL_NOTIFIER_DEBUG_LOG=/path/to/log.
- Setup, settings, troubleshooting: docs/cursor-witness.md
- Notifier protocol (generic): docs/notifiers.md
When rtk is installed, supertool automatically delegates read, grep, and wc to RTK for compressed output. No configuration needed — detected via which rtk at first use.
- With RTK + compact: uses
rtk read --level aggressive(maximum compression) - With RTK, no compact: uses
rtk read(RTK formatting, no stripping) - Without RTK + compact: native regex-based blank/comment stripping
- Without RTK, no compact: supertool's own output (default)
RTK is optional. Supertool works identically without it — RTK is just an accelerator.
Six or seven ops per call is routine; two is too few.
supertool \
'read:src/Module.py' \
'read:src/Permissions.py' \
'read:src/Options.py' \
'grep:extends:src/:20' \
'grep:@related:src/:10' \
'glob:src/Components/**/*.xml' \
'glob:src/EventsManagers/*.py'One round-trip. Seven ops worth of output. The session-start hook reminds the model of this each session.
The tool auto-promotes these wasted patterns silently, but you should still recognize them and batch up front:
glob:concrete/path.xmlfollowed byread:concrete/path.xml— glob on a path with no wildcards is useless; justread:. SuperTool auto-reads it.grep:FOO:single_file.pyfollowed byread:single_file.py— same file, two turns. SuperTool auto-reads if the file is < 20KB with a match.- A second SuperTool call whose ops could have fit in the first.
Self-check: if the output contains [auto-read: ...], SuperTool just salvaged a wasted turn you asked for. Tighten your next prompt to batch up front.
Every SuperTool call is logged to /tmp/supertool-calls.log with this format:
2026-04-16 21:05:42 | user=alice ppid=74394 entry=cli | ops=3 out=12400b | read:a.py read:b.py grep:X:src/:20
Fields:
user=— the shell userppid=— parent process (stable within one Claude Code session, useful for grouping)entry=— how Claude Code was invoked (cli,sdk, etc.)ops=N— number of ops in this callout=Nb— output bytes emitted to the model
awk -F'|' '{ for (i=1;i<=NF;i++) if ($i ~ /ops=/) print $i }' /tmp/supertool-calls.log \
| sort | uniq -c | sort -rnA healthy run has most calls at ops=3+. A run dominated by ops=1 means the model is using SuperTool but not batching — tighten the system prompt.
awk -F'|' '
{ for (i=1;i<=NF;i++) if ($i ~ /ops=/) { gsub(/[^0-9]/,"",$i); t+=$i; n++ } }
END { printf "%d ops in %d calls → %d round-trips saved vs all-single\n", t, n, t-n }
' /tmp/supertool-calls.logEach saved round-trip avoids one prefix cache re-read. The bigger your prefix, the bigger the saving per trip.
Every path arg supertool sees is checked against the current working directory. A malicious .supertool.json or prompt-injected op like paste:~/.ssh/authorized_keys:::pwned or read:/etc/passwd is rejected with a clean error. Symlinks crossing the boundary are caught (realpath follows them). NUL bytes rejected early. See issue #146 for the full threat model.
Opt-out (any one is enough):
# 1. Env var — CI / one-off:
export SUPERTOOL_ALLOW_OUTSIDE_CWD=1// 2. Project-pinned in .supertool.json (most ergonomic for daily dev):
{ "allow_outside_cwd": true }Env var only counts the literal "1". "0", "false", empty — all stay strict (fail closed). Env precedence over JSON for one-off override.
Default excludes (grep / glob / tree / map) prune .env/, .max/, .ssh/, .aws/, .gnupg/, .kube/, .docker/, .terraform/, .chef/, .npm/, secrets/, credentials/ so tokens don't surface into an LLM's context.
Vim shell verbs (:!, :%!, :r !) are disabled by default — they're full shell exec inside a vim macro, full RCE if a prompt-injected payload reaches them. Opt-in:
export SUPERTOOL_ALLOW_VIM_SHELL=1Editor verbs (i/a/o/d/s/etc.) work unconditionally. See issue #147.
See docs/contributing.md — custom ops, presets, validators, running tests, submitting upstream.
Linux/macOS: works out of the box.
Windows: works via Git Bash or WSL (the plugin's hooks/session-start.sh + .githooks/pre-push are bash scripts; the Python tool itself is cross-platform). Native cmd.exe / PowerShell without bash won't fire the hooks.
Paths with spaces: fine. Arguments arrive via sys.argv pre-tokenized by the shell, so supertool "'read:/home/jo bob/file.py'" works unchanged.
Windows drive letters: the tool recognizes C:\... and D:/... automatically and reassembles them after colon-splitting. So supertool 'read:C:\Users\file.py' and supertool 'grep:needle:C:/src:20' both parse correctly. If you hit edge cases, forward slashes (C:/path) work everywhere on Windows too.
Temp/log location: the call log uses tempfile.gettempdir() — macOS: /var/folders/.../T/supertool-calls.log, Linux: /tmp/supertool-calls.log, Windows: %TEMP%\supertool-calls.log.
- One file.
supertool.pyis ~980 LoC (16 ops, 3 integration tiers). No package, nosetup.py, no required deps. Drop in and use. - Python 3.9+. macOS ships 3.9 via CommandLineTools; we don't force upgrades.
- Supertool isn't an MCP server. For the ops supertool ships (run a script, return output), MCP would be ceremony — Bash-invoked binaries are simpler, faster, and plug into Claude Code's existing
--allowedTools/--disallowedToolsflow. But supertool consumes MCP servers when you want LSP-grade accuracy onresolve/refs/workspace— see the MCP integration above. - Trade Python work for LLM tokens. LLM compute is expensive; local CPU is cheap. Any time the model would spend tokens computing, parsing, formatting, or finding — supertool should spend milliseconds instead. Richer op output (state hints, guards, semantic anchors, auto-formatting, syntax checks) is not feature creep — it's the whole thesis. Heavy Python is fine if it shaves tokens off the model side.
Community License — free for personal, educational, and internal business use. © 2026 Digital Process Tools.
