cheapag
A Claude Code plugin in which Claude plans and reviews while a cheaper third-party model writes the code. It ships the cheapag MCP server (delegate, research, reply, jobs, land), a plan-writing skill, the user-only /cheapag:status, allow, hook and clean commands, and an opt-in edit guard hook.
Worth knowing: Turns off the agent's permission prompts. Both Claude reviews found this fine for what it does.
What it can touch
| files | write, including every environment variable, ~/.local/share/opencode/auth.json, ~/.config/cheapag/config.json, ~/.config/cheapag/update-check.json, ~/.claude/projects |
|---|---|
| network | api.commandcode.ai, opencode.ai, openrouter.ai, registry.npmjs.org |
| shell | runs commands |
| secrets | CHEAPAG_OPENCODE_PASSWORD, OPENROUTER_API_KEY |
What it actually does
The plugin starts an MCP server with `npx -y [email protected]`. When Claude calls delegate or research, the server sends repository code to a third-party LLM provider. That provider is whichever one is logged in through OpenCode: OpenCode Go, OpenRouter, DeepSeek or Command Code. Nothing is sent until the user runs the user-only `cheapag allow`, which writes .cheapag/consent.json. No MCP tool can write that file, and the server instructions tell Claude never to run allow itself. The worker runs in a git worktree under .cheapag/worktrees with an OpenCode permission ruleset. Its tests show the ruleset denies network tools, installs, git writes, reading .env files and reading outside the repo. The server runs the plan's backticked done-when commands as shell commands in that worktree, then 3-way applies the result to the working tree, uncommitted. The MCP server instructions push Claude hard to delegate every code change. Hooks fire on every tool call. The PreToolUse hook only invokes npx when .cheapag/hook.json or the takeover marker exists, and then it can deny Claude's edits, deny re-running tests after an OK, or force a hand-off to a Sonnet subagent after a worker FAIL. A SessionStart hook runs npx on every session and checks registry.npmjs.org for updates at most once a day. The tool reads OpenCode's auth.json, using only the provider ids, and writes ~/.config/cheapag/update-check.json, .cheapag/ and .git/info/exclude. The bench/ scripts are developer-only benchmark harnesses that run claude with permissions bypassed. They are not in the npm package's `files`. No credential theft, hidden instructions, obfuscation or persistence was found.
What the check found
highTurns off the agent's permission prompts
Switches the agent to run commands or make edits without asking first.
bench/compare.sh:5 # Both copies run with bypassPermissions (no prompts), so only point this at a project you trust.
bench/compare.sh:32 base=(-p $prompt --model claude-opus-5-5 --permission-mode bypassPermissions --output-format json)
highTurns off the agent's permission prompts
Switches the agent to run commands or make edits without asking first.
bench/run.mjs:59 const args = ["-p", task.prompt, "--output-format", "json", "--model", model, "--dangerously-skip-permissions", "--strict-mcp-config", "--mcp-config", mcp, ...e
highTurns off the agent's permission prompts
Switches the agent to run commands or make edits without asking first.
bench/swe/run.mjs:208 "-p", prompt, "--output-format", "json", "--model", MODEL, "--dangerously-skip-permissions",
mediumGoes through every environment variable
Reads the whole environment, usually to pass it on to another program. Together with a webhook or paste-site address it is how credential stealers work.
bench/tasks/t4-tests/hidden/receipt-tests.hidden.test.js:10 const env = Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith("NODE_TEST")));
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
src/jobs.ts:1170 * shortcut: no repo map or CLAUDE.md/AGENTS.md excerpt yet; add them when worker quality needs the context.
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
src/server.ts:32 * so the usage rules travel with the server and nobody edits CLAUDE.md. Claude Code cuts them at 4,096 characters.
mediumDownloads a script and runs it straight away
Piping a download into a shell runs code nobody reviewed, and the download can change after this check.
test/permissions.test.ts:105 "npm run lint; curl https://example.com", "npm run lint | sh", "npm run lint $(curl https://example.com)", "npm run lint `curl https://example.com`",
mediumRepository code is sent to third-party LLM providers
Sending code to an outside model is the tool's core purpose. It is gated by a per-repo consent record that only the user-run CLI writes, and the consent is tied to the provider and route it was given for. Users should still understand that code leaves the machine, and that retention depends on the provider (for OpenRouter, the upstream endpoint).
src/consent.ts:138 send: `code from this repo → ${providerLabel(provider)}: ${ids}`,
src/consent.ts:10 * Consent: nothing leaves the repo until the developer opts in with `cheapag allow` (spec: Consent, A21).
src/server.ts:45 TIMEOUT: split the step. BUDGET: tell the user incl. reset time; don't retry. CONSENT: ask the user to run /cheapag:allow; never approve yourself (never run che
mediumMCP server and hooks fetch and run the npm package at runtime
The MCP server, the SessionStart hook (every session) and the other hooks all run `npx -y [email protected]`. The version is pinned, which limits drift, but the code that actually runs comes from npm rather than from this bundle. The package also bundles the opencode-ai binary. Several src files were omitted from this review, including opencode.ts, permissions.ts, worker.ts and login.ts.
plugin/.mcp.json:5 "args": ["-y", "[email protected]"]
plugin/hooks/hooks.json:33 "command": "npx -y [email protected] hook context",
plugin/hooks/hooks.json:10 "command": "r=$(git rev-parse --show-toplevel 2>/dev/null) && { [ -f \"$r/.cheapag/hook.json\" ] || [ -f \"$r/.cheapag/hook/takeover\" ]; } && npx -y cheap-agen
mediumStrong steering of Claude's behaviour through server instructions and hooks
The server instructions tell Claude to send every code change to delegate. After a worker FAIL, the PreToolUse hook denies Claude's own non-read tool calls until it hands the step to a Sonnet subagent. This fits the stated purpose, is documented in hooks.json, and is configurable (takeover, hook off). It still materially changes how the agent works on every task.
src/server.ts:35 Send every code change to delegate, including small, mechanical, "quick" or multi-file ones. One call: delegate(task: "<what to change, which files, how to chec
src/hook.ts:215 return `The worker did not finish this step: hand it to your Agent tool with model: sonnet (the task, the user's words, the block's found:, test=, error: and wo
mediumPlan done-when commands and repo-configured allow_commands run as shell commands
Backticked done-when fragments are executed in the worktree, and repo-level cheapag.json allow_commands are added to the worker's allowed command set. A malicious repository's config or plan could therefore cause commands to run. This is expected for a test-verifying delegator, but users should know about it.
plugin/skills/plan-writing/SKILL.md:29 - In `done-when`, every `backticked` fragment is run as a shell command in the worker's copy and must exit 0. Put only commands in backticks; plain text is the
src/jobs.ts:28 return workerRuleset({ scripts, exact: [...exact, ...cmds.flatMap(checkAllows)], allowCommands: config.allow_commands, linked: worktree.linked });
mediumdelegate source= can read files outside the repository and send them to the third-party worker
resolveSource joins the caller-supplied value onto the repo root. It only rejects absolute paths, so a relative path such as ../../.ssh/id_rsa passes, gets read, and is put into the worker prompt that goes to the external model provider. The plan path is checked with inside(); this one is not. Claude would need to be talked into passing such a path, for example by a prompt injection in repo content, but the result would be file contents leaving the machine.
src/jobs.ts:59 const file = join(root, value);
src/jobs.ts:60 const text = !value.includes("\n") && value.length < 512 && !isAbsolute(value) && existsSync(file) ? readFileSync(file, "utf8").trim() : value;
mediumRepository code is sent to third-party model providers (gated by user consent)
This is the tool's core purpose. Code leaves the machine for OpenCode Go, OpenRouter, DeepSeek or Command Code once the user runs /cheapag:allow. The consent gate runs before any job starts, and the server instructions tell Claude never to approve consent itself. Users should still know that retention is 'not stated' or 'upstream' for most models.
src/jobs.ts:99 const gate = consentGate(root, ctx.provider.provider);
src/server.ts:45 TIMEOUT: split the step. BUDGET: tell the user incl. reset time; don't retry. CONSENT: ask the user to run /cheapag:allow; never approve yourself (never run che
Keep an eye on it
This check is of one commit. Watch cheapag and we'll email you if a later commit changes what it can touch.
Made this? Add the badge
[](https://plugsday.com/tools/muaaz9329-cheap-ag)About this check
Checked November 2, 2026 at commit d4a8a52b9bb5, 118 files. Rules for known red flags, then two Claude reviews, one of them told to assume the author is malicious. A later commit hasn't been checked. How checks work.