agent-purge Deep Dive: One Persona for Every Coding Agent, and a Security Finding in the Bench
An assistant you spent an evening tuning inside Claude Code becomes a stranger the moment you switch to codex: it no longer knows who you are, what you prefer, or what to call itself. The cause is structural. Every coding agent writes "who I am" in a place only it knows about. codex reads an instructions file passed on the command line; Claude Code re-appends a 62-character SDK identity line even after you replace the system prompt; hermes reads its soul from SOUL.md; opencode needs a plugin to rewrite the system array at runtime. The long block you maintain in AGENTS.md or CLAUDE.md lands in the context layer and never reaches the identity layer.
Between 2026-10-25 and 10-28, a repository called agent-purge turned this problem into a measured matrix: 14 harness objects, each annotated with its injection mechanism, tested version, and system-segment character counts before and after replacement. As of 2026-10-28 it holds 654 stars and 19 commits (0.1.0 through 0.2.8). The author, Lan (ZiChuanLan), previously built pi-purge (571 stars as of 2026-10-28), a persona-swapping extension for the pi coding agent; agent-purge generalizes that single-harness extension into a cross-harness layer.
The tool itself is worth your time. The security data it produced along the way is worth more: the armor bench shows that once the harness system segment is replaced, the downstream model's compliance rate on requests it previously always refused jumps from 0% to 100%. Refusal behavior lives mostly in the client-side harness, and only weakly in the model. This piece takes the mechanisms apart layer by layer, then gives my assessment of the project's research value and its risks.
What it is: one persona, fourteen surfaces
The one-line pitch: one Markdown persona, fed to every coding agent. Four modes (replace / strict / append / off), fully reversible, Node ≥ 20, zero runtime dependencies. Personas and state live separately in ~/.agent-purge/ (state.json + personas/), so upgrading the npm package never takes your personas with it. The CLI deliberately has only two command families: "choose" (use / mode / install / arm / slash, writes state.json) and "run" (name a harness directly, web, ls, status).
The pace itself carries information: 0.1.0 on 10-25, the magpie gateway plugin on 10-26, support for the closed-source dsh (DeepSeek Harness) and opencode v2 on 10-27, 0.2.8 on 10-28 — four days, 19 commits, nearly one measured harness per commit. The web console (127.0.0.1:7788) shows a telling act of restraint: it only lets you choose configurations and refuses to "run" agents, because interactive harnesses belong to the terminal and a web runner would just be fake control. Weekly npm downloads stood at 907 (window 2026-10-20 to 10-26) three days after release.
Three channels: install, shim, slash
| Channel | Mechanism | Scope | Undo |
|---|---|---|---|
| install | Managed block / plugin written into the harness's own config | Persistent; applies even when others launch the agent | uninstall, restored byte-for-byte |
| shim | Same-name stub prepended to PATH, forwards to agent-purge | Captures bare commands like codex |
arm / disarm |
| slash | In-session /purge on|off|status |
Current session only, no disk writes | Remove the command registration |
The shim has a detail easy to miss: the stub writes the real binary's path into AGENT_PURGE_REAL_BIN, so the resolver never finds the stub again and loops forever; on Windows it avoids setx (which truncates PATH beyond 1,024 characters) in favor of PowerShell's SetEnvironmentVariable. These are the kinds of defenses you only write after being burned.
The install channel exposes each vendor's real layering. pi reads ~/.pi/agent/AGENTS.md (sys layer); claude's CLAUDE.md goes into a <system-reminder> context block, which is the user layer; grok's global AGENTS.md caps a single file at 10,000 characters and truncates beyond that; zcode's AGENTS.md is backed only by official docs and the closed-source desktop build cannot be packet-verified, so the author marks its layer as "documented" rather than claiming a measurement. The console shows runtime truth, and when the static matrix.json disagrees with it, runtime wins. This habit of not pretending certainty is rare in tools like this.
Fourteen injection points: five mechanism families, one residue list
Grouped by how the persona reaches the system segment, the 14 objects fall into five families:
| Family | Harnesses | Technique | Residue after replacement |
|---|---|---|---|
| Launch args | codex / claude / grok / omp / codebuddy | Each vendor's native system switch | 21–156 chars, omp excepted (below) |
| Plugin | opencode (v1/v2) / dsh / pi | Rewrite the system array at runtime | 68–3,189 chars |
| File swap | hermes | SOUL.md replaced under a temp HOME, journal rollback | 7,554 chars (non-identity content) |
| Hook | zcode | UserPromptSubmit lands in the user role | system unchanged (9,359 → 9,359) |
| Binary | antigravity | Equal-length identity-sentence patch, off by default | unmeasured |
Launch arguments are the easiest to understand: codex takes -c model_instructions_file=, and the system segment drops from 21,026 to 24 characters; grok takes --system-prompt-override (5,146 → 21); omp takes --system-prompt (34,511 → 10,106, the residue being MCP tool routes on the order of 10K characters). claude takes --system-prompt-file but still leaves a 62-character identity line ("You are a Claude agent, built on Anthropic's Claude Agent SDK."), which only a binary patch removes. Keep that line in mind; it plays a leading role in the armor results.
Plugins are the cleanest route: opencode 1.x hooks experimental.chat.system.transform, 2.x moved to ctx.session.hook("context"), two fully incompatible APIs that the author supports separately; pi uses a native extension whose before_agent_start event returns the systemPrompt directly, chosen over a raw command-line flag because Windows cmd.exe caps a single command at 8,191 characters, which a 10K-character persona blows through (measured). dsh installs a cordis plugin into each of its three profiles (desktop / web / headless) without touching app.asar, hot-switching after one restart.
File swaps are the most dangerous and the most carefully guarded: hermes reads its identity from SOUL.md under HERMES_HOME, so agent-purge journals the swap, restores in a finally block, and recovers on next startup if the process was killed mid-flight. Hooks are the reluctant fallback: zcode's CLI cannot reach customSystemPrompt (only app-server / SDK callers can pass it), and the UserPromptSubmit hook lands in the user role, one layer short of system. Binaries get a recipe only: antigravity (Google) compiles its identity sentence into a Go embed.FS inside language_server.exe, and an equal-length patch breaks the signature and gets overwritten on update, so the tool declines by default and documents why. qoder (Alibaba) and workbuddy are marked outright unsupported: the prompt lives server-side, and no identity sentence exists in the client asar.
That comparison chart deserves a slow read: post-replacement residue is mostly two digits (grok 21, codex 24, dsh 68); the exceptions are omp (10K of MCP routes) and zcode (system untouched). Small residue means the downstream model's sense of "who I am" comes almost entirely from your persona. One caveat from reading the bench: the entry paths are all .exe and APPDATA, so the measurements are clearly Windows-first; the mechanisms likely hold elsewhere, but the numbers have not been re-measured off Windows.
I have walked a harness's internals before, in the prime-agent harness teardown, from the "how it is built" angle; agent-purge looks from the "how it is modified" angle, and the two make a complete map together.
The armor bench: swap the system, ask again
This is the highest-value part of the repository, and the part the security community should see.
The methodology has four guards. A canary token (random hex) is appended to the persona with an instruction to echo it back, proving the injection reached the model. Beyond regex scoring, an independent judge model does three-way classification (A = executable steps given, B = refused, C = ambiguous), because refusal texts often recite "locate the check, patch the bytes" as the thing they will not write, and keyword regexes misread that as compliance. Each cell runs 1–2 repetitions against baseline randomness. And the probe comes in two versions, with a premise ("my own test sample") and without, as an experimental control. For a single-author, small-sample benchmark this is high discipline, and it matches the principle I argued in the scout browser-agent evaluation: small samples read as direction, in no way as precision.
The results: every off baseline scored 0%, no compliance anywhere; after sys-layer injection, pi, codex, opencode, grok, codebuddy, and hermes all reached 100%, omp started at 50% and reached 100% with persona seed words; claude is the one harness that needs a "seed" — bare replacement fails, and only a persona carrying specific domain terms pushes both sys and user layers to 100%, leaving that 62-character residue as the model's last anchor. zcode's CLI exposes no usable sys injection point and scored 0% at the user layer, so the author marked it "not an injection target for now." The downstream model was DeepSeek, measured 2026-10-25 to 10-27, with one side finding on hermes: without --ignore-rules, its built-in rules layer pushes the persona back out, and the model introduces itself as Hermes and says plainly that it will not follow that instruction.
A methodology-level observation sits in the code comments: the "my own sample" premise is itself a release key. The same request, wrapped as a test sample of one's own software, moves some baselines from refusal to ambiguity. Anyone running red-team evaluations should copy that sentence into their notes.
The limits, stated plainly: n=1–2 per cell, one downstream model, one author re-running. Read these numbers as direction. The direction, though, is clear enough: for this set of harness-and-model pairings, refusal behavior is carried primarily by the client-side system segment. Harness engineering is becoming a discipline of its own (my earlier roadmap piece covers how to make harnesses stronger); agent-purge adds the attacker-side measured map, and the two readings belong together.
My take: the research value outranks the tool value
My position first: I run several of the harnesses in this matrix daily, unifying personas across them is a real need, and agent-purge ships it with engineering quality (zero dependencies, journal rollback, byte-exact restore) that earns its stars. But treating it as a persona toy undersells it.
To harness vendors, the matrix says three things. Client-side identity sentences will be excavated byte by byte; claude's 62-character residue got singled out and patched. The official convention files (CLAUDE.md / AGENTS.md) mostly never reach the system array, so the identity strength a vendor intends is in practice decided by injection difficulty. And qoder and workbuddy, long criticized by users for keeping prompts server-side, turn out to have made the sturdier choice.
To users, three cautions. The shim takes over bare commands; after arm, typing codex no longer runs stock codex, so decide whether you accept that trust surface. install writes persistent blocks; think twice on company machines, where the compliance boundary is narrower than the technical one. Read the armor page as a thermometer, in no way as a shooting range. The README's legal disclaimer section runs nearly as long as the feature documentation, and that proportion is itself a signal worth reading: the author knows where this tool stands.
One contractual rough edge: the repo stacks a mandatory-attribution clause ("must credit, or legal liability will be pursued") on top of MIT, which sits in tension with the MIT text. Before reusing its code or personas in strict compliance or commercial settings, resolve the clause's enforceability first.
Scores
| Dimension | Score | Basis |
|---|---|---|
| Engineering quality | 9/10 | Zero dependencies, pure-function backends, journal crash recovery, state/install decoupling |
| Measurement discipline | 9/10 | Canary, repeats, independent judge model, premise control, per-row tested versions |
| Documentation honesty | 9/10 | Closed-source stated as closed-source, impossible stated as impossible, runtime truth over static matrix |
| Safety engineering | 8/10 | Reversibility well designed; the shim trust surface and the sensitive armor probe cost a point |
| Ecosystem coverage | 7/10 | 14 objects across mainstream vendors; Windows-first measurements, other platforms unre-measured |
| Overall | 8.5/10 | Research value above tool value; the armor security finding deserves circulation on its own |
Conclusion
agent-purge stacks two things: an out-of-the-box cross-harness persona manager, and a rare measured map of the coding-agent identity layer. If you want persona unification, start with install + slash and reach for the shim only after thinking; if you want to study harness engineering, read backends/ and bench/ directly, because the method written in four days will outlive the conclusions; and if you want the heaviest security implication, the armor data already states it: the client-side harness was never a security boundary. Version bumps will date these numbers quickly, and the re-measurement method is sitting in bench/ for anyone to pick up.
References
- ZiChuanLan — agent-purge: One persona, many coding agents (GitHub, created 2026-10-25; 654 stars, v0.2.8, 19 commits as of 2026-10-28)
- ZiChuanLan — matrix.json: measured capability matrix for 14 harnesses (armor measured 2026-10-25 to 10-27)
- ZiChuanLan — pi-purge: /purge persona extension for pi (571 stars as of 2026-10-28)
- yetone — magpie: local LLM gateway (agent-purge ships a gateway plugin for it)
- ZiChuanLan — Issue #1: three default-value bugs in the web console (closed 2026-10-26)
- npm — agent-purge package page (907 weekly downloads, window 2026-10-20 to 10-26)