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 like async, 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-run to any git 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 triggers FileChanged events.
    • 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 with updatedInput or updatedPermissions.

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. Use haiku for cheap, fast tasks; opus for complex analysis.
  • effort: Controls reasoning depth: low, medium, high, or max.
  • 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-name works.
  • 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, or gray.
  • 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 the CLAUDE.md instruction hierarchy. Useful for a "fresh eyes" reviewer.
  • criticalSystemReminder_EXPERIMENTAL: A short message re-injected at every turn as a system reminder. The field name has EXPERIMENTAL in it; Anthropic engineers consider it unstable.

What You Should Do Now

  1. Audit your current Claude Code setup — check if you're missing these hooks and skill features that could streamline your workflow.
  2. Implement a PreToolUse hook with updatedInput to auto-dry-run destructive commands.
  3. Try memory: project on an agent to build a persistent codebase guide that learns over sessions.
  4. Watch for changes — undocumented features can disappear in future releases. Pin your Claude Code version if you rely on them.