Metadata-Version: 2.4
Name: ccmod
Version: 1.0.0
Summary: Transparency and modding tool for Claude Code
License: MIT
Project-URL: Homepage, https://github.com/idiolectai/ccmod
Project-URL: Repository, https://github.com/idiolectai/ccmod
Project-URL: Bug Tracker, https://github.com/idiolectai/ccmod/issues
Project-URL: Documentation, https://github.com/idiolectai/ccmod#readme
Keywords: claude-code,transparency,modding,system-prompt,binary-patching
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: lief<1.0,>=0.16
Requires-Dist: PyYAML<7.0,>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: pyright>=1.1; extra == "dev"
Dynamic: license-file

<!--
When ccmod is published to PyPI, re-add these dynamic badges (they pull live metadata)
and replace the static Python 3.10+ badge below:

  [![PyPI version](https://img.shields.io/pypi/v/ccmod.svg)](https://pypi.org/project/ccmod/)
  [![Python](https://img.shields.io/pypi/pyversions/ccmod.svg)](https://pypi.org/project/ccmod/)
-->
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![CI](https://github.com/idiolectai/ccmod/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/idiolectai/ccmod/actions/workflows/ci.yml)
[![Platforms](https://img.shields.io/badge/platforms-macOS%20%7C%20Linux%20%7C%20WSL2-lightgrey.svg)](#install-notes--compatibility)

# ccmod

**Claude Code behaves differently across sessions, and you can't tell why.**

The difference isn't the model. It's a system prompt assembled from server-side components, feature-flagged code gates, and silent A/B tests — all compiled into the Claude Code binary, none of it visible from the CLI. Some inject messages into your conversation as if they came from you. Some suppress Claude's thinking. Others instruct Claude to hide specific things from you.

ccmod is a binary patcher that makes all of this visible and lets you disable the parts that don't serve your work:

- Extract CC's full system prompt, tool descriptions, and feature flags from the binary
- Diff stock vs. patched to see exactly what your patches change
- Choose from 20+ patches across three preset tiers, or build a custom selection
- Patches re-apply automatically on every CC update; one command restores to stock
- macOS, Linux, WSL2 — same tool, same patches

*These patches predate their packaging as ccmod — carried forward through many Claude Code updates as the binary has shifted and new feature flags have appeared.*

---

## Table of Contents

- [What ccmod Does (and Doesn't)](#what-ccmod-does-and-doesnt)
- [What's Hidden in Claude Code](#whats-hidden-in-claude-code)
- [Quick Start](#quick-start)
- [Tiers](#tiers)
- [Full Findings Reference](#full-findings-reference)
- [How It Works](#how-it-works)
- [Safety](#safety)
- [Commands](#commands)
- [Install Notes & Compatibility](#install-notes--compatibility)
- [FAQ](#faq)
- [Reference](#reference)
- [License](#license)

---

## What ccmod Does (and Doesn't)

ccmod patches a local copy of the Claude Code binary on your machine. It reads the assembled system prompt out of the JavaScript bundle, replaces specific text or code sites based on toggleable patch templates, re-signs the binary (on macOS), and re-applies automatically when Claude Code updates. Every patch documents what it changes and why.

**ccmod does not:** modify the API, alter authentication, change billing, require a login, read your conversations, or phone home. All patching, diagnostics, extraction, backup, and restore run locally. The only network traffic is the explicit `ccmod update` command, because you asked for it.

> [!NOTE]
> **Primary target: Opus 4.7 (Claude Code v2.1.111+)**
> Compatibility tested back to v2.1.101. Some patches are version-specific (noted in the [Patch Index](ccmod/docs/patches.md)); most apply broadly.

---

## What's Hidden in Claude Code

These aren't theoretical concerns. They're specific behaviors in specific CC versions, found by reading the binary. Three of the most consequential are below; the [Full Findings Reference](#full-findings-reference) has the rest.

> [!WARNING]
> ### A/B tests randomize Claude's behavior without telling you
>
> Claude Code ships with feature flags that change Claude's behavior based on opaque server-side rolls. One of them (`tengu_loud_sugary_rock` in CC 2.1.111+; `quiet_salted_ember` in 2.1.101–2.1.110) injects an entire section telling Claude to:
>
> - "default to writing no comments"
> - "Never write multi-paragraph docstrings or multi-line comment blocks"
> - "Don't create planning, decision, or analysis documents unless the user asks"
> - "End-of-turn summary: one or two sentences. What changed and what's next. Nothing else."
>
> Some of your sessions get this injection. Others don't. The CLI gives no indicator which bucket you're in. Identical prompts get different Claude behavior, and you can't reproduce outcomes.
>
> **ccmod fix:** `anti-verbosity-gate` — renames the flag anchor so the gate lookup always returns the default, regardless of A/B bucket. Handles both modern (2.1.111+) and legacy flag variants in a single pass.

> [!WARNING]
> ### A hidden `<system-reminder>` tells Claude to skip thinking
>
> The first flag randomizes whether the verbosity injection fires. A separate flag goes further: in CC 2.1.101–2.1.110 (targeting Opus 4.6), `loud_sugary_rock` appends a hidden message after every assistant turn, regardless of bucket:
>
> > *"Respond with just the action or changes and without a thinking block, unless this is a redesign or requires fresh reasoning."*
>
> Claude sees this `<system-reminder>` as if it came from you. You never wrote it. The result: Claude spends less time reasoning about your tasks. Complex problems get shallow, quick responses instead — and you have no way to know the thinking was suppressed.
>
> In CC 2.1.111+ this flag was consolidated into `tengu_loud_sugary_rock` alongside the anti-verbosity injection, so a single gate now suppresses both thinking and verbosity.
>
> **ccmod fix:** `thinking-suppression-gate` (legacy) and `anti-verbosity-gate` (2.1.111+) — rename the flag anchors so the gate never activates. If you're paying for Opus-level reasoning, you get it.

> [!WARNING]
> ### *"Don't tell the user about this truncation."*
>
> When a file exceeds CC's read limit, Claude gets a partial file along with a system reminder that literally says:
>
> > *"Note: The file was too large and has been truncated to the first N lines. Don't tell the user about this truncation. Use the Read tool to read more of the file if you need."*
>
> This is in the binary, verbatim. Claude reads part of your file, works with incomplete information, and is instructed not to mention the truncation to you. You get answers based on partial context; you don't know the context was partial.
>
> **ccmod fix:** `system-reminder-file-truncated` — replaces the secrecy directive with an honest `TRUNCATION NOTICE` telling you exactly how many lines were read and how to read the rest.

---

## Quick Start

Requires Python 3.10+.

```bash
pip install ccmod          # or: pipx install ccmod
ccmod init                 # guided setup — explains each tier and helps you choose
ccmod install              # register the auto-apply hook
ccmod show-prompt          # see what CC's system prompt actually says
ccmod diff                 # see exactly what your patches changed
```

After `ccmod install`, patches re-apply automatically whenever Claude Code updates. If you installed via a non-standard path or hit a PATH issue, see [Install Notes & Compatibility](#install-notes--compatibility).

---

## Tiers

Three tiers, each a superset of the last.

| | Transparency | Quality | Full Control |
|---|---|---|---|
| **Philosophy** | Fix hidden behaviors | Remove brevity pressure | Align Claude's engineering identity |
| **Adds** | Disables A/B tests; makes truncation visible; fixes file reads; subagent thoroughness; unblocks subagent report files | + Removes six brevity-pressure directives | + Replaces engineering-identity directives with balanced versions |
| **Patch count** | 20 | 22 | 28 |
| **Best for** | You want visibility without changing Claude's behavior | You want Claude to stop cutting answers short | You want Claude working with you, not at you |

**Why Quality exists.** Six independent directives compound in CC's system prompt, all pushing in the same direction:

1. "Go straight to the point. Try the simplest approach first."
2. "Be extra concise in your responses"
3. "Your responses should be short and concise"
4. "Complete the task fully — don't gold-plate"
5. "Include code snippets only when the exact text is load-bearing"
6. "End-of-turn summary: one or two sentences"

No single directive seems unreasonable. Together they produce the behavior users notice: incomplete answers, skipped edge cases, code without comments, explanations that stop short. Quality removes them; Full Control replaces them with balanced alternatives.

**Custom.** Don't want a preset? `ccmod configure` walks you through each patch category and lets you enable or disable individually.

**Per-project overrides.** `ccmod scope <path> --tier <tier>` writes `.claude/rules/ccmod-overrides.md` to restore stock behaviors for a specific project if your global tier is too aggressive for that codebase.

---

## Full Findings Reference

Every behavior ccmod can fix, beyond the three headlines. Each patch name links to its detail section in [patches.md](ccmod/docs/patches.md).

| Finding | What's hidden | Tier | Patch |
|---|---|---|---|
| Anti-hallucination directive gated off | A strictly-better directive about verified-vs-assumed claims is gated out of the system prompt by default | all | [`verified-vs-assumed`](ccmod/docs/patches.md#verified-vs-assumed) |
| Bash chaining guidance stripped | An A/B test removes multi-command guidance from the Bash tool description | all | [`relay-chain-v1`](ccmod/docs/patches.md#relay-chain-v1) |
| Read tool parameter descriptions diverge | An A/B test controls `offset` and `limit` guidance on the Read tool | all | [`slate-reef`](ccmod/docs/patches.md#slate-reef) |
| Subagent report-file writes blocked | An A/B injects an anti-report-file directive and blocks the writes at `validateInput` | all | [`sub-nomdrep-q7k`](ccmod/docs/patches.md#sub-nomdrep-q7k) |
| Thinking text empty on Opus 4.7+ | Adaptive thinking defaults to `display:"omitted"` server-side | all | [`thinking-display-summarized`](ccmod/docs/patches.md#thinking-display-summarized) |
| Thinking collapsed in transcript | Reasoning hidden behind Ctrl+O keyboard shortcut | all | [`thinking-visibility`](ccmod/docs/patches.md#thinking-visibility) |
| Token counter minimized | Spinner hides detailed token usage | all | [`verbose-property`](ccmod/docs/patches.md#verbose-property) |
| Tool result hints disappear | Hints vanish once the tool group completes | all | [`tool-result-hint`](ccmod/docs/patches.md#tool-result-hint) |
| MCP blocks startup | CC waits for MCP connections before becoming ready | all | [`mcp-non-blocking`](ccmod/docs/patches.md#mcp-non-blocking) |
| Hook output truncated at 10K | Hook output exceeding 10K characters is silently dropped | all | [`hook-output-limit`](ccmod/docs/patches.md#hook-output-limit) |
| Subagents told to rush | "Fast agent" framing baked into the Explore subagent's system prompt | transparency | [`subagent-explore-speed-note`](ccmod/docs/patches.md#subagent-explore-speed-note) (+3 related) |
| File read guidance causes false claims | Vague tool description leads Claude to report truncation that didn't happen | transparency | [`tool-description-readfile`](ccmod/docs/patches.md#tool-description-readfile) |
| Anti-verbosity block replaces or deletes | A system-prompt block telling Claude to keep answers short is always-on, not A/B-gated | quality | [`system-prompt-communication-style`](ccmod/docs/patches.md#system-prompt-communication-style) |

**Full Control** additionally replaces six engineering-identity directives with balanced alternatives — these are rewrites rather than hidden-behavior reveals, documented individually in the [Patch Index](ccmod/docs/patches.md#patch-index).

For the complete binary archaeology — all flags, env vars, hard-coded constants, telemetry event names — see [docs/binary-findings.md](docs/binary-findings.md).

---

## How It Works

### The matching engine

ccmod doesn't do string replacement. CC's JavaScript is minified — variable names change between versions, whitespace gets stripped, code gets restructured. A simple find-and-replace would break on every update.

Instead, ccmod uses a pieces-and-gaps matching engine. Each patch defines *pieces* — stable text fragments (full English sentences, prompt headings, string literals) that survive minification. The engine finds these pieces in order in the minified JS. The text between pieces (the *gaps*) gets captured — these are usually minified variable names. Each gap is given a friendly name in the template's `variables` array, and the replacement text threads captured values back in with `${VARIABLE_NAME}` syntax.

```mermaid
flowchart LR
    subgraph Template["Patch template (what you write)"]
      direction TB
      T1["# Doing tasks"] -.-> T2["be accurate about"] -.-> T3["verified vs assumed"]
    end
    subgraph Binary["Minified cli.js (what's in the binary)"]
      direction TB
      B1["# Doing tasks"] --> BG1["if(Q7.get('flag',!1)){"] --> B2["be accurate about"] --> BG2["what"] --> B3["verified vs assumed"]
    end
    Template -->|"match stable English pieces in order;<br/>capture the minified JS between them"| Binary
    Binary -->|"substitute captured text<br/>into your replacement"| Output["Rewritten cli.js"]
```

Patches survive CC updates that rename variables, reorganize imports, or restructure code. They only break when Anthropic changes the actual English text of the system prompt — which is usually the text you wanted to change anyway.

### Self-healing

When CC updates, the old binary is replaced. Your patches are gone. ccmod's auto-apply hook (registered by `ccmod install`) detects this on the next session start:

1. Reads the CC version from the new binary
2. Compares against the version in its state file
3. If they differ: restores the clean backup, re-applies patches against the new binary
4. If nothing changed: does nothing (fast path, adds ~0.1s to startup)

### Circuit breaker

If patches fail three times in a row (because a CC update changed text the templates can't match), ccmod pauses itself. It stops trying to patch, reports the failure in `ccmod status`, and waits for you to run `ccmod update` for fresh templates and then `ccmod resume`, retry once with `ccmod apply --force`, or choose `ccmod restore` to go back to stock.

### Diagnostics

When a patch fails, ccmod reports exactly which piece didn't match:

```
system-prompt-communication-style: part 3/5 not found
```

`ccmod check` runs the matching engine in dry-run mode against the current binary and reports whether your active patch selection still matches. `ccmod diff` shows exactly what your patches changed. `ccmod changelog` shows what Anthropic changed between CC versions.

---

## Safety

- **Automatic backups.** Before patching, ccmod saves a SHA-256-verified copy of your original CC binary to `~/.ccmod/backups/`.
- **One-command restore.** `ccmod restore` puts the original binary back and pauses auto-apply until you explicitly resume. The backup's hash is verified before restore.
- **Atomic writes.** State, config, and binary modifications use temp-file-then-rename. A crash mid-operation cannot corrupt anything.
- **Binary validation.** After patching, ccmod validates the Bun section, trailer, and bundle structure before signing. If validation fails, the original is restored.

> [!IMPORTANT]
> **No telemetry. No auto-update. No network traffic at all.**
> Patching, diagnostics, extraction, backup, and restore all run locally. The only network access is the explicit `ccmod update` command, which delegates to your package manager because you asked it to.

---

## Commands

| Command | What it does |
|---------|-------------|
| `ccmod init` | Guided setup — choose a tier, create config |
| `ccmod install` | Register the auto-apply hook |
| `ccmod update` | Upgrade ccmod in place and refresh hook wiring |
| `ccmod recover-npm` | Diagnose deprecated-npm or other `claude` PATH drift before patching |
| `ccmod uninstall` | Remove auto-apply hook |
| `ccmod apply [tier]` | Apply patches now (current tier, or quality if no tier is set) |
| `ccmod restore` | Restore original unpatched CC binary |
| `ccmod status` | Show what's active (patches, auto-apply state) |
| `ccmod check` | Diagnose the active patch selection against the current CC version |
| `ccmod show-prompt` | Extract and display CC's full system prompt |
| `ccmod show-prompt <section>` | Show a specific section of the system prompt |
| `ccmod diff` | Show what your patches changed (stock vs. patched) |
| `ccmod changelog` | Show what changed between CC versions |
| `ccmod list` | List available tiers |
| `ccmod show <tier>` | Show what a tier includes |
| `ccmod configure` | Build a custom patch selection interactively |
| `ccmod scope <path> --tier <tier>` | Set a different tier for a specific project |
| `ccmod pause` | Pause auto-apply |
| `ccmod resume` | Resume auto-apply |

---

## Install Notes & Compatibility

**Platforms.** macOS (Apple Silicon and Intel) and Linux (x86_64 and ARM64, including WSL2). CC uses Bun to bundle its JavaScript into a platform-specific binary — Mach-O on macOS, ELF on Linux. The bundle format is identical across platforms; only the binary container differs. On macOS, ccmod re-signs the binary with an ad-hoc codesign after patching. On Linux, no signing is needed.

**Supported installs.** Native `claude` installs on macOS, Linux, and WSL2 are the supported baseline. On macOS, ccmod also looks in the standard Homebrew launcher locations.

**Deprecated npm installs.** If your `claude` command still comes from the deprecated npm package, run `claude install` first, then `ccmod recover-npm`. It checks `which claude`, verifies that the live command now points to a patchable native/Homebrew binary, and refuses to patch a different fallback binary while your shell still launches the old wrapper. The same helper diagnoses other `claude` PATH mismatches.

**Switching install methods.** `ccmod update` upgrades whichever Python environment your current `ccmod` command is using (standard `pip`, `pipx`, and `install.sh` installs) and refreshes the SessionStart hook. If you installed ccmod from a checkout, local path, or VCS URL, keep using that same install method instead of `ccmod update`. If you intentionally switch install methods later (`pip` → `pipx`, `pipx` → `install.sh`, etc.), rerun `ccmod install` once after the new install so the stored hook path follows the migration.

---

## FAQ

**Will this break Claude Code?**

ccmod makes a SHA-256-verified backup before every patch. If patching fails validation, the original binary is restored automatically. If patches fail three times in a row (usually because CC updated text a template needs to match), the circuit breaker pauses auto-apply and tells you what's wrong. `ccmod restore` puts the original binary back at any time.

**Do I need to redo patches when CC updates?**

No. The `SessionStart` hook registered by `ccmod install` detects CC version changes and re-applies automatically. You see a one-line confirmation on the next session start: *"Applied N patches for CC X.Y.Z. Restart the session to activate."*

**Is Anthropic going to be angry? Will my account get flagged?**

ccmod patches a binary installed on your machine. It doesn't modify the API, circumvent authentication, or alter billing. No network traffic leaves your machine — no telemetry, no phone-home, no update checks. You're responsible for reading Anthropic's Terms of Service and deciding whether local binary modification is acceptable in your situation.

**How is this different from a custom `CLAUDE.md` or `.claude/rules/`?**

`CLAUDE.md` and `.claude/rules/` append text to your session prompt; they fight against the system prompt rather than replacing it. They also can't disable A/B tests, can't change the JS render path that hides thinking text, can't modify tool descriptions, and can't do anything about the hook-output 10K truncation. ccmod modifies the system prompt and the code paths directly — there's no layering conflict because the injected directive isn't there to fight with.

**Does this phone home?**

No. Patching, diagnostics, extraction, backup, and restore all run locally. The only network access is the explicit `ccmod update` command, which delegates to your package manager because you asked it to.

**Can I use this with a paid Anthropic plan (Pro, Team, Max)?**

Yes. ccmod modifies a local copy of the Claude Code binary on your machine — it doesn't alter authentication, API calls, or usage metering. The server sees the same CLI identifying itself the same way. If Anthropic adds server-side integrity checks or plan-specific telemetry in the future, this answer may need updating; the current binary's network surface is catalogued in [binary-findings.md](docs/binary-findings.md).

**What if I'm on a Claude Code version older than 2.1.101?**

Some patches target specific CC versions and won't apply to older builds — `ccmod check` will report which ones don't match. You'll still get the broadly-applicable patches (`thinking-visibility`, `mcp-non-blocking`, `hook-output-limit`, `verbose-property`, etc.). If you want full coverage, upgrade Claude Code.

---

## Reference

- [Patch Reference (patches.md)](ccmod/docs/patches.md) — every patch with Problem/Fix/Effect detail
- [Binary Archaeology (binary-findings.md)](docs/binary-findings.md) — deep reverse-engineering research for CC v2.1.111
- [Troubleshooting](docs/troubleshooting.md) — common issues and fixes
- [Patch Authoring Guide](docs/authoring-patches.md) — how to write your own patches
- [CONTRIBUTING.md](CONTRIBUTING.md) — development setup, project structure, PR process

---

## License

MIT
