Claude Code's Undocumented Features: What the Docs Don't Tell You
Claude Code's source code, distributed as an npm package and sitting in your node_modules, contains dozens of features the official documentation omits. Based on version @anthropic-ai/claude-code@2.1.87, this article catalogs the most impactful undocumented capabilities, with working code examples.
The YOLO Classifier
Claude Code's auto-mode permission system is internally called the "YOLO Classifier" (see yoloClassifier.ts). You can configure it with plain English descriptions of your environment—e.g., "this is a staging server, destructive operations are acceptable"—and the classifier uses that to decide what to auto-approve. This setting is not documented anywhere.
Hook System: Hidden Fields
The documented hook fields are type, command, matcher, timeout, if, and statusMessage. The source code parser accepts three more that fundamentally change hook behavior:
once: true: Fires the hook exactly once, then auto-removes. Perfect for first-session setup:
{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "[ -f .env ] || cp .env.example .env && echo 'Created .env from template'",
"once": true,
"statusMessage": "First-time setup..."
}]
}]
}
}
async: true: Runs the hook in the background without blocking Claude. Fire and forget:
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "jq '{timestamp: now, command: .tool_input.command, session: .session_id}' < /dev/stdin >> ~/.claude/audit.jsonl",
"async": true
}]
}]
}
}
asyncRewake: true: Runs in the background likeasync, but if it exits with code 2, it wakes the model back up and blocks the operation. Non-blocking on the happy path, blocking when something's wrong:
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "~/.claude/hooks/scan-secrets.sh",
"asyncRewake": true,
"statusMessage": "Scanning for secrets..."
}]
}]
}
}
With scan-secrets.sh:
#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // .tool_response.filePath' < /dev/stdin)
if grep -qE '(password|secret|api_key)\s*=' "$FILE" 2>/dev/null; then
exit 2
fi
exit 0
Hook Return Values: Real-Time Modification
Hooks can return JSON on stdout with event-specific fields that modify Claude Code's behavior in real time. The source code reveals exactly what each event type accepts:
- PreToolUse hooks can return:
updatedInput– rewrite the tool's input before execution. Example: add--dry-runto anygit push:
#!/bin/bash
INPUT=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$INPUT" | grep -q 'git push'; then
jq -n --arg cmd "$INPUT --dry-run" '{"updatedInput": {"command": $cmd}}'
fi
-
permissionDecision– force "allow" or "deny" without prompting. -
permissionDecisionReason– explanation shown in UI. -
additionalContext– inject text into conversation context. -
SessionStart hooks can return:
watchPaths– set up automatic file watching that triggersFileChangedevents.initialUserMessage– prepend content to the first user message.additionalContext– inject persistent context.
-
PostToolUse hooks can return:
updatedMCPToolOutput– modify what Claude sees from an MCP tool.additionalContext– inject context after a tool runs.
-
PermissionRequest hooks can return:
decision– programmatically allow or deny withupdatedInputorupdatedPermissions.
Skill Frontmatter: Undocumented Fields
The documentation covers name, description, allowed-tools, argument-hint, when_to_use, and context. The parser accepts six more:
model: Override which model runs the skill. Usehaikufor cheap, fast tasks;opusfor complex analysis.effort: Controls reasoning depth:low,medium,high, ormax.hooks: Define hooks scoped to when the skill is active. They register when the skill fires and deregister when it completes.agent: Delegate the skill to a custom agent.disable-model-invocation: true: Prevents auto-invocation; only/skill-nameworks.shell: Specifies which shell to use for execution.
Example skill using model and effort:
---
name: quick-lint
description: Fast lint check using the cheapest model
model: haiku
effort: low
allowed-tools: Bash, Read
argument-hint: "[file]"
---
Run the project linter on: $ARGUMENTS
Detect the linter from config (eslint, ruff, clippy) and run it. Report only errors, not warnings.
Custom Agents: memory and color
Custom agents in .claude/agents/ support frontmatter fields the documentation doesn't mention:
color: Sets UI color:red,orange,yellow,green,blue,purple,pink, orgray.memory: Gives the agent persistent memory across invocations:user– global, persists across all projects.project– per-project persistence.local– private per-project (gitignored).
Example agent with memory:
---
name: codebase-guide
description: Answer questions about the codebase, learning more with each session
tools: [Read, Grep, Glob, Bash]
color: green
memory: project
---
You are a codebase guide with persistent memory. Check your memory first before exploring the code.
After answering a question, save useful context to memory:
- Architecture decisions (type: project)
- Code locations for common tasks (type: reference)
- Patterns and conventions (type: feedback)
Over time, you should answer faster because you remember where things are.
omitClaudeMd: true: Skips loading theCLAUDE.mdinstruction hierarchy. Useful for a "fresh eyes" reviewer.criticalSystemReminder_EXPERIMENTAL: A short message re-injected at every turn as a system reminder. The field name hasEXPERIMENTALin it; Anthropic engineers consider it unstable.
What You Should Do Now
- Audit your current Claude Code setup — check if you're missing these hooks and skill features that could streamline your workflow.
- Implement a
PreToolUsehook withupdatedInputto auto-dry-run destructive commands. - Try
memory: projecton an agent to build a persistent codebase guide that learns over sessions. - Watch for changes — undocumented features can disappear in future releases. Pin your Claude Code version if you rely on them.






