Skip to content

zulfff/lelouch

Repository files navigation

⚡ Lelouch

Absolute command over your AI agent's token budget.

Your coding agent reads a 5,000-line test log to find 2 failures. Lelouch commands that output to obey — only the signal reaches the model.

CI npm node tests zero deps license


Lelouch (名: the power to command) — a local-first plugin that compresses noisy command output after it runs but before your AI coding agent sees it, so the model spends its context window on signal, not on 300 lines of ✓ passed.

No cloud. No API keys. Nothing leaves your machine.

The problem

Your agent runs npm test, gets 300 passing tests and 2 failures, and the entire 3,000-token log is pasted into its context. It only needed the 2 failures and the summary line. Multiply that across a session and you burn tokens — and precious context budget — on noise.

The command

✓ test case 1 passed
✓ test case 2 passed
        … 298 more lines …
✕ test 301 FAILED: expected 2 got 3
Tests: 1 failed, 300 passed, 301 total

becomes

✕ test 301 FAILED: expected 2 got 3
Error: assertion failed at foo.js:42
Tests: 1 failed, 300 passed, 301 total

… (300 more passing-test lines elided by lelouch)

Failures, errors, and summaries are never dropped. Only redundant, predictable noise is. And the full original is stashed locally so you can pull it back on demand (see The memory store).

Measured savings

From npm run bench on synthetic fixtures — estimates (≈4 chars/token) on made-up inputs. Real workloads vary. Run it yourself:

Fixture Before After Saved
test log (300 pass, 2 fail) 3,173 65 98%
npm install (200 warnings) 2,852 1,708 40%
git status (80 modified) 609 53 91%
Total 6,634 1,826 72%

⚠️ Honesty note: the "80%+ savings" you may see quoted elsewhere is a ceiling on favorable inputs, not a guarantee. A log that's already dense compresses very little. We ship a reproducible benchmark instead of a hero number.

Install

Claude Code

/plugin install lelouch

Or test locally before publishing:

git clone https://github.com/zulfff/lelouch
claude --plugin-dir ./lelouch

It runs automatically on Bash output via a PostToolUse hook, ships an MCP retrieval server, and installs a PreCompact digest hook.

OpenCode

npm install -g lelouch

Drop a plugin file in .opencode/plugins/ (project) or ~/.config/opencode/plugins/ (global):

// .opencode/plugins/lelouch.js
export { LelouchPlugin } from "lelouch/adapters/opencode.js";

Verified against the @opencode-ai/plugin v1.18.x type contract (tool.execute.after mutating output.output in place) and unit-tested. The adapter is defensive — any surprise leaves output untouched. If your OpenCode version differs, open an issue.

Codex / Gemini CLI / any shell

No output-rewriting hook API → you wrap commands. Wrap mode runs the command, compresses combined output, and preserves the exit code:

npm install -g lelouch
lelouch wrap -- npm test
alias ll='lelouch wrap --'   # then: ll npm test

Manual (Unix filter)

npm test 2>&1 | lelouch --command "npm test" --stats

The memory store

When output is very large (>~2,000 tokens), Lelouch stashes the full original in a local append-only store (~/.cache/lelouch/memory.jsonl) and tells the model its reference id. A bundled MCP server exposes:

  • memory_get({ id }) — full uncompressed text
  • memory_search({ query, id? }) — keyword search (AND per line) with line numbers

So nothing is truly lost — just deferred until the model asks.

Honesty note: this is keyword search, not semantic/vector search — fast and predictable, but it won't match paraphrases. See src/store.js.

Surviving compaction

A PreCompact hook builds a deterministic preservation digest before your context is compacted — extracting file paths, error/exit codes, stored mem_* ids, and TODO/FIXME markers, then re-injecting them so they aren't summarized away. It calls no model and does not block compaction. See hooks/pre-compact.js.

How it works (no magic)

Bash runs → PostToolUse hook → DLP redaction → compressor → updatedToolOutput → model
                                                    │
                                          (large?) stash full → mem_N ──► MCP retrieval

Claude Code's PostToolUse hook can replace a tool's result via hookSpecificOutput.updatedToolOutput. Lelouch hooks Bash, routes output to a strategy (test log / git status / directory listing / generic repeat-collapse + head-tail), and hands back the smaller version.

Design guarantees:

  • Never changes execution. Only what the model sees — exit codes, stderr, and result structure are preserved.
  • Fail-safe. Any unexpected input → the hook emits nothing and the original passes through untouched. Saving tokens is never worth corrupting a result.
  • Secrets redacted first. A regex DLP pass strips AWS/GitHub/OpenAI keys, JWTs, private keys, and *_SECRET= values before anything is stored or shown. Best-effort, not a guarantee — see src/dlp.js.

What's built

Module Status
Output compressor (test/git/ls/generic) ✅ built + tested
PostToolUse hook (updatedToolOutput) ✅ built + tested
DLP secret redaction ✅ built + tested
CLI + wrap mode ✅ built + tested
Memory store + MCP retrieval ✅ built + tested
PreCompact preservation digest ✅ built + tested
OpenCode adapter ✅ built + tested (verified vs @opencode-ai/plugin v1.18.x)
Codex / Gemini ✅ via wrap (shell-level, not native)
LLM history summarizer ❌ deliberately omitted — needs network/model, breaks local-first. The deterministic digest is the alternative.

Develop

npm test      # 22 tests, zero dependencies
npm run bench # reproduce the savings table
claude --plugin-dir .

Contributions welcome — see CONTRIBUTING.md. Security policy: SECURITY.md.

License

MIT © zulfff

Named for the tactician who commands with a word. Point it at your noisiest logs.

About

⚡ Absolute command over your AI agent's token budget — local-first compression of noisy tool output before it reaches the model. Claude Code / OpenCode / Codex / Gemini.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages