메뉴
HN
Hacker News 2일 전

클로드 코드 공식 문서에 없는 숨겨진 설정 총정리

IMP
9/10
핵심 요약

클로드 코드(Claude Code)의 소스 코드를 심층 분석하여 공식 문서에 기재되지 않은 수많은 숨겨진 기능과 고급 설정 방법을 공유한 기술 기사입니다. 단순한 자동 승인 시스템부터 명령어를 실시간으로 변조하는 후크(Hook) 기능, 세션 전반에 걸쳐 유지되는 컨텍스트 주입 등 개발자가 생산성을 극대화할 수 있는 강력한 미공개 기능들이 다수 포함되어 있습니다.

번역된 본문

클로드 코드의 소스 코드를 분석했습니다. 공식 문서에 나오지 않지만 설정할 수 있는 모든 것을 알려드립니다. 명령어 실행 중 이를 재작성(rewrite)하는 훅(Hook) 필드, 영구적인 에이전트 메모리, 일상적인 언어로 작성된 자동 모드 규칙, 자기 개선형 드림 루프(dream loops)까지, 모든 예시는 복사해서 바로 붙여넣을 수 있습니다. — André Figueira, 2026년 4월 1일

클로드 코드의 자동 모드 권한 시스템은 내부적으로 “YOLO Classifier(요로 분류기)”라고 불립니다. 이는 yoloClassifier.ts 파일 내의 실제 변수 이름입니다. 그리고 이를 환경에 대한 평이한 영어 설명으로 구성할 수 있습니다. 예를 들어 “이곳은 스테이징 서버이므로 파괴적 작업이 허용됩니다”라고 적어두면, 분류기가 이를 읽고 자동 승인해도 안전한지 판단합니다. 이 내용은 어떤 문서에도 나와 있지 않습니다. 공개적으로 배포된 npm 패키지로 여러분의 node_modules에 그대로 존재하는 클로드 코드 소스 코드 깊숙이 숨겨진 수십 가지 미공개 기능 중 하나일 뿐입니다.

공식 문서도 기본기는 충분히 잘 다루고 있습니다. 하지만 소스 코드를 파고들면 여러분이 만들 수 있는 것의 범위를 획기적으로 넓혀줄 필드, 응답 형식(response formats), 설정들이 무수히 많이 나옵니다. 이 글에서 소개하는 모든 것은 현재 작동하는 기능이며, 모든 예시는 그대로 복사해 프로젝트에 적용할 수 있도록 설계되었습니다.

버전에 관한 참고 사항: 이 글의 내용은 @anthropic-ai/claude-code@2.1.87 버전을 기반으로 합니다. 미공개 기능은 릴리스 사이에 변경될 수 있으므로, 오늘날 사용할 수 있는 기능의 스냅샷으로 취급하시기 바랍니다. 이름에 “EXPERIMENTAL(실험용)”이 포함된 필드는 Anthropic 엔지니어들이 직접 불안정하다고 명시한 것이며, 이 글에서도 개별적으로 따로 언급하겠습니다.

시작하기 전에 모든 파일의 위치에 대한 빠른 참고 자료입니다:

  • 설정(Settings): ~/.claude/settings.json (개인용) 또는 .claude/settings.json (프로젝트용, Git을 통해 공유)
  • 스킬(Skills): ~/.claude/skills//SKILL.md (개인용) 또는 .claude/skills//SKILL.md (프로젝트용)
  • 에이전트(Agents): ~/.claude/agents/.md (개인용) 또는 .claude/agents/.md (프로젝트용)
  • 훅 스크립트(Hook scripts): ~/.claude/hooks/ 경로를 사용하는 것이 좋습니다. 스크립트에 chmod +x 권한을 부여하는 것을 잊지 마세요.

.claude/에 있는 프로젝트 수준의 파일은 Git에 커밋하여 팀과 공유할 수 있습니다. ~/.claude/의 개인 파일은 여러분만의 것입니다.

당신의 훅이 응답할 수 있다는 사실을 아무도 알려주지 않았습니다 이것이 문서화 측면에서 가장 큰 허점입니다. 문서에는 훅이 표준 입력(stdin)을 통해 JSON을 받고, 종료 코드(exit code)가 2이면 작업이 차단된다고만 나와 있습니다. 하지만 문서에 나오지 않는 가장 중요한 사실은, 훅이 이벤트별로 정의된 특정 필드를 포함한 JSON을 표준 출력(stdout)으로 반환하여 실시간으로 클로드 코드의 동작을 수정(modify)할 수 있다는 것입니다. 소스 코드는 각 이벤트 유형이 허용하는 정확한 필드를 보여줍니다.

PreToolUse 훅은 다음을 반환할 수 있습니다:

  • updatedInput: 도구가 실행되기 전에 도구의 입력을 다시 작성합니다. 명령을 실행 도중에 수정할 수 있습니다.
  • permissionDecision: 사용자에게 묻지 않고 강제로 'allow(허용)' 또는 'deny(거부)'를 지정합니다.
  • permissionDecisionReason: 결정에 대한 이유를 설명합니다(UI에 표시됨).
  • additionalContext: 대화 컨텍스트에 텍스트를 주입합니다.

SessionStart 훅은 다음을 반환할 수 있습니다:

  • watchPaths: FileChanged 이벤트를 발생시키는 자동 파일 감시를 설정합니다.
  • initialUserMessage: 세션의 첫 번째 사용자 메시지 앞에 콘텐츠를 추가합니다.
  • additionalContext: 전체 세션 동안 유지되는 컨텍스트를 주입합니다.

PostToolUse 훅은 다음을 반환할 수 있습니다:

  • updatedMCPToolOutput: 클로드가 MCP 도구 응답에서 보는 내용을 수정합니다.
  • additionalContext: 도구가 실행된 후 컨텍스트를 주입합니다.

PermissionRequest 훅은 다음을 반환할 수 있습니다:

  • decision: updatedInput이나 updatedPermissions를 사용해 프로그래밍 방식으로 허용하거나 거부합니다.

이는 매우 강력한 기능입니다. 다음은 Claude가 실행하기 전에 모든 git push 명령어에 자동으로 --dry-run을 추가하는 PreToolUse 훅 예시입니다.

settings.json 파일 설정: { "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "~/.claude/hooks/dry-run-pushes.sh" }] }] } }

그리고 ~/.claude/hooks/dry-run-pushes.sh 스크립트는 다음과 같습니다: #!/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

클로드는 자신이 git push origin main 명령어를 실행한다고 생각하지만, 실제로는 실행되기 전에 여러분의 훅이 이를 조용히 git push origin main --dry-run으로 재작성하게 됩니다. updatedInput 필드는 그 어떤 문서에도 나와 있지 않습니다.

다음은 Se...

원문 보기
원문 보기 (영어)
I Read the Claude Code Source Code. Here's Everything You Can Configure That the Docs Don't Tell You. Hook fields that rewrite commands mid-flight, persistent agent memory, auto-mode rules in plain English, self-improving dream loops, and every example is copy-paste ready. André Figueira Apr 01, 2026 2 1 Share Claude Code’s auto-mode permission system is internally called the “YOLO Classifier.” That’s the actual variable name in yoloClassifier.ts. And you can configure it with plain English descriptions of your environment, things like “this is a staging server, destructive operations are acceptable,” that the classifier reads to decide what’s safe to auto-approve. This isn’t in any documentation. It’s one of dozens of undocumented capabilities buried in the Claude Code source code, which is sitting right there in your node_modules as a publicly distributed npm package. The official docs cover the basics well enough. But the source code reveals fields, response formats, and settings that dramatically expand what you can build. Everything here works right now, and every example is designed to be dropped into your project as-is. A note on versioning: These findings come from @anthropic-ai/claude-code@2.1.87. Undocumented features can change between releases, so treat this as a snapshot of what’s available today. Fields with “EXPERIMENTAL” in their names are explicitly flagged as unstable by Anthropic’s own engineers, and I’ll call those out individually. Before you start Quick reference for where everything lives: Settings: ~/.claude/settings.json (personal) or .claude/settings.json (project, shared via git) Skills: ~/.claude/skills/<name>/SKILL.md (personal) or .claude/skills/<name>/SKILL.md (project) Agents: ~/.claude/agents/<name>.md (personal) or .claude/agents/<name>.md (project) Hook scripts: ~/.claude/hooks/ is a good convention. Remember to chmod +x your scripts. Project-level files in .claude/ can be committed to git and shared with your team. Personal files in ~/.claude/ are yours alone. Your hooks can talk back, and nobody told you how This is the biggest gap in the documentation. The docs tell you hooks receive JSON on stdin and that exit code 2 blocks an operation. What they don’t tell you is that 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 it executes. You can modify commands mid-flight. permissionDecision - force “allow” or “deny” without prompting the user. permissionDecisionReason - explain the decision (shown in UI). additionalContext - inject text into the conversation context. SessionStart hooks can return: watchPaths - set up automatic file watching that triggers FileChanged events. initialUserMessage - prepend content to the first user message in the session. additionalContext - inject context that persists for the whole session. PostToolUse hooks can return: updatedMCPToolOutput - modify what Claude sees from an MCP tool response. additionalContext - inject context after a tool runs. PermissionRequest hooks can return: decision - programmatically allow or deny with updatedInput or updatedPermissions . This is powerful stuff. Here’s a PreToolUse hook that automatically adds --dry-run to any git push command before Claude executes it. In your settings.json : { "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "~/.claude/hooks/dry-run-pushes.sh" }] }] } } And the script at ~/.claude/hooks/dry-run-pushes.sh : #!/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 Claude thinks it’s running git push origin main , but your hook quietly rewrites it to git push origin main --dry-run before execution. The updatedInput field isn’t in any docs. Here’s a SessionStart hook that watches your config files and injects git context into every session. settings.json : { "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/session-context.sh", "statusMessage": "Loading project context..." }] }] } } ~/.claude/hooks/session-context.sh : #!/bin/bash BRANCH=$(git branch --show-current 2>/dev/null) CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') jq -n \ --arg branch "$BRANCH" \ --arg changes "$CHANGES" \ '{ "watchPaths": ["package.json", ".env", "tsconfig.json"], "additionalContext": "Current branch: \($branch). Uncommitted changes: \($changes) files." }' Now Claude Code automatically watches your package.json , .env , and tsconfig for changes, and it knows what branch you’re on and how many uncommitted files you have before you even type anything. And here’s one that auto-approves read-only bash commands without prompting. settings.json : { "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-approve-readonly.sh" }] }] } } ~/.claude/hooks/auto-approve-readonly.sh : #!/bin/bash CMD=$(jq -r '.tool_input.command' < /dev/stdin) if echo "$CMD" | grep -qE '^(ls|cat|echo|pwd|whoami|date|git status|git log|git diff)'; then echo '{"permissionDecision": "allow", "permissionDecisionReason": "Safe read-only command"}' fi You’re basically building your own permission classifier with shell scripts. The permissionDecision field isn’t in any docs. Three hook fields the docs forgot to mention The documented hook fields are type , command , matcher , timeout , if , and statusMessage . The source code parser accepts three more that fundamentally change how hooks behave. once: true fires the hook exactly once, then auto-removes it. 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..." }] }] } } Simple enough to inline. It checks if .env exists, copies the template if not, and never runs again. 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 }] }] } } That logs every bash command to an audit file without adding any latency to your session. asyncRewake: true is the clever one. It runs in the background like async, so it doesn’t block on the happy path. But if it exits with code 2, it wakes the model back up and blocks the operation. Non-blocking when everything’s fine, blocking when something’s wrong: settings.json : { "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "~/.claude/hooks/scan-secrets.sh", "asyncRewake": true, "statusMessage": "Scanning for secrets..." }] }] } } ~/.claude/hooks/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 # Block: secrets detected fi exit 0 # Clean: carry on This scans every file Claude writes for hardcoded secrets. If it finds one, it blocks and tells Claude. If not, you never even notice it ran. Skill frontmatter fields the docs don’t show The documentation covers name , description , allowed-tools , argument-hint , when_to_use , and context . The actual frontmatter parser in the source code accepts six more. model lets you override which model runs the skill. Use haiku for cheap, fast tasks and opus for complex analysis: --- 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, c