Customizing Hook Messages

The Problem¶
ctx hooks speak ctx's language, not your project's. The QA gate says
"lint the ENTIRE project" and "make build," but your Python project uses
pytest and ruff. The post-commit nudge suggests running lints, but
your project uses npm test. You could remove the hook entirely, but
then you lose the logic (counting, state tracking, adaptive frequency)
just to change the words.
How do you customize what hooks say without removing what they do?
TL;DR¶
ctx system message list # see all hooks and their messages
ctx system message show qa-reminder gate # view the current template
ctx system message edit qa-reminder gate # copy default to .context/ for editing
ctx system message reset qa-reminder gate # revert to embedded default
Commands Used¶
| Tool | Type | Purpose |
|---|---|---|
ctx system message list |
CLI command | Show all hook messages with category and override status |
ctx system message show |
CLI command | Print the effective message template |
ctx system message edit |
CLI command | Copy embedded default to .context/ for editing |
ctx system message reset |
CLI command | Delete user override, revert to default |
How It Works¶
Hook messages use a 3-tier fallback:
- User override:
.context/hooks/messages/{hook}/{variant}.txt - Embedded default: compiled into the
ctxbinary - Hardcoded fallback: belt-and-suspenders safety net
The hook logic (when to fire, counting, state tracking, cooldowns) is unchanged. Only the content (what text gets emitted) comes from the template. You customize what the hook says without touching how it decides to speak.
Finding the Original Templates¶
The default templates live in the ctx source tree at:
You can also browse them on GitHub:
internal/assets/hooks/messages/
Or use ctx system message show to print any template without digging
through source code:
ctx system message show qa-reminder gate # QA gate instructions
ctx system message show check-persistence nudge # persistence nudge
ctx system message show post-commit nudge # post-commit reminder
The show output includes the template source and available variables --
everything you need to write a replacement.
Template Variables¶
Some messages use Go text/template variables for dynamic content:
No context files updated in {{.PromptsSinceNudge}}+ prompts.
Have you discovered learnings, made decisions,
established conventions, or completed tasks
worth persisting?
The show and edit commands list available variables for each message.
When writing a replacement, keep the same {{.VariableName}} placeholders
to preserve dynamic content. Variables that you omit render as <no value>:
no error, but the output may look odd.
Intentional Silence¶
An empty template file (0 bytes or whitespace-only) means "don't
emit a message". The hook still runs its logic but produces no output.
This lets you silence specific messages without removing the hook from
hooks.json.
Example: Python Project QA Gate¶
The default QA gate says "lint the ENTIRE project" and references
make lint. For a Python project, you want pytest and ruff:
# See the current default
ctx system message show qa-reminder gate
# Copy it to .context/ for editing
ctx system message edit qa-reminder gate
# Edit the override
Replace the content in .context/hooks/messages/qa-reminder/gate.txt:
HARD GATE! DO NOT COMMIT without completing ALL of these steps first:
(1) Run the full test suite: pytest -x
(2) Run the linter: ruff check .
(3) Verify a clean working tree
Run tests and linter BEFORE every git commit, no exceptions.
The hook still fires on every Edit call. The logic is identical. Only
the instructions changed.
Example: Silencing Ceremony Nudges¶
The ceremony check nudges you to use /ctx-remember and /ctx-wrap-up.
If your team has a different workflow and finds these noisy:
ctx system message edit check-ceremonies both
ctx system message edit check-ceremonies remember
ctx system message edit check-ceremonies wrapup
Then empty each file:
echo -n "" > .context/hooks/messages/check-ceremonies/both.txt
echo -n "" > .context/hooks/messages/check-ceremonies/remember.txt
echo -n "" > .context/hooks/messages/check-ceremonies/wrapup.txt
The hooks still track ceremony usage internally, but they no longer emit any visible output.
Example: JavaScript Project Post-Commit¶
The default post-commit nudge mentions generic "lints and tests." For a JavaScript project:
Replace with:
Commit succeeded. 1. Offer context capture to the user: Decision (design
choice?), Learning (gotcha?), or Neither. 2. Ask the user: "Want me to
run npm test and eslint before you push?" Do NOT push. The user pushes
manually.
The Two Categories¶
Not all messages are equal. The list command shows each message's
category:
Customizable (16 messages)¶
Messages that are opinions: project-specific wording that benefits from customization. These are the primary targets for override.
| Hook | Variant | Description |
|---|---|---|
| check-backup-age | warning | Backup staleness warning |
| check-ceremonies | both | Both ceremonies missing |
| check-ceremonies | remember | Start-of-session ceremony |
| check-ceremonies | wrapup | End-of-session ceremony |
| check-context-size | checkpoint | Context capacity warning |
| check-context-size | oversize | Injection oversize nudge |
| check-context-size | window | Context window usage warning (>80%) |
| check-journal | both | Unexported sessions + unenriched entries |
| check-journal | unenriched | Unenriched journal entries |
| check-journal | unexported | Unexported sessions |
| check-knowledge | warning | Knowledge file growth |
| check-map-staleness | stale | Architecture map staleness |
| check-persistence | nudge | Context persistence nudge |
| post-commit | nudge | Post-commit context capture |
| qa-reminder | gate | Pre-commit QA gate |
ctx-specific (10 messages)¶
Messages specific to ctx's own development workflow. You can customize
them, but edit will warn you first.
| Hook | Variant | Description |
|---|---|---|
| block-dangerous-commands | cp-to-bin | Block copy to bin dirs |
| block-dangerous-commands | install-to-local-bin | Block copy to ~/.local/bin |
| block-dangerous-commands | mid-git-push | Block git push |
| block-dangerous-commands | mid-sudo | Block sudo |
| block-non-path-ctx | absolute-path | Block absolute path invocation |
| block-non-path-ctx | dot-slash | Block ./ctx invocation |
| block-non-path-ctx | go-run | Block go run invocation |
| check-reminders | reminders | Pending reminders relay |
| check-resources | alert | Resource pressure alert |
| check-version | key-rotation | Key rotation nudge |
| check-version | mismatch | Version mismatch |
Template Variables Reference¶
| Hook | Variant | Variables |
|---|---|---|
| check-backup-age | warning | {{.Warnings}} |
| check-context-size | checkpoint | (none) |
| check-context-size | oversize | {{.TokenCount}} |
| check-context-size | window | {{.TokenCount}}, {{.Percentage}} |
| check-ceremonies | both, remember, wrapup | (none) |
| check-journal | both | {{.UnexportedCount}}, {{.UnenrichedCount}} |
| check-journal | unenriched | {{.UnenrichedCount}} |
| check-journal | unexported | {{.UnexportedCount}} |
| check-knowledge | warning | {{.FileWarnings}} |
| check-map-staleness | stale | {{.LastRefreshDate}}, {{.ModuleCount}} |
| check-persistence | nudge | {{.PromptsSinceNudge}} |
| check-reminders | reminders | {{.ReminderList}} |
| check-resources | alert | {{.AlertMessages}} |
| check-version | key-rotation | {{.KeyAgeDays}} |
| check-version | mismatch | {{.BinaryVersion}}, {{.PluginVersion}} |
| post-commit | nudge | (none) |
| qa-reminder | gate | (none) |
| block-dangerous-commands | all variants | (none) |
| block-non-path-ctx | all variants | (none) |
Templates that reference undefined variables render <no value>:
no error, graceful degradation.
Tips¶
- Override files are version-controlled: they live in
.context/alongside your other context files. Team members get the same customized messages. - Start with
show: always check the current default before editing. The embedded template is the baseline your override replaces. - Use
resetto undo: if a customization causes confusion, reset reverts to the embedded default instantly. - Empty file = silence: you don't need to delete the hook. An empty override file silences the message while preserving the hook's logic.
- JSON output for scripting:
ctx system message list --jsonreturns structured data for automation.
See Also¶
- Hook Output Patterns: understanding VERBATIM relays, agent directives, and hard gates
- Auditing System Hooks: verifying hooks are running and auditing their output
- Configuration: project-level settings
via
.ctxrc