Compatibility: verified with Claude Code 2.1.207.
Claude Code's main system prompt can be replaced at launch by a wrapper that assembles ordered Markdown files and passes the result through --system-prompt-file. A separate --append-system-prompt-file can add a final persona layer. Tool descriptions, agent prompts, reminders, and /compact templates are separate client-embedded fragments: the wrapper does not replace them, but many can be patched independently in Claude Code's bundled JavaScript. Every binary change requires a version-specific, fail-closed patch and a controlled staging → validation → installation workflow.
| Layer | Mechanism | Binary patch required |
|---|---|---|
| Main system prompt | --system-prompt-file plus an optional --append-system-prompt-file |
No |
| Built-in tool descriptions and agent prompts | tweakcc prompt fragments or a targeted patch in the bundled JavaScript |
Yes |
Built-in /compact templates |
Coordinated replacement of the compact prompt variants | Yes |
Treat these as separate operational layers. Updating Markdown prompt blocks changes the launch-time system prompt, but it does not rewrite client-embedded tool, agent, or utility prompts.
launcher or service
→ scripts/cc-wrapper.sh
→ concatenate custom_prompt/[0-9]*.md in filename order
→ --system-prompt-file /tmp/cc_custom_sp_<model>.md
→ --append-system-prompt-file persona.md
→ Claude Code executable
--system-prompt-file replaces Claude Code's default main system prompt for that process. It is not an append-only customization. persona.md is then appended as the final prompt layer.
The reference implementation is under examples/:
examples/
├── scripts/
│ └── cc-wrapper.sh
├── custom_prompt/
│ ├── 00_core.md
│ ├── 20_tools.md
│ ├── 30_engineering.md
│ ├── 40_memory.md
│ └── 50_compact.md
└── persona.md
Numeric prefixes define assembly order. The block names are organizational; only the numeric filename ordering is significant. 50_compact.md is a Markdown file describing the writing rules you want /compact output to follow.
The example wrapper expects CC_BIN to identify the real Claude Code executable. It must not point back to the wrapper itself. CUSTOM_PROMPT_DIR and PERSONA_PROMPT_FILE can override the default example paths.
- If the caller already supplies
--system-promptor--system-prompt-file, the wrapper passes the invocation through unchanged. - If no numbered Markdown blocks exist, the wrapper falls back to append-only mode when
persona.mdexists. - If numbered blocks exist but
persona.mddoes not, the assembled main prompt is still installed without the appended persona layer. - Prompt blocks are reassembled on every launch, so a new Claude Code process sees the current files.
The following inputs are not part of the single main system-prompt file and are still injected by Claude Code at runtime:
- tool descriptions;
- dynamic
system-remindermessages; - context produced by hooks;
- some subagent helper prompts;
- built-in compact summary templates;
- Claude Code client protocol and service-side constraints.
Replacing the main system prompt therefore does not remove every instruction or constraint in Claude Code. Many client-embedded items in this list can be patched separately; hook output, tool implementation, client protocol, and service-side constraints are different mechanisms and are not changed by editing prompt text.
Claude Code builds much of its runtime instruction set from prompt fragments embedded in its JavaScript bundle. Patching one of these fragments changes what the model is told about a tool or workflow. It does not, by itself, change the tool's parameter schema, permission checks, executable implementation, or any service-side behavior.
| Client-embedded component | Examples | What a prompt patch can change |
|---|---|---|
| Built-in tool descriptions | Agent, Bash, Read, Edit, AskUserQuestion | Guidance about when and how the model should call the tool |
| Built-in agent prompts | General-purpose, Explore, Plan, worker/fork agents | Task scope, completion criteria, reporting format, and handoff expectations |
| Utility prompts | /compact, WebFetch summarization, command analysis, metadata generators |
The structure and emphasis of generated utility output |
| Client-side reminders | Version-dependent dynamic reminders present in the bundle | The reminder text sent by the client when its runtime condition is met |
The list is version-dependent. For example, Claude Code 2.1.207 exposes several separate fragments involved in subagent reporting:
tool-description-agent-usage-notes
tool-description-agent-simple-usage-notes
agent-prompt-general-purpose
agent-prompt-worker-fork
system-prompt-tool-usage-subagent-guidance
These fragments address different sides of the exchange: instructions shown to the parent model, instructions given to the subagent, and conditional Agent-tool variants. Changing only one fragment may leave another runtime path unchanged.
Prefer the prompt catalog before searching minified JavaScript manually:
tweakcc --list-system-prompts 2.1.207For a version managed by tweakcc, prompt fragments are materialized under ~/.tweakcc/system-prompts/ or $XDG_CONFIG_HOME/tweakcc/system-prompts/. Editing a catalogued fragment is safer than matching arbitrary minified code because tweakcc already tracks the fragment's identity, variables, and source version.
For an unlisted fragment, unpack a staging binary and search for a distinctive sentence from the instruction:
tweakcc unpack ./staging/claude.js ./staging/claude
TARGET_ANCHOR='a distinctive sentence from the target instruction'
rg -o -F "$TARGET_ANCHOR" ./staging/claude.js
rg -o -F "$TARGET_ANCHOR" ./staging/claude.js | wc -lUse natural-language text as the anchor, not minified variable or function names. Before defining a replacement boundary, inspect the enclosing string or template literal and check for:
- alternate full, simple, foreground, background, team, or fork variants;
- conditional interpolation such as
${...}; - shared prefixes or suffixes used by other prompts;
- the exact number of occurrences in the verified version.
An anchor that is unique in one release is not assumed to be unique in the next release.
For a target that is not handled through tweakcc's prompt files, a minimal exact-fragment patcher should verify both its input and output. The original and replacement files in this example contain the exact serialized JavaScript fragments, not a guessed Markdown rendering:
#!/usr/bin/env python3
import argparse
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("source", type=Path)
parser.add_argument("output", type=Path)
parser.add_argument("original_fragment", type=Path)
parser.add_argument("replacement_fragment", type=Path)
parser.add_argument("--expected-count", type=int, required=True)
args = parser.parse_args()
source = args.source.read_text(encoding="utf-8")
original = args.original_fragment.read_text(encoding="utf-8")
replacement = args.replacement_fragment.read_text(encoding="utf-8")
if args.expected_count < 1:
raise SystemExit("expected count must be at least one")
if args.source.resolve() == args.output.resolve():
raise SystemExit("output must be a new file, not the source file")
if not original or original == replacement:
raise SystemExit("original and replacement fragments must be non-empty and different")
actual_count = source.count(original)
if actual_count != args.expected_count:
raise SystemExit(
f"original fingerprint count mismatch: expected {args.expected_count}, "
f"found {actual_count}"
)
replacement_count_before = source.count(replacement)
patched = source.replace(original, replacement)
if patched.count(original) != 0:
raise SystemExit("original fingerprint remains after replacement")
if patched.count(replacement) != replacement_count_before + args.expected_count:
raise SystemExit("replacement fingerprint count mismatch")
args.output.write_text(patched, encoding="utf-8")This pattern is appropriate only when the complete serialized fragment has a stable, exact boundary. For JavaScript template literals, preserve required ${...} interpolation and correctly encode backslashes, backticks, and literal ${ sequences. Use a dedicated bounded patcher when several related templates must be replaced together; the compact patch described below is one such case.
The patcher must write a new output file only after every check succeeds. Do not patch an active installation in place.
An upgrade replaces the patched JavaScript bundle. Reapply built-in prompt changes through the same controlled workflow:
- obtain the target version in staging and regenerate its
tweakccprompt catalog; - review upstream changes to every customized fragment, including its variables and conditional variants;
- update anchors, expected counts, and replacement boundaries only after that review;
- patch and repack the staging executable, then unpack it again to verify the installed JavaScript;
- test the actual runtime path—for example, invoke the relevant Agent variant and inspect its handoff behavior—before installation.
A patch script failing on a new release is the intended outcome until the new bundle has been inspected.
Claude Code 2.1.207 contains three built-in templates for interactive compaction:
- a full-conversation summary;
- a recent-portion summary;
- a partial summary ending at a specified point.
Adding compact rules to the persistent system prompt can influence the result, but it does not reliably replace the built-in summary structure. A targeted patch replaces the bodies of all three templates while preserving their shared protocol: no tool calls and output in the <summary> form expected by Claude Code.
The patch should change only those three prompt bodies. It should not modify tool descriptions, dynamic reminders, hooks, subagent prompts, or unrelated runtime constraints. When tweakcc exposes the compact variants as catalogued prompt fragments, they can be edited through its prompt directory; a dedicated patcher remains useful when the variants must be replaced and verified as one coordinated unit.
A safe patcher treats the verified Claude Code version as a fixed input format and refuses unfamiliar binaries:
- each of the three compact-template start anchors must occur exactly once;
- known structural markers from the original templates must match the counts established for the verified version;
- the custom template's start and end fingerprints must each occur exactly three times after replacement;
- the shared no-tools and output-format protocol must remain exactly once;
- any mismatch stops the operation without producing an installable artifact.
These checks prevent a new Claude Code release from being patched at guessed offsets after its bundled JavaScript changes. Every release must be staged and revalidated.
Updating @anthropic-ai/claude-code replaces the installed executable.
| Component | Preserved after update |
|---|---|
custom_prompt/*.md |
Yes |
persona.md |
Yes |
scripts/cc-wrapper.sh |
Yes |
| Prompt patch source files, patch scripts, and tests | Yes |
| Tool, agent, or utility prompt patches installed in the executable | No — repatch the new executable |
| Compact patch installed in the Claude Code executable | No — repatch the new executable |
If Claude Code is updated without repatching, the wrapper-based main prompt replacement continues to work, but patched tool, agent, and utility instructions—including /compact—return to the versions bundled with the new release.
- Install or extract the target Claude Code version into an isolated staging directory. Do not overwrite the active executable.
- Use
tweakccto unpack the staged executable into JavaScript. - Run all selected tool, agent, utility, and compact prompt patches against the unpacked JavaScript. Use
custom_prompt/50_compact.mdas the compact replacement template. - Use
tweakccto repack the patched JavaScript into a staged executable.
tweakcc repacking may significantly increase the executable size. Size alone is not a validity check; startup, reverse-unpack, and real compaction validation are authoritative.
All checks must pass before installation:
- every patcher's regression tests pass;
- the staged executable runs
--versionand--helpsuccessfully; - unpacking the patched executable reproduces the patched JavaScript byte for byte;
- every tool, agent, and utility patch has its expected fingerprints, and all three custom compact fingerprints are present while the old built-in section markers are absent;
- an isolated
/compacttest is run through--resume ... --fork-session; - the result follows the custom summary style requirements, such as role-play continuity, a specific narrative perspective, or preservation of project context;
- the source session used for the isolated test remains unchanged.
Installation is gated by explicit manual confirmation after the staged artifact and validation results have been reviewed.
- Keep the previous executable available as a rollback artifact.
- Install the exact executable that passed staging validation.
- Confirm the active executable reports the expected version.
- Start a fresh Claude Code process through
scripts/cc-wrapper.shand verify that both prompt-file arguments are present whenpersona.mdis enabled. - Run a normal-message smoke test. If installation checks fail, restore the previous executable before resuming normal use.
Controlled binary patching requires Claude Code updates to remain manual.
Set the environment variable for every entry point that can start Claude Code:
export DISABLE_AUTOUPDATER=1Set the updater status in both user-level and project-level Claude Code settings where applicable:
{
"autoUpdaterStatus": "disabled"
}For a service-managed process, add the same environment variable to the service definition:
Environment=DISABLE_AUTOUPDATER=1DISABLE_AUTOUPDATER=1 disables background upgrades and automatic update checks while preserving manual updates. Do not substitute DISABLE_UPDATES=1; that setting also disables manual claude update behavior.
| Change | File or workflow | Repatch executable | Start a new Claude Code process |
|---|---|---|---|
| Core behavior | custom_prompt/00_core.md |
No | Yes |
| Tool, engineering, or memory rules | Corresponding numbered prompt block | No | Yes |
| Persona or final communication layer | persona.md |
No | Yes |
| Built-in tool instructions | Corresponding tweakcc prompt fragment or fail-closed patch |
Yes | Yes |
| Built-in agent instructions or reporting | All relevant Agent and subagent prompt variants | Yes | Yes |
/compact writing rules |
custom_prompt/50_compact.md |
Yes | Yes |
| Claude Code version | Full staging → validation → installation workflow | Yes | Yes, after manual confirmation |
The operational boundary is simple: Markdown-only main-prompt changes require a new process; any client-embedded prompt or Claude Code version change requires a newly validated binary.