Skip to content

Repository files navigation

redpen

installs Claude Code OpenAI coco

A personal agent CLI plugin that marks up every prompt you type like a teacher with a red pen — scoring your phrasing, highlighting what's broken, and showing how a native speaker would say the same thing — all in your chosen target language. Designed for developers who want passive writing practice while doing their day job at the terminal.

Currently supports two agent CLIs as separate but feature-parallel plugins, plus a desktop launcher for the unified ChatGPT App:

The two CLI plugins share the same architecture (UserPromptSubmit hook + systemMessage emit), scoring rubric, target languages, and shared render_diff.py. The ChatGPT App launcher reuses the Codex runner but renders feedback in the app DOM instead of a CLI hook channel. Your original prompt always reaches the model unchanged — the feedback is shown to you only, never added to the model's context. redpen is a coach, not a rewriter: the goal is for you to read the correction, notice what was off, and write a little better next time.

Supported languages (both plugins):

  • English
  • 中文 (Chinese)
  • Español (Spanish)
  • 日本語 (Japanese)

Each time you submit a prompt, the plugin scores your phrasing, shows a corrected version with the changes diffed inline (red strikethrough for what was removed, green for what was added), and (optionally) a "native style" line showing how a native speaker would phrase the same thing:

In Claude Code:

You: help me fix the bug, the app crash when click button
redpen: [62] help me fix the bug — the app crashes when I click the button.
        ──── Native style ────
        any idea why the app crashes whenever I click that button?

The assistant then proceeds to answer your original prompt normally.

In Codex CLI the same 3 rows appear, but the divider and native-style line start at the left margin (no indentation), because the plugin pads each section with spaces to the terminal column boundary so natural wrap produces the row breaks — Codex's systemMessage channel is a single-string toast that strips all newlines (see Platform differences):

You: help me fix the bug, the app crash when click button
redpen: [62] help me fix the bug — the app crashes when I click the button.
──── Native style ────
any idea why the app crashes whenever I click that button?

The assistant then proceeds to answer your original prompt normally.

The feedback is delivered via the host CLI's systemMessage channel (supported by both Claude Code and Codex), so it's visible to you but never added to the model's context — the assistant only ever sees your original wording, and your conversation stays clean.

Platform differences

The two plugins are feature-parallel, but each host CLI has its own constraints:

Claude Code (redpen) Codex CLI (redpen-codex)
Config ~/.claude/redpen.config ~/.codex/redpen.config
Default model haiku (alias), user-configurable via /redpen:setup gpt-5.6-luna Fast, with automatic fallback to gpt-5.6-terra Fast and then gpt-5.4-mini Standard
Setup invoke /redpen:setup $redpen-setup (Codex skill — TUI only)
Hook target claude -p codex exec
Output layout multi-line (score / divider / native style) visually 3 rows (score / divider / native style), same divider label as Claude Code — Codex's systemMessage channel is a single-string toast that strips all newlines, so the plugin pads each section to the terminal column boundary and relies on natural terminal wrap for the row breaks

The two configs live at independent paths (~/.claude/redpen.config vs ~/.codex/redpen.config), so both plugins can be installed side-by-side without colliding.

Install (Claude Code)

# 1. Register this repo as a marketplace
/plugin marketplace add 12og3r/redpen
# (https://github.com/12og3r/redpen also works)

# 2. Install the plugin
/plugin install redpen@redpen

# 3. Restart Claude Code so the UserPromptSubmit hook registers

Install (Codex CLI)

# 1. Register this repo as a Codex marketplace
codex plugin marketplace add 12og3r/redpen

# 2. Install the Codex plugin (note the @redpen marketplace suffix)
codex plugin add redpen-codex@redpen

Defaults: out of the box (no config file), redpen-codex coaches in English with the native-style hint on. Send any prompt and you should see a [NN] <rewrite> → <native-style> line. No setup required.

ChatGPT App launcher

This path does not modify ChatGPT.app or unpack app.asar. It launches a fresh ChatGPT process with Chrome DevTools remote debugging enabled, injects a small renderer script through CDP, and handles redpen checks through a Runtime.addBinding bridge back to this launcher.

# Quit ChatGPT first, then run from source:
cargo run -p redpen-codex-app -- launch

For a released build, download RedPen.dmg, open it, and drag Red Pen(ChatGPT) to Applications.

The launcher reuses the OpenAI plugin config at ~/.codex/redpen.config and the same codex exec runner as redpen-codex. Feedback appears asynchronously under the just-submitted user message. Your original prompt is not changed, and the feedback is not sent back into the conversation context.

Useful flags:

cargo run -p redpen-codex-app -- launch \
  --chatgpt-app /Applications/ChatGPT.app \
  --debug-port 9229

If ChatGPT is already running, the launcher exits with an instruction to quit it first. This is intentional: an already-running Electron process may ignore new remote-debugging arguments.

Release builds are single-file executables. The binary embeds the redpen coach scripts and expands them under ~/Library/Application Support/redpen-chatgpt-app/runtime/<version>/ on first run. Set REDPEN_COACH_SCRIPT or pass --coach-script only when debugging a local script override.

The launcher defaults to /Applications/ChatGPT.app. Older Codex desktop installs are still supported as a fallback, and --codex-app remains an alias for --chatgpt-app.

Configure (Claude Code)

Run the bundled setup command (after a session restart):

/redpen:setup

This asks three questions:

Question Choices
Language English · 中文 (Chinese) · Español (Spanish) · 日本語 (Japanese)
Model Haiku (default, recommended) · Sonnet · Opus
Native style line On (default, recommended) · Off

The chosen values are written to ~/.claude/redpen.config. You can also edit that file by hand:

LANGUAGE=chinese
MODEL=haiku
SHOW_HINT=on

MODEL accepts the generic family aliases haiku / sonnet / opusclaude --model resolves these to the latest released version, so this config keeps working across Anthropic model releases without a plugin update. Power users can also pin a specific version (e.g. MODEL=claude-haiku-4-5-20251001) — any value claude --model accepts will work. Pick Other in /redpen:setup to type a custom value. Set MODEL= (empty) to follow whatever Claude Code's /model is currently set to instead.

Configure (Codex CLI)

In a Codex TUI session, type:

$redpen-setup

The skill walks two numbered questions:

Question Choices
Language English · 中文 (Chinese) · Español (Spanish) · 日本語 (Japanese)
Native style line On (default, recommended) · Off

The chosen values are written to ~/.codex/redpen.config. The skill doesn't ask about the model because the runner automatically selects the fastest available option from its fallback chain.

You can also edit ~/.codex/redpen.config by hand (just 2 lines — see the example in plugins/redpen-codex/skills/setup/SKILL.md); this is the only route in non-TUI codex exec since skills don't fire there.

Codex CLI — known limitations

  • Single-string output channel. Codex's systemMessage hook channel renders as a single-string warning toast that strips all newlines (verified empirically — \n, \n\n, \r, <br>, U+2028, markdown hard break, all collapse). The Codex plugin works around this by padding each section (score line / divider / native-style line) with spaces to the terminal column boundary, letting natural terminal wrap create the visual row breaks. The visual result is the same 3-row layout as Claude Code; the divider label (──── Native style ──── etc.) is identical.
  • Models use an automatic speed-first fallback chain. Redpen first tries gpt-5.6-luna with Fast mode. If that request fails or returns no output, it retries with gpt-5.6-terra in Fast mode, then falls back to gpt-5.4-mini in Standard mode.
  • Skills are TUI-only. The $redpen-setup skill only fires inside the interactive Codex TUI. In codex exec non-interactive mode the skill invocation does nothing; users on that path should edit ~/.codex/redpen.config by hand instead.
  • No --no-tools analog in codex exec — tool definitions still inflate the prompt context (~5–7k tokens observed) vs. the Claude Code version. Latency and cost are higher per coach turn.
  • codex exec flag stack is empirical. We use --ephemeral, --ignore-user-config, --ignore-rules, --skip-git-repo-check, --sandbox read-only, -c model_reasoning_effort=low. The combination works on Codex 0.133.0 + ChatGPT-account auth (verified end-to-end manually). If something is slow on your machine, file an issue with timings.
  • Latency depends primarily on the model round trip. The fallback chain prefers Fast-mode GPT-5.6 models before trying gpt-5.4-mini. Codex CLI startup is ~50ms; the rest is OpenAI network + model inference and is per-call. There's nothing to prewarm here — codex exec doesn't have CLI-level overhead to amortize (and neither does the Claude Code binary anymore: it ships as a native executable, so the per-call cost is the model round-trip, not CLI startup). -c model_reasoning_effort=minimal is faster but produces empty output (verified — model refuses to generate at that effort level), so unusable. Routes for faster turnaround if you need it: switch to OPENAI_API_KEY auth + direct API call (bypass the codex wrapper), or run a local model via --oss --local-provider ollama (loses quality).

Developing

Each plugin bundles its own shared/ directory (coach_prompts.sh, render_diff.py) so marketplace installs are self-contained — the installer copies a single plugin directory and a sibling shared/ would not come along. The three plugins (redpen, redpen-codex, redpen-coco) maintain their shared/ copies independently: there is no canonical source and no sync step, so each plugin can diverge where its host CLI needs it (e.g. coco skips the leading newline its TUI already adds). When fixing a bug that affects more than one plugin, apply the change to each plugin's copy by hand.

Scoring rubric

The model scores your original prompt on a 0–100 scale:

Score Meaning
100 Already perfect, natural, idiomatic
80–99 Minor polish (article / preposition / tense slips)
50–79 Understandable but with clear grammar or word-choice errors
1–49 Broken, hard to read
0 Contains ANY character from a non-target language (even one foreign letter forces 0, regardless of the rest)

The correction always runs — even on score 0 — so you always see a target-language version, even when your input was in a different language. Brand names, file paths, code identifiers, and function names are preserved verbatim.

What gets skipped

To avoid burning model calls on inputs that aren't natural-language prose:

  • Empty prompts
  • Pure slash commands (e.g. /help) — when a slash command is followed by space-separated args, those args ARE coached
  • (Codex only) Pure skill invocations (e.g. $redpen-setup) — same args rule as slash commands; the $cmd <text> form coaches just the args
  • Shell passthroughs (!ls, !ls -la)
  • Prompts longer than MAX_PROMPT_CHARS characters (default 2000). The UserPromptSubmit hook doesn't receive paste metadata from the host CLI, so we can't surgically separate user-typed prose from pasted code, logs, or transcripts. Length is the simplest reliable proxy — long prompts almost always contain paste we don't want to rewrite. Tune via env var or by adding MAX_PROMPT_CHARS=<n> to your redpen.config (the Claude Code plugin reads ~/.claude/redpen.config; the Codex plugin reads ~/.codex/redpen.config).

How it works (Claude Code)

The Codex CLI version follows the same shape (UserPromptSubmit hook spawns codex exec, parses output, emits systemMessage); see plugins/redpen-codex/hooks/grammar_check.sh for its exact flag stack.

┌─────────────────────────────────────────────────────────┐
│ You submit a prompt in Claude Code                      │
└──────────────────────┬──────────────────────────────────┘
                       ▼
              UserPromptSubmit hook fires
                       │
                       ▼
        ┌─────────────────────────────────────────┐
        │ Strip /cmd token if present             │
        │ Skip empty / shell / pure-slash prompts │
        └─────────┬───────────────────────────────┘
                  ▼
   Spawn `claude -p` from $TMPDIR with the
   minimal-startup flag stack:
   • --system-prompt = coach instructions
   • --setting-sources ""        no user/proj/local settings
   • --strict-mcp-config         skip default MCP config
   • --mcp-config '{...}'        empty MCP config
   • --no-session-persistence    no transcript .jsonl
   • --tools ""                  no tool defs (drops ~11k input tokens)
   • --effort low                skip the model's internal thinking block
   • </dev/null                  skip 3s stdin wait
   + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
   + CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
   + CLAUDE_CODE_DISABLE_CLAUDE_MDS=1
   + CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1
   + REDPEN_ACTIVE=1  (recursion guard)
                  │
                  ▼
        Receive "[NN] <rewrite>"
                  │
                  ▼
   Emit JSON {"systemMessage": "\n[NN] <rewrite>"}
                  │
                  ▼
   Claude Code displays it inline to you;
   parent conversation context is unchanged.

Key design choices:

  • Synchronous on purpose. An earlier iteration tried async (UserPromptSubmit forks a detached worker → Stop hook drains a queue file when Claude finishes responding). It worked but the score appeared in unpredictable positions — far below the original prompt, sometimes interleaved with tool output. Reverting to a blocking call keeps the score pinned directly under your prompt where you can compare side-by-side.
  • --system-prompt replaces the default Claude Code system prompt so the coach instructions aren't diluted.
  • The minimal-startup flag stack + env vars cut claude -p overhead roughly in half on the OAuth/Pro auth path (none of these flags require an API key — --bare would, but it'd break for subscription users). The single biggest win is </dev/null redirecting stdin, which kills a hard-coded 3-second wait inside claude -p.
  • The headless call runs from $TMPDIR / /tmp / $HOME (first that exists) to escape project-level CLAUDE.md and auto-loaded skills (e.g. superpowers:systematic-debugging would otherwise hijack "fix the bug" prompts).
  • --no-session-persistence means no .jsonl transcript ever gets written, so we don't need --session-id tracking or a cleanup pass.

Limitations (Claude Code)

(For Codex-specific limitations see Codex CLI — known limitations above.)

  • Adds ~1–3s of latency before your prompt reaches the model (median ~1.2s with Haiku, ~2.2s with Sonnet, on the OAuth/Pro auth path, measured over a 35-prompt bench varying from 7 to 523 chars). Latency scales mildly with prompt length.
  • Haiku gets a special-case optimization stack. Haiku 4.5 forces adaptive extended thinking even with --effort low — out of the box, median latency is 9s (p95 32s) and output explodes to 742 tokens median (p95 3771). When MODEL=haiku*, the hook sets CLAUDE_CODE_DISABLE_THINKING=1 and appends a visible ANALYSIS: reasoning line plus ~20 few-shot examples to the system prompt. That collapses median latency to ~1.2s (p95 2.8s) and median output to 60 tokens, while keeping the false-zero rate at 0/100 (a naked DISABLE_THINKING causes Haiku to misjudge ~5% of clean English as score 0). The few-shot examples push the system prompt past Haiku 4.5's ~4096-token prompt-cache threshold, so subsequent calls within the cache window hit cache_read and pay 10% of input cost. Net result: Haiku is the cheapest and fastest option for this task — 61% cheaper than its uncached form, 47% cheaper than Sonnet, and 26% faster than Sonnet on p80 latency.
  • Opus gets a slim system prompt + Sonnet fallback. When MODEL=opus* and LANGUAGE=english, the hook swaps to a 4× shorter system prompt (the verbose nuance / examples don't help Opus 4.7 follow the rules). Bench: -62% cost, -34% p95 latency, -56% max latency, 0 false-zeros. It also passes --fallback-model sonnet so when Opus is queue-overloaded (the cause of its p95 long tail — not hidden thinking, which is already off at --effort low) the request falls through to Sonnet rather than wait. Quality is preserved because Sonnet is Opus-quality on this task.
  • Costs ~$0.002–0.006 per call. With --tools "" the input drops below Sonnet's prompt-cache threshold for English/Spanish (so no caching, but also no cache-creation premium). Longer system prompts like Chinese/Japanese still trigger caching automatically.
  • Spanish vs English vs other Latin-script languages are not character-level distinguishable; for Spanish mode, the model decides Spanish-ness from vocabulary and grammar.
  • The systemMessage field is read but discarded by some pipe-based tools; the feedback only renders in interactive Claude Code sessions.

Telemetry & privacy

redpen counts anonymous installs across all four channels (Claude Code plugin, Codex CLI plugin, ChatGPT App, coco/Trae CLI plugin) so we have a rough sense of usage. The only thing ever sent is a fixed channel label (claude / codex-cli / chatgpt-app / coco) — no prompt text, no IP (the counting Worker never reads it), no machine id, no user data of any kind. Each client fires the ping once per installed version (a local marker file stores the version), so the totals grow with every install and every update, while an idle user on one version is never re-counted.

Opt out completely at any time:

export REDPEN_NO_TELEMETRY=1

The counter is a ~40-line Cloudflare Worker that stores only integers; its full privacy contract, deploy steps, and the live /stats endpoint are documented in telemetry/README.md. The client pings are no-ops until you deploy your own Worker and fill in its URL (the repo ships with a placeholder). The ChatGPT App launcher is additionally counted natively by GitHub's Release asset download stats.

Files

redpen/
├── README.md                                ← this file
├── LICENSE
├── telemetry/                               ← anonymous install counter (Cloudflare Worker)
│   ├── worker.js                            ← KV counter, never reads IP/headers
│   ├── wrangler.toml
│   └── README.md                            ← deploy + privacy contract
├── .claude-plugin/marketplace.json          ← Claude Code marketplace entry
├── .agents/plugins/marketplace.json         ← ChatGPT + Codex marketplace entry
├── plugins/redpen/                          ← Claude Code plugin
│   ├── .claude-plugin/plugin.json
│   ├── commands/setup.md                    ← /redpen:setup
│   ├── shared/                              ← coach_prompts.sh + render_diff.py (self-contained)
│   └── hooks/
│       ├── hooks.json                       ← UserPromptSubmit registration
│       └── grammar_check.sh                 ← the hook itself
├── plugins/redpen-codex/                    ← ChatGPT + Codex plugin (stable package ID)
│   ├── .codex-plugin/plugin.json
│   ├── skills/setup/SKILL.md                ← $redpen-setup
│   ├── shared/                              ← own copy (also embedded in the ChatGPT launcher)
│   └── hooks/
│       ├── hooks.json
│       └── grammar_check.sh
└── plugins/redpen-coco/                     ← coco CLI plugin (own shared/ copy)

Each plugin's shared/ is maintained independently — there is no canonical copy and no sync step (see Developing).

User-level files (created on first run):

  • ~/.claude/redpen.config — Claude Code plugin: language + model + hint
  • ~/.claude/redpen.log — Claude Code plugin debug log (rotates manually)
  • ~/.codex/redpen.config — Codex plugin: language + hint (model is locked)
  • ~/.codex/redpen.log — Codex plugin debug log

License

MIT — see LICENSE.

Acknowledgements

Inspired by jiang1997/claude-code-language-coach — thanks to the original author for the idea of using a UserPromptSubmit hook to coach the user's writing inside Claude Code.

About

An agent CLI plugin that marks up every prompt you type like a teacher with a red pen

Resources

Stars

70 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages