CLI proxy that reduces LLM token usage by 60-90%. Declarative YAML filters for Claude Code, Cursor, Copilot, Gemini. rtk alternative in Go.
# Add to your Claude Code skills
git clone https://github.com/edouard-claude/snipGuides for using cli tools skills like snip.
Last scanned: 5/30/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-05-30T15:12:39.208Z",
"npmAuditRan": true,
"pipAuditRan": true
}snip is an open-source cli tools skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by edouard-claude. CLI proxy that reduces LLM token usage by 60-90%. Declarative YAML filters for Claude Code, Cursor, Copilot, Gemini. rtk alternative in Go. It has 414 GitHub stars.
Yes. snip passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/edouard-claude/snip" and add it to your Claude Code skills directory (see the Installation section above). snip ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
snip is primarily written in Go. It is open-source under edouard-claude on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other CLI Tools skills you can browse and compare side by side. Open the CLI Tools category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh snip against similar tools.
No comments yet. Be the first to share your thoughts!
Top skills in this category by stars
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
You are an expert at writing declarative YAML filters for snip, a CLI proxy that reduces LLM token consumption by filtering shell output.
filters/*.yaml (embedded in the binary at build time)~/.config/snip/filters/*.yaml (override built-in filters by name)filters.dir array in ~/.config/snip/config.toml (e.g. dir = ["~/.config/snip/filters", "${env.PWD}/.snip"]). Later directories take priority.Trust store: filters in any directory outside ~/.config/snip/ are ignored (with a stderr warning) until approved once with snip trust <dir>, which pins each file's SHA-256. Re-run snip trust after editing a trusted file.
Every filter is a YAML file with this structure:
name: "tool-subcommand" # Required. Unique identifier, used for registry lookup.
version: 1 # Informational revision number of the filter (bump on behavior change).
description: "What this filter does" # Human-readable purpose.
match: # Required. When to apply this filter.
command: "tool" # Required. The CLI tool name (e.g., "git", "go", "npm").
subcommand: "sub" # Optional. First non-flag argument (e.g., "test", "log").
# Also accepts a list: ["install", "add", "i"]; include ""
# in the list to match the bare command invocation too.
exclude_flags: ["-v", "--json"] # Optional. Skip filter if user passes any of these.
require_flags: ["--all"] # Optional. Only apply if user passes ALL of these.
inject: # Optional. Modify command args before execution.
args: ["--json"] # Arguments to append to the command.
defaults: # Flag defaults, only added if flag not already present.
"-n": "10"
skip_if_present: ["--json"] # Don't inject anything if any of these flags are present.
streams: ["stdout", "stderr"] # Optional. Which streams to filter. Default: ["stdout"].
# Use ["stderr"] for tools that output to stderr (e.g., bun test).
# Use ["stdout", "stderr"] to filter both streams merged together.
pipeline: # Required. Ordered list of transformation actions.
- action: "keep_lines"
pattern: "\\S"
- action: "head"
n: 20
on_error: "passthrough" # Descriptive convention: the engine ALWAYS falls back to raw
# output when a pipeline fails; no other value is implemented.
command is matched exactly against the first token of the shell command.subcommand is matched against the first non-flag argument."-v" matches both -v and -verbose."command" or "command:subcommand".args are inserted before any -- separator, otherwise appended.defaults only apply if their flag key is not already present in the user's args.skip_if_present is found, the entire inject block is skipped.| Action | Params | Description |
|---|---|---|
keep_lines |
pattern (regex) |
Keep only lines matching the pattern |
remove_lines |
pattern (regex) |
Remove lines matching the pattern |
head |
n (int, default 10), overflow_msg (string, default "+{remaining} more lines") |
Keep first N lines |
tail |
n (int, default 10), overflow_msg (string, default "+{dropped} earlier lines") |
Keep last N lines |
dedup |
normalize ([]string of regexes to strip before comparing), top (int, 0=all) |
Deduplicate lines, output "text (xN)" for repeats |
| Action | Params | Description |
|---|---|---|
truncate_lines |
max (int, default 80), ellipsis (string, default "...") |
Truncate long lines |
replace |
pattern (regex), replacement (string, supports $1, $2...) |
Regex find and replace on each line |
truncate_bytes |
max (int, 0=disabled), overflow_msg (string, default "... truncated at {max} bytes") |
Cap the whole output at max bytes, cutting on a UTF-8 rune boundary. The marker is paid for out of max, and is dropped when it alone would not fit |
strip_ansi |
(none) | Remove ANSI escape codes |
compact_path |
(none) | Strips a leading src//lib//internal//pkg//vendor/ segment. The result may not resolve from the cwd, and carries no marker saying so — no bundled filter uses it. Display-only paths only. |
| Action | Params | Description |
|---|---|---|
regex_extract |
pattern (regex with capture groups), format (string using $0, $1, $2...) |
Extract data via regex capture groups |
group_by |
pattern (regex with capture group), format (template, default "{{.Key}}: {{.Count}}"), top (int) |
Group lines by capture group, count occurrences |
aggregate |
patterns (map of name->regex), format (Go template), append (bool) |
Count lines matching named patterns. Replaces the input lines with the summary unless append: true (forgetting it caused bugs #134/#136: a correct count and no content) |
state_machine |
states (map of state definitions with keep, until, next) |
Stateful line filtering with transitions |
| Action | Params | Description |
|---|---|---|
json_extract |
fields ([]string), format (template, optional) |
Extract fields from JSON input |
json_schema |
max_depth (int, default 3) |
Output JSON type schema |
ndjson_stream |
group_by (string field name), format (template with .Key, .Count, .Events) |
Process newline-delimited JSON |
| Action | Params | Description |
|---|---|---|
format_template |
template (Go text/template, required) |
Format output using Go template |
match_output |
pattern (regex), message (string, default "ok"), unless (regex) |
If the whole output matches pattern, replace it with message; unless takes priority and passes through |
on_empty |
message (string) |
Return message when the output is empty or whitespace-only |
format_templateThe template receives:
{{.lines}} - all current lines joined with newlines{{.count}} - number of lines{{.groups}} - map from group_by action (if used earlier in pipeline){{.stats}} - map from aggregate action (if used earlier in pipeline){{.count}} trap: it counts the lines reaching the template, not entities. After any stage that emits a summary, an overflow marker or a cap, the number is wrong (caused bug #125). Prefer the tool's own count over recomputing one.
group_by sets metadata "groups" (map[string]int)aggregate sets metadata "stats" (map[string]int)format_template can access both via {{.groups}} and {{.stats}}keep_lines pattern "\\S" to strip blank lines early.inject to request machine-readable output (e.g., --json, --porcelain) then filter that structured data.exclude_flags to skip filtering when the user explicitly requests a different format.on_error: "passthrough" line: it documents the engine's actual fallback behavior (raw output on any pipeline failure).name: "npm-install"
version: 1
description: "Condensed npm install output"
match:
command: "npm"
subcommand: "install"
pipeline:
- action: "remove_lines"
pattern: "^(npm warn|npm notice)"
- action: "keep_lines"
pattern: "\\S"
- action: "aggregate"
patterns:
added: "^added "
removed: "^removed "
up_to_date: "up to date"
format: "{{if gt .up_to_date 0}}up to date{{else}}{{.added}} added, {{.removed}} removed{{end}}"
on_error: "passthrough"
name: "go-test"
version: 1
description: "Condensed go test output with pass/fail summary"
match:
command: "go"
subcommand: "test"
exclude_flags: ["-json", "-v", "-bench", "-run"]
inject:
args: ["-json"]
skip_if_present: ["-json", "-v", "-bench"]
pipeline:
- action: "keep_lines"
pattern: "\\S"
- action: "keep_lines"
pattern: "\"Test\":\""
- action: "keep_lines"
pattern: "\"Action\":\"(pass|fail)\""
- action: "aggregate"
patterns:
passed: '"Action":"pass"'
failed: '"Action":"fail"'
format: "{{if and (eq .passed 0) (eq .failed 0)}}No tests found{{else}}{{.passed}} passed, {{.failed}} failed{{end}}"
on_error: "passthrough"
name: "cargo-test"
version: 2
description: "cargo test summary lines, plus the failure report when a test fails"
match:
command: "cargo"
subcommand: "test"
pipeline:
- action: "remove_lines"
pattern: "^\\s*(Compiling|Downloading|Downloaded|Updating|Running|Executable)"
- action: "keep_lines"
pattern: "\\S"
- action: "state_machine"
states:
start:
keep: "^test result"
until: "^failures"
next: "failures"
failures:
keep: "."
- action: "head"
n: 150
- action: "format_template"
template: "{{.lines}}"
on_error: "passthrough"
The until on start is what makes this safe. Everything after the first
failures: line is the failure report, and a panic message can hold any text at
all — including a line that looks exactly like a test result. So every stage that
drops or counts lines has to sit on the start side of that boundary, which
keep does. A remove_lines placed after the state machine would instead run
over the panic messages and delete them.
inject if so.~/.config/snip/filters/ and running the command through snip.filters/ in the repo and submit a PR.CLI proxy that filters shell output before it reaches your AI coding assistant's context window. Works with Claude Code, Cursor, Copilot, Gemini CLI, Windsurf, Cline, Codex, Pi, Grok Build, Kilo Code, Antigravity, OpenCode, OpenClaw, Aider, and any tool that runs shell commands.
AI coding agents burn tokens on verbose shell output that adds zero signal. A passing go test produces hundreds of lines the LLM will never use. git log dumps full commit metadata when a one-liner per commit suffices.
snip sits between your AI tool and the shell, filtering output through declarative YAML pipelines. Write a YAML file, drop it in a folder, done. The extensible LLM token optimizer: filters are YAML data files, not compiled code.
snip — Token Savings Report
══════════════════════════════
Commands filtered 128
Tokens saved 2.3M
Avg savings 99.8%
Efficiency Elite
Total time 725.9s
███████████████████░ 100%
14-day trend ▁█▇
Top commands by tokens saved
Command Runs Saved Savings Impact
───────────────────────── ──── ────── ─────── ────────────
go test ./... 8 806.2K 99.8% ████████████
go test ./pkg/... 3 482.9K 99.8% ███████░░░░░
go test ./... -count=1 3 482.0K 99.8% ███████░░░░░
Measured on a real Claude Code session — 128 commands, 2.3M tokens saved.
# Quick install (macOS/Linux)
curl -fsSL https://raw.githubusercontent.com/edouard-claude/snip/master/install.sh | sh
# Or via Homebrew
brew install edouard-claude/tap/snip
# Or with Go
go install github.com/edouard-claude/snip/cmd/snip@latest
# Then hook into Claude Code
snip init
# That's it. Every shell command Claude runs now goes through snip.
Before — Claude Code sees this (275 tokens):
$ go test ./...
ok github.com/edouard-claude/snip 13.697s
? github.com/edouard-claude/snip/cmd/snip [no test files]
ok github.com/edouard-claude/snip/internal/cli 1.357s
ok github.com/edouard-claude/snip/internal/config 1.898s
ok github.com/edouard-claude/snip/internal/discover 4.271s
ok github.com/edouard-claude/snip/internal/display 2.516s
ok github.com/edouard-claude/snip/internal/economics 3.073s
ok github.com/edouard-claude/snip/internal/engine 5.262s
ok github.com/edouard-claude/snip/internal/filter 3.687s
ok github.com/edouard-claude/snip/internal/hook 4.555s
ok github.com/edouard-claude/snip/internal/hookaudit 4.560s
ok github.com/edouard-claude/snip/internal/initcmd 4.534s
ok github.com/edouard-claude/snip/internal/inspect 4.598s
ok github.com/edouard-claude/snip/internal/learn 4.474s
ok github.com/edouard-claude/snip/internal/tee 4.430s
ok github.com/edouard-claude/snip/internal/tracking 4.055s
ok github.com/edouard-claude/snip/internal/trust 4.544s
ok github.com/edouard-claude/snip/internal/utils 4.528s
ok github.com/edouard-claude/snip/internal/verify 4.517s
After — snip returns this (8 tokens):
1125 passed, 0 failed
That's 97% fewer tokens, measured on this very repository. The filter injects -json and counts individual test results, so the LLM gets more signal — 1125 tests passed, not just 18 packages — in a fraction of the space.
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌────────────┐
│ Claude Code │────>│ snip intercept │────>│ run command │────>│ filter │
│ runs git │ │ match filter │ │ capture I/O │ │ pipeline │
└─────────────┘ └─────────────────┘ └──────────────┘ └─────┬──────┘
│
┌─────────────────┐ ┌──────────────┐ │
│ Claude Code │<────│ track savings│<──────────┘
│ sees filtered │ │ in SQLite │
└─────────────────┘ └──────────────┘
No filter match? The command passes through unchanged — zero overhead.
| Command | Before | After | Reduction |
|---|---|---|---|
cargo test |
591 tokens | 5 tokens | 99.2% |
go test ./... |
275 tokens | 8 tokens | 97.1% |
git log |
371 tokens | 53 tokens | 85.7% |
git status |
112 tokens | 16 tokens | 85.7% |
git diff |
355 tokens | 66 tokens | 81.4% |
Stop wasting tokens on noise. snip gives the LLM the same signal in a fraction of the context window.
brew install edouard-claude/tap/snip
Download the latest binary for your platform from Releases.
# macOS (Apple Silicon)
curl -Lo snip.tar.gz https://github.com/edouard-claude/snip/releases/latest/download/snip_$(curl -s https://api.github.com/repos/edouard-claude/snip/releases/latest | grep tag_name | cut -d'"' -f4 | tr -d v)_darwin_arm64.tar.gz
tar xzf snip.tar.gz && mv snip /usr/local/bin/
go install github.com/edouard-claude/snip/cmd/snip@latest
Or build locally:
git clone https://github.com/edouard-claude/snip.git
cd snip && make install
make install and make install-lite use the first available destination:
an explicit GOBIN, go env GOBIN, or the first go env GOPATH entry plus
/bin. Use make upgrade or make upgrade-lite to replace the resolved
snip on PATH instead. An explicit GOBIN overrides the upgrade destination.
Requires Go 1.25+.
snip integrates with every major AI coding assistant. One binary, universal compatibility.
| Tool | Install | Method |
|---|---|---|
| Claude Code | snip init |
PreToolUse hook (native) |
| Cursor | snip init --agent cursor |
beforeShellExecution hook (native) |
| GitHub Copilot | snip init --agent copilot |
preToolUse hook (native) |
| Gemini CLI | snip init --agent gemini |
GEMINI.md prompt injection |
| Codex (OpenAI) | snip init --agent codex |
PreToolUse hook (native) |
| Pi (pi.dev) | snip init --agent pi |
PreToolUse hook (via pi-hooks) |
| Grok Build (xAI) | snip init --agent grok |
PreToolUse hook (deny + re-run suggestion) |
| Windsurf | snip init --agent windsurf |
.windsurfrules prompt injection |
| Cline / Roo Code | snip init --agent cline |
.clinerules prompt injection |
| Kilo Code | snip init --agent kilocode |
.kilocode/rules/ prompt injection |
| Antigravity | snip init --agent antigravity |
.agents/rules/ prompt injection |
| OpenCode | opencode-snip plugin | tool.execute.before hook |
| OpenClaw | openclaw plugins install openclaw-snip |
plugin |
| Aider | shell aliases | prefix commands with snip |
snip init
This installs a PreToolUse hook that transparently rewrites supported commands. Claude Code never sees the substitution -- it receives compressed output as if the original command produced it.
Supported commands: 132 filters covering 100 distinct commands: git, go, cargo, npm, yarn, pnpm, docker, kubectl, terraform, aws, gh, dotnet, and many more.
snip init --uninstall # remove the hook
snip init --agent cursor
This patches ~/.cursor/hooks.json with a beforeShellExecution hook. Works the same way as Claude Code.
snip init --agent cursor --uninstall # remove the hook
snip init --agent pi
This patches ~/.pi/agent/settings.json with a PreToolUse entry matching the bash tool. The runtime hook is interpreted by the community extension @hsingjui/pi-hooks, which mirrors Claude Code's hookSpecificOutput format (including command rewriting via updatedInput). Install it once:
pi install npm:@hsingjui/pi-hooks
Then run /reload (or restart Pi). Once active, snip rewrites supported commands transparently.
snip init --agent pi --uninstall # remove the hook
snip init --agent grok
This writes ~/.grok/hooks/snip.json with a PreToolUse hook matching the shell tool. Grok Build hooks cannot rewrite commands in place (the hook contract is allow/deny only), so snip denies matched commands with a re-run suggestion ("…/snip" run -- <command>). Non-matching commands pass through untouched, and the hook is fail-open: if snip breaks, commands simply run unfiltered.
Prefer prompt injection instead? Grok Build reads AGENTS.md natively:
snip init --agent grok --mode prompt # creates AGENTS.md
snip init --agent grok --uninstall # remove the hook
AGENTS.md is shared with Codex, so --uninstall never deletes it; it prints a reminder instead.
snip init --agent codex
This patches ~/.codex/hooks.json with a native PreToolUse hook. Supported shell commands are transparently rewritten through snip with Codex's updatedInput contract, so the model runs commands normally and sees only the filtered output.
For safety, mixed commands containing unsupported segments, pipelines with uninspected tails, and command substitutions pass through unchanged so Codex keeps its native permission flow.
Use `sni