The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .devcontainer
    ├── Dockerfile
    ├── devcontainer.json
    └── init-firewall.sh
├── .gitattributes
├── .github
    ├── ISSUE_TEMPLATE
    │   └── bug_report.md
    └── workflows
    │   ├── claude-issue-triage.yml
    │   └── claude.yml
├── .vscode
    └── extensions.json
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── Script
    └── run_devcontainer_claude_code.ps1
├── demo.gif
└── examples
    └── hooks
        └── bash_command_validator_example.py


/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM node:20
 2 | 
 3 | ARG TZ
 4 | ENV TZ="$TZ"
 5 | 
 6 | # Install basic development tools and iptables/ipset
 7 | RUN apt update && apt install -y less \
 8 |   git \
 9 |   procps \
10 |   sudo \
11 |   fzf \
12 |   zsh \
13 |   man-db \
14 |   unzip \
15 |   gnupg2 \
16 |   gh \
17 |   iptables \
18 |   ipset \
19 |   iproute2 \
20 |   dnsutils \
21 |   aggregate \
22 |   jq
23 | 
24 | # Ensure default node user has access to /usr/local/share
25 | RUN mkdir -p /usr/local/share/npm-global && \
26 |   chown -R node:node /usr/local/share
27 | 
28 | ARG USERNAME=node
29 | 
30 | # Persist bash history.
31 | RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
32 |   && mkdir /commandhistory \
33 |   && touch /commandhistory/.bash_history \
34 |   && chown -R $USERNAME /commandhistory
35 | 
36 | # Set `DEVCONTAINER` environment variable to help with orientation
37 | ENV DEVCONTAINER=true
38 | 
39 | # Create workspace and config directories and set permissions
40 | RUN mkdir -p /workspace /home/node/.claude && \
41 |   chown -R node:node /workspace /home/node/.claude
42 | 
43 | WORKDIR /workspace
44 | 
45 | RUN ARCH=$(dpkg --print-architecture) && \
46 |   wget "https://github.com/dandavison/delta/releases/download/0.18.2/git-delta_0.18.2_${ARCH}.deb" && \
47 |   sudo dpkg -i "git-delta_0.18.2_${ARCH}.deb" && \
48 |   rm "git-delta_0.18.2_${ARCH}.deb"
49 | 
50 | # Set up non-root user
51 | USER node
52 | 
53 | # Install global packages
54 | ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
55 | ENV PATH=$PATH:/usr/local/share/npm-global/bin
56 | 
57 | # Set the default shell to zsh rather than sh
58 | ENV SHELL=/bin/zsh
59 | 
60 | # Default powerline10k theme
61 | RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.2.0/zsh-in-docker.sh)" -- \
62 |   -p git \
63 |   -p fzf \
64 |   -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
65 |   -a "source /usr/share/doc/fzf/examples/completion.zsh" \
66 |   -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
67 |   -x
68 | 
69 | # Install Claude
70 | RUN npm install -g @anthropic-ai/claude-code
71 | 
72 | # Copy and set up firewall script
73 | COPY init-firewall.sh /usr/local/bin/
74 | USER root
75 | RUN chmod +x /usr/local/bin/init-firewall.sh && \
76 |   echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
77 |   chmod 0440 /etc/sudoers.d/node-firewall
78 | USER node
79 | 


--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "Claude Code Sandbox",
 3 |   "build": {
 4 |     "dockerfile": "Dockerfile",
 5 |     "args": {
 6 |       "TZ": "${localEnv:TZ:America/Los_Angeles}"
 7 |     }
 8 |   },
 9 |   "runArgs": [
10 |     "--cap-add=NET_ADMIN",
11 |     "--cap-add=NET_RAW"
12 |   ],
13 |   "customizations": {
14 |     "vscode": {
15 |       "extensions": [
16 |         "dbaeumer.vscode-eslint",
17 |         "esbenp.prettier-vscode",
18 |         "eamodio.gitlens"
19 |       ],
20 |       "settings": {
21 |         "editor.formatOnSave": true,
22 |         "editor.defaultFormatter": "esbenp.prettier-vscode",
23 |         "editor.codeActionsOnSave": {
24 |           "source.fixAll.eslint": "explicit"
25 |         },
26 |         "terminal.integrated.defaultProfile.linux": "zsh",
27 |         "terminal.integrated.profiles.linux": {
28 |           "bash": {
29 |             "path": "bash",
30 |             "icon": "terminal-bash"
31 |           },
32 |           "zsh": {
33 |             "path": "zsh"
34 |           }
35 |         }
36 |       }
37 |     }
38 |   },
39 |   "remoteUser": "node",
40 |   "mounts": [
41 |     "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
42 |     "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
43 |   ],
44 |   "remoteEnv": {
45 |     "NODE_OPTIONS": "--max-old-space-size=4096",
46 |     "CLAUDE_CONFIG_DIR": "/home/node/.claude",
47 |     "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
48 |   },
49 |   "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
50 |   "workspaceFolder": "/workspace",
51 |   "postCreateCommand": "sudo /usr/local/bin/init-firewall.sh"
52 | }
53 | 


--------------------------------------------------------------------------------
/.devcontainer/init-firewall.sh:
--------------------------------------------------------------------------------
  1 | #!/bin/bash
  2 | set -euo pipefail  # Exit on error, undefined vars, and pipeline failures
  3 | IFS=
#39;\n\t'       # Stricter word splitting
  4 | 
  5 | # Flush existing rules and delete existing ipsets
  6 | iptables -F
  7 | iptables -X
  8 | iptables -t nat -F
  9 | iptables -t nat -X
 10 | iptables -t mangle -F
 11 | iptables -t mangle -X
 12 | ipset destroy allowed-domains 2>/dev/null || true
 13 | 
 14 | # First allow DNS and localhost before any restrictions
 15 | # Allow outbound DNS
 16 | iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
 17 | # Allow inbound DNS responses
 18 | iptables -A INPUT -p udp --sport 53 -j ACCEPT
 19 | # Allow outbound SSH
 20 | iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
 21 | # Allow inbound SSH responses
 22 | iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
 23 | # Allow localhost
 24 | iptables -A INPUT -i lo -j ACCEPT
 25 | iptables -A OUTPUT -o lo -j ACCEPT
 26 | 
 27 | # Create ipset with CIDR support
 28 | ipset create allowed-domains hash:net
 29 | 
 30 | # Fetch GitHub meta information and aggregate + add their IP ranges
 31 | echo "Fetching GitHub IP ranges..."
 32 | gh_ranges=$(curl -s https://api.github.com/meta)
 33 | if [ -z "$gh_ranges" ]; then
 34 |     echo "ERROR: Failed to fetch GitHub IP ranges"
 35 |     exit 1
 36 | fi
 37 | 
 38 | if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
 39 |     echo "ERROR: GitHub API response missing required fields"
 40 |     exit 1
 41 | fi
 42 | 
 43 | echo "Processing GitHub IPs..."
 44 | while read -r cidr; do
 45 |     if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
 46 |         echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
 47 |         exit 1
 48 |     fi
 49 |     echo "Adding GitHub range $cidr"
 50 |     ipset add allowed-domains "$cidr"
 51 | done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
 52 | 
 53 | # Resolve and add other allowed domains
 54 | for domain in \
 55 |     "registry.npmjs.org" \
 56 |     "api.anthropic.com" \
 57 |     "sentry.io" \
 58 |     "statsig.anthropic.com" \
 59 |     "statsig.com"; do
 60 |     echo "Resolving $domain..."
 61 |     ips=$(dig +short A "$domain")
 62 |     if [ -z "$ips" ]; then
 63 |         echo "ERROR: Failed to resolve $domain"
 64 |         exit 1
 65 |     fi
 66 |     
 67 |     while read -r ip; do
 68 |         if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
 69 |             echo "ERROR: Invalid IP from DNS for $domain: $ip"
 70 |             exit 1
 71 |         fi
 72 |         echo "Adding $ip for $domain"
 73 |         ipset add allowed-domains "$ip"
 74 |     done < <(echo "$ips")
 75 | done
 76 | 
 77 | # Get host IP from default route
 78 | HOST_IP=$(ip route | grep default | cut -d" " -f3)
 79 | if [ -z "$HOST_IP" ]; then
 80 |     echo "ERROR: Failed to detect host IP"
 81 |     exit 1
 82 | fi
 83 | 
 84 | HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
 85 | echo "Host network detected as: $HOST_NETWORK"
 86 | 
 87 | # Set up remaining iptables rules
 88 | iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
 89 | iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
 90 | 
 91 | # Set default policies to DROP first
 92 | iptables -P INPUT DROP
 93 | iptables -P FORWARD DROP
 94 | iptables -P OUTPUT DROP
 95 | 
 96 | # First allow established connections for already approved traffic
 97 | iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
 98 | iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
 99 | 
100 | # Then allow only specific outbound traffic to allowed domains
101 | iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
102 | 
103 | echo "Firewall configuration complete"
104 | echo "Verifying firewall rules..."
105 | if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
106 |     echo "ERROR: Firewall verification failed - was able to reach https://example.com"
107 |     exit 1
108 | else
109 |     echo "Firewall verification passed - unable to reach https://example.com as expected"
110 | fi
111 | 
112 | # Verify GitHub API access
113 | if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
114 |     echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
115 |     exit 1
116 | else
117 |     echo "Firewall verification passed - able to reach https://api.github.com as expected"
118 | fi
119 | 


--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.sh text eol=lf


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: Create a report to help us improve
 4 | title: '[BUG] '
 5 | labels: bug
 6 | assignees: ''
 7 | ---
 8 | 
 9 | ## Environment
10 | - Platform (select one):
11 |   - [ ] Anthropic API
12 |   - [ ] AWS Bedrock
13 |   - [ ] Google Vertex AI
14 |   - [ ] Other: <!-- specify -->
15 | - Claude CLI version: <!-- output of `claude --version` -->
16 | - Operating System: <!-- e.g. macOS 14.3, Windows 11, Ubuntu 22.04 -->
17 | - Terminal:  <!-- e.g. iTerm2, Terminal App -->
18 | 
19 | ## Bug Description
20 | <!-- A clear and concise description of the bug -->
21 | 
22 | ## Steps to Reproduce
23 | 1. <!-- First step -->
24 | 2. <!-- Second step -->
25 | 3. <!-- And so on... -->
26 | 
27 | ## Expected Behavior
28 | <!-- What you expected to happen -->
29 | 
30 | ## Actual Behavior
31 | <!-- What actually happened -->
32 | 
33 | ## Additional Context
34 | <!-- Add any other context about the problem here, such as screenshots, logs, etc. -->


--------------------------------------------------------------------------------
/.github/workflows/claude-issue-triage.yml:
--------------------------------------------------------------------------------
  1 | name: Claude Issue Triage
  2 | description: Automatically triage GitHub issues using Claude Code
  3 | on:
  4 |   issues:
  5 |     types: [opened]
  6 | 
  7 | jobs:
  8 |   triage-issue:
  9 |     runs-on: ubuntu-latest
 10 |     timeout-minutes: 10
 11 |     permissions:
 12 |       contents: read
 13 |       issues: write
 14 | 
 15 |     steps:
 16 |       - name: Checkout repository
 17 |         uses: actions/checkout@v4
 18 | 
 19 |       - name: Create triage prompt
 20 |         run: |
 21 |           mkdir -p /tmp/claude-prompts
 22 |           cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF'
 23 |           You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
 24 | 
 25 |           IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
 26 | 
 27 |           Issue Information:
 28 |           - REPO: ${{ github.repository }}
 29 |           - ISSUE_NUMBER: ${{ github.event.issue.number }}
 30 | 
 31 |           TASK OVERVIEW:
 32 | 
 33 |           1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
 34 | 
 35 |           2. Next, use the GitHub tools to get context about the issue:
 36 |              - You have access to these tools:
 37 |                - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
 38 |                - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
 39 |                - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
 40 |                - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
 41 |                - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
 42 |              - Start by using mcp__github__get_issue to get the issue details
 43 | 
 44 |           3. Analyze the issue content, considering:
 45 |              - The issue title and description
 46 |              - The type of issue (bug report, feature request, question, etc.)
 47 |              - Technical areas mentioned
 48 |              - Severity or priority indicators
 49 |              - User impact
 50 |              - Components affected
 51 | 
 52 |           4. Select appropriate labels from the available labels list provided above:
 53 |              - Choose labels that accurately reflect the issue's nature
 54 |              - Be specific but comprehensive
 55 |              - Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
 56 |              - Consider platform labels (android, ios) if applicable
 57 |              - If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
 58 | 
 59 |           5. Apply the selected labels:
 60 |              - Use mcp__github__update_issue to apply your selected labels
 61 |              - DO NOT post any comments explaining your decision
 62 |              - DO NOT communicate directly with users
 63 |              - If no labels are clearly applicable, do not apply any labels
 64 | 
 65 |           IMPORTANT GUIDELINES:
 66 |           - Be thorough in your analysis
 67 |           - Only select labels from the provided list above
 68 |           - DO NOT post any comments to the issue
 69 |           - Your ONLY action should be to apply labels using mcp__github__update_issue
 70 |           - It's okay to not add any labels if none are clearly applicable
 71 |           EOF
 72 | 
 73 |       - name: Setup GitHub MCP Server
 74 |         run: |
 75 |           mkdir -p /tmp/mcp-config
 76 |           cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
 77 |           {
 78 |             "mcpServers": {
 79 |               "github": {
 80 |                 "command": "docker",
 81 |                 "args": [
 82 |                   "run",
 83 |                   "-i",
 84 |                   "--rm",
 85 |                   "-e",
 86 |                   "GITHUB_PERSONAL_ACCESS_TOKEN",
 87 |                   "ghcr.io/github/github-mcp-server:sha-7aced2b"
 88 |                 ],
 89 |                 "env": {
 90 |                   "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
 91 |                 }
 92 |               }
 93 |             }
 94 |           }
 95 |           EOF
 96 | 
 97 |       - name: Run Claude Code for Issue Triage
 98 |         uses: anthropics/claude-code-base-action@beta
 99 |         with:
100 |           prompt_file: /tmp/claude-prompts/triage-prompt.txt
101 |           allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
102 |           timeout_minutes: "5"
103 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
104 |           mcp_config: /tmp/mcp-config/mcp-servers.json
105 |           claude_env: |
106 |             GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107 | 


--------------------------------------------------------------------------------
/.github/workflows/claude.yml:
--------------------------------------------------------------------------------
 1 | name: Claude Code
 2 | 
 3 | on:
 4 |   issue_comment:
 5 |     types: [created]
 6 |   pull_request_review_comment:
 7 |     types: [created]
 8 |   issues:
 9 |     types: [opened, assigned]
10 |   pull_request_review:
11 |     types: [submitted]
12 | 
13 | jobs:
14 |   claude:
15 |     if: |
16 |       (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17 |       (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18 |       (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19 |       (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20 |     runs-on: ubuntu-latest
21 |     permissions:
22 |       contents: read
23 |       pull-requests: read
24 |       issues: read
25 |       id-token: write
26 |     steps:
27 |       - name: Checkout repository
28 |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4
29 |         with:
30 |           fetch-depth: 1
31 | 
32 |       - name: Run Claude Code
33 |         id: claude
34 |         uses: anthropics/claude-code-action@beta
35 |         with:
36 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
37 | 
38 | 


--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 |   "recommendations": [
3 |     "dbaeumer.vscode-eslint",
4 |     "esbenp.prettier-vscode",
5 |     "ms-vscode-remote.remote-containers",
6 |     "eamodio.gitlens"
7 |   ]
8 | }
9 | 


--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
  1 | # Changelog
  2 | 
  3 | ## 1.0.51
  4 | 
  5 | - Added support for native Windows (requires Git for Windows)
  6 | - Added support for Bedrock API keys through environment variable AWS_BEARER_TOKEN_BEDROCK
  7 | - Settings: /doctor can now help you identify and fix invalid setting files
  8 | - `--append-system-prompt` can now be used in interactive mode, not just --print/-p.
  9 | - Increased auto-compact warning threshold from 60% to 80%
 10 | - Fixed an issue with handling user directories with spaces for shell snapshots
 11 | - OTEL resource now includes os.type, os.version, host.arch, and wsl.version (if running on Windows Subsystem for Linux)
 12 | - Custom slash commands: Fixed user-level commands in subdirectories
 13 | - Plan mode: Fixed issue where rejected plan from sub-task would get discarded
 14 | 
 15 | ## 1.0.48
 16 | 
 17 | - Fixed a bug in v1.0.45 where the app would sometimes freeze on launch
 18 | - Added progress messages to Bash tool based on the last 5 lines of command output
 19 | - Added expanding variables support for MCP server configuration
 20 | - Moved shell snapshots from /tmp to ~/.claude for more reliable Bash tool calls
 21 | - Improved IDE extension path handling when Claude Code runs in WSL
 22 | - Hooks: Added a PreCompact hook
 23 | - Vim mode: Added c, f/F, t/T
 24 | 
 25 | ## 1.0.45
 26 | 
 27 | - Redesigned Search (Grep) tool with new tool input parameters and features
 28 | - Disabled IDE diffs for notebook files, fixing "Timeout waiting after 1000ms" error
 29 | - Fixed config file corruption issue by enforcing atomic writes
 30 | - Updated prompt input undo to Ctrl+\_ to avoid breaking existing Ctrl+U behavior, matching zsh's undo shortcut
 31 | - Stop Hooks: Fixed transcript path after /clear and fixed triggering when loop ends with tool call
 32 | - Custom slash commands: Restored namespacing in command names based on subdirectories. For example, .claude/commands/frontend/component.md is now /frontend:component, not /component.
 33 | 
 34 | ## 1.0.44
 35 | 
 36 | - New /export command lets you quickly export a conversation for sharing
 37 | - MCP: resource_link tool results are now supported
 38 | - MCP: tool annotations and tool titles now display in /mcp view
 39 | - Changed Ctrl+Z to suspend Claude Code. Resume by running `fg`. Prompt input undo is now Ctrl+U.
 40 | 
 41 | ## 1.0.43
 42 | 
 43 | - Fixed a bug where the theme selector was saving excessively
 44 | - Hooks: Added EPIPE system error handling
 45 | 
 46 | ## 1.0.42
 47 | 
 48 | - Added tilde (`~`) expansion support to `/add-dir` command
 49 | 
 50 | ## 1.0.41
 51 | 
 52 | - Hooks: Split Stop hook triggering into Stop and SubagentStop
 53 | - Hooks: Enabled optional timeout configuration for each command
 54 | - Hooks: Added "hook_event_name" to hook input
 55 | - Fixed a bug where MCP tools would display twice in tool list
 56 | - New tool parameters JSON for Bash tool in `tool_decision` event
 57 | 
 58 | ## 1.0.40
 59 | 
 60 | - Fixed a bug causing API connection errors with UNABLE_TO_GET_ISSUER_CERT_LOCALLY if `NODE_EXTRA_CA_CERTS` was set
 61 | 
 62 | ## 1.0.39
 63 | 
 64 | - New Active Time metric in OpenTelemetry logging
 65 | 
 66 | ## 1.0.38
 67 | 
 68 | - Released hooks. Special thanks to community input in https://github.com/anthropics/claude-code/issues/712. Docs: https://docs.anthropic.com/en/docs/claude-code/hooks
 69 | 
 70 | ## 1.0.37
 71 | 
 72 | - Remove ability to set `Proxy-Authorization` header via ANTHROPIC_AUTH_TOKEN or apiKeyHelper
 73 | 
 74 | ## 1.0.36
 75 | 
 76 | - Web search now takes today's date into context
 77 | - Fixed a bug where stdio MCP servers were not terminating properly on exit
 78 | 
 79 | ## 1.0.35
 80 | 
 81 | - Added support for MCP OAuth Authorization Server discovery
 82 | 
 83 | ## 1.0.34
 84 | 
 85 | - Fixed a memory leak causing a MaxListenersExceededWarning message to appear
 86 | 
 87 | ## 1.0.33
 88 | 
 89 | - Improved logging functionality with session ID support
 90 | - Added prompt input undo functionality (Ctrl+Z and vim 'u' command)
 91 | - Improvements to plan mode
 92 | 
 93 | ## 1.0.32
 94 | 
 95 | - Updated loopback config for litellm
 96 | - Added forceLoginMethod setting to bypass login selection screen
 97 | 
 98 | ## 1.0.31
 99 | 
100 | - Fixed a bug where ~/.claude.json would get reset when file contained invalid JSON
101 | 
102 | ## 1.0.30
103 | 
104 | - Custom slash commands: Run bash output, @-mention files, enable thinking with thinking keywords
105 | - Improved file path autocomplete with filename matching
106 | - Added timestamps in Ctrl-r mode and fixed Ctrl-c handling
107 | - Enhanced jq regex support for complex filters with pipes and select
108 | 
109 | ## 1.0.29
110 | 
111 | - Improved CJK character support in cursor navigation and rendering
112 | 
113 | ## 1.0.28
114 | 
115 | - Slash commands: Fix selector display during history navigation
116 | - Resizes images before upload to prevent API size limit errors
117 | - Added XDG_CONFIG_HOME support to configuration directory
118 | - Performance optimizations for memory usage
119 | - New attributes (terminal.type, language) in OpenTelemetry logging
120 | 
121 | ## 1.0.27
122 | 
123 | - Streamable HTTP MCP servers are now supported
124 | - Remote MCP servers (SSE and HTTP) now support OAuth
125 | - MCP resources can now be @-mentioned
126 | - /resume slash command to switch conversations within Claude Code
127 | 
128 | ## 1.0.25
129 | 
130 | - Slash commands: moved "project" and "user" prefixes to descriptions
131 | - Slash commands: improved reliability for command discovery
132 | - Improved support for Ghostty
133 | - Improved web search reliability
134 | 
135 | ## 1.0.24
136 | 
137 | - Improved /mcp output
138 | - Fixed a bug where settings arrays got overwritten instead of merged
139 | 
140 | ## 1.0.23
141 | 
142 | - Released TypeScript SDK: import @anthropic-ai/claude-code to get started
143 | - Released Python SDK: pip install claude-code-sdk to get started
144 | 
145 | ## 1.0.22
146 | 
147 | - SDK: Renamed `total_cost` to `total_cost_usd`
148 | 
149 | ## 1.0.21
150 | 
151 | - Improved editing of files with tab-based indentation
152 | - Fix for tool_use without matching tool_result errors
153 | - Fixed a bug where stdio MCP server processes would linger after quitting Claude Code
154 | 
155 | ## 1.0.18
156 | 
157 | - Added --add-dir CLI argument for specifying additional working directories
158 | - Added streaming input support without require -p flag
159 | - Improved startup performance and session storage performance
160 | - Added CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR environment variable to freeze working directory for bash commands
161 | - Added detailed MCP server tools display (/mcp)
162 | - MCP authentication and permission improvements
163 | - Added auto-reconnection for MCP SSE connections on disconnect
164 | - Fixed issue where pasted content was lost when dialogs appeared
165 | 
166 | ## 1.0.17
167 | 
168 | - We now emit messages from sub-tasks in -p mode (look for the parent_tool_use_id property)
169 | - Fixed crashes when the VS Code diff tool is invoked multiple times quickly
170 | - MCP server list UI improvements
171 | - Update Claude Code process title to display "claude" instead of "node"
172 | 
173 | ## 1.0.11
174 | 
175 | - Claude Code can now also be used with a Claude Pro subscription
176 | - Added /upgrade for smoother switching to Claude Max plans
177 | - Improved UI for authentication from API keys and Bedrock/Vertex/external auth tokens
178 | - Improved shell configuration error handling
179 | - Improved todo list handling during compaction
180 | 
181 | ## 1.0.10
182 | 
183 | - Added markdown table support
184 | - Improved streaming performance
185 | 
186 | ## 1.0.8
187 | 
188 | - Fixed Vertex AI region fallback when using CLOUD_ML_REGION
189 | - Increased default otel interval from 1s -> 5s
190 | - Fixed edge cases where MCP_TIMEOUT and MCP_TOOL_TIMEOUT weren't being respected
191 | - Fixed a regression where search tools unnecessarily asked for permissions
192 | - Added support for triggering thinking non-English languages
193 | - Improved compacting UI
194 | 
195 | ## 1.0.7
196 | 
197 | - Renamed /allowed-tools -> /permissions
198 | - Migrated allowedTools and ignorePatterns from .claude.json -> settings.json
199 | - Deprecated claude config commands in favor of editing settings.json
200 | - Fixed a bug where --dangerously-skip-permissions sometimes didn't work in --print mode
201 | - Improved error handling for /install-github-app
202 | - Bugfixes, UI polish, and tool reliability improvements
203 | 
204 | ## 1.0.6
205 | 
206 | - Improved edit reliability for tab-indented files
207 | - Respect CLAUDE_CONFIG_DIR everywhere
208 | - Reduced unnecessary tool permission prompts
209 | - Added support for symlinks in @file typeahead
210 | - Bugfixes, UI polish, and tool reliability improvements
211 | 
212 | ## 1.0.4
213 | 
214 | - Fixed a bug where MCP tool errors weren't being parsed correctly
215 | 
216 | ## 1.0.1
217 | 
218 | - Added `DISABLE_INTERLEAVED_THINKING` to give users the option to opt out of interleaved thinking.
219 | - Improved model references to show provider-specific names (Sonnet 3.7 for Bedrock, Sonnet 4 for Console)
220 | - Updated documentation links and OAuth process descriptions
221 | 
222 | ## 1.0.0
223 | 
224 | - Claude Code is now generally available
225 | - Introducing Sonnet 4 and Opus 4 models
226 | 
227 | ## 0.2.125
228 | 
229 | - Breaking change: Bedrock ARN passed to `ANTHROPIC_MODEL` or `ANTHROPIC_SMALL_FAST_MODEL` should no longer contain an escaped slash (specify `/` instead of `%2F`)
230 | - Removed `DEBUG=true` in favor of `ANTHROPIC_LOG=debug`, to log all requests
231 | 
232 | ## 0.2.117
233 | 
234 | - Breaking change: --print JSON output now returns nested message objects, for forwards-compatibility as we introduce new metadata fields
235 | - Introduced settings.cleanupPeriodDays
236 | - Introduced CLAUDE_CODE_API_KEY_HELPER_TTL_MS env var
237 | - Introduced --debug mode
238 | 
239 | ## 0.2.108
240 | 
241 | - You can now send messages to Claude while it works to steer Claude in real-time
242 | - Introduced BASH_DEFAULT_TIMEOUT_MS and BASH_MAX_TIMEOUT_MS env vars
243 | - Fixed a bug where thinking was not working in -p mode
244 | - Fixed a regression in /cost reporting
245 | - Deprecated MCP wizard interface in favor of other MCP commands
246 | - Lots of other bugfixes and improvements
247 | 
248 | ## 0.2.107
249 | 
250 | - CLAUDE.md files can now import other files. Add @path/to/file.md to ./CLAUDE.md to load additional files on launch
251 | 
252 | ## 0.2.106
253 | 
254 | - MCP SSE server configs can now specify custom headers
255 | - Fixed a bug where MCP permission prompt didn't always show correctly
256 | 
257 | ## 0.2.105
258 | 
259 | - Claude can now search the web
260 | - Moved system & account status to /status
261 | - Added word movement keybindings for Vim
262 | - Improved latency for startup, todo tool, and file edits
263 | 
264 | ## 0.2.102
265 | 
266 | - Improved thinking triggering reliability
267 | - Improved @mention reliability for images and folders
268 | - You can now paste multiple large chunks into one prompt
269 | 
270 | ## 0.2.100
271 | 
272 | - Fixed a crash caused by a stack overflow error
273 | - Made db storage optional; missing db support disables --continue and --resume
274 | 
275 | ## 0.2.98
276 | 
277 | - Fixed an issue where auto-compact was running twice
278 | 
279 | ## 0.2.96
280 | 
281 | - Claude Code can now also be used with a Claude Max subscription (https://claude.ai/upgrade)
282 | 
283 | ## 0.2.93
284 | 
285 | - Resume conversations from where you left off from with "claude --continue" and "claude --resume"
286 | - Claude now has access to a Todo list that helps it stay on track and be more organized
287 | 
288 | ## 0.2.82
289 | 
290 | - Added support for --disallowedTools
291 | - Renamed tools for consistency: LSTool -> LS, View -> Read, etc.
292 | 
293 | ## 0.2.75
294 | 
295 | - Hit Enter to queue up additional messages while Claude is working
296 | - Drag in or copy/paste image files directly into the prompt
297 | - @-mention files to directly add them to context
298 | - Run one-off MCP servers with `claude --mcp-config <path-to-file>`
299 | - Improved performance for filename auto-complete
300 | 
301 | ## 0.2.74
302 | 
303 | - Added support for refreshing dynamically generated API keys (via apiKeyHelper), with a 5 minute TTL
304 | - Task tool can now perform writes and run bash commands
305 | 
306 | ## 0.2.72
307 | 
308 | - Updated spinner to indicate tokens loaded and tool usage
309 | 
310 | ## 0.2.70
311 | 
312 | - Network commands like curl are now available for Claude to use
313 | - Claude can now run multiple web queries in parallel
314 | - Pressing ESC once immediately interrupts Claude in Auto-accept mode
315 | 
316 | ## 0.2.69
317 | 
318 | - Fixed UI glitches with improved Select component behavior
319 | - Enhanced terminal output display with better text truncation logic
320 | 
321 | ## 0.2.67
322 | 
323 | - Shared project permission rules can be saved in .claude/settings.json
324 | 
325 | ## 0.2.66
326 | 
327 | - Print mode (-p) now supports streaming output via --output-format=stream-json
328 | - Fixed issue where pasting could trigger memory or bash mode unexpectedly
329 | 
330 | ## 0.2.63
331 | 
332 | - Fixed an issue where MCP tools were loaded twice, which caused tool call errors
333 | 
334 | ## 0.2.61
335 | 
336 | - Navigate menus with vim-style keys (j/k) or bash/emacs shortcuts (Ctrl+n/p) for faster interaction
337 | - Enhanced image detection for more reliable clipboard paste functionality
338 | - Fixed an issue where ESC key could crash the conversation history selector
339 | 
340 | ## 0.2.59
341 | 
342 | - Copy+paste images directly into your prompt
343 | - Improved progress indicators for bash and fetch tools
344 | - Bugfixes for non-interactive mode (-p)
345 | 
346 | ## 0.2.54
347 | 
348 | - Quickly add to Memory by starting your message with '#'
349 | - Press ctrl+r to see full output for long tool results
350 | - Added support for MCP SSE transport
351 | 
352 | ## 0.2.53
353 | 
354 | - New web fetch tool lets Claude view URLs that you paste in
355 | - Fixed a bug with JPEG detection
356 | 
357 | ## 0.2.50
358 | 
359 | - New MCP "project" scope now allows you to add MCP servers to .mcp.json files and commit them to your repository
360 | 
361 | ## 0.2.49
362 | 
363 | - Previous MCP server scopes have been renamed: previous "project" scope is now "local" and "global" scope is now "user"
364 | 
365 | ## 0.2.47
366 | 
367 | - Press Tab to auto-complete file and folder names
368 | - Press Shift + Tab to toggle auto-accept for file edits
369 | - Automatic conversation compaction for infinite conversation length (toggle with /config)
370 | 
371 | ## 0.2.44
372 | 
373 | - Ask Claude to make a plan with thinking mode: just say 'think' or 'think harder' or even 'ultrathink'
374 | 
375 | ## 0.2.41
376 | 
377 | - MCP server startup timeout can now be configured via MCP_TIMEOUT environment variable
378 | - MCP server startup no longer blocks the app from starting up
379 | 
380 | ## 0.2.37
381 | 
382 | - New /release-notes command lets you view release notes at any time
383 | - `claude config add/remove` commands now accept multiple values separated by commas or spaces
384 | 
385 | ## 0.2.36
386 | 
387 | - Import MCP servers from Claude Desktop with `claude mcp add-from-claude-desktop`
388 | - Add MCP servers as JSON strings with `claude mcp add-json <n> <json>`
389 | 
390 | ## 0.2.34
391 | 
392 | - Vim bindings for text input - enable with /vim or /config
393 | 
394 | ## 0.2.32
395 | 
396 | - Interactive MCP setup wizard: Run "claude mcp add" to add MCP servers with a step-by-step interface
397 | - Fix for some PersistentShell issues
398 | 
399 | ## 0.2.31
400 | 
401 | - Custom slash commands: Markdown files in .claude/commands/ directories now appear as custom slash commands to insert prompts into your conversation
402 | - MCP debug mode: Run with --mcp-debug flag to get more information about MCP server errors
403 | 
404 | ## 0.2.30
405 | 
406 | - Added ANSI color theme for better terminal compatibility
407 | - Fixed issue where slash command arguments weren't being sent properly
408 | - (Mac-only) API keys are now stored in macOS Keychain
409 | 
410 | ## 0.2.26
411 | 
412 | - New /approved-tools command for managing tool permissions
413 | - Word-level diff display for improved code readability
414 | - Fuzzy matching for slash commands
415 | 
416 | ## 0.2.21
417 | 
418 | - Fuzzy matching for /commands
419 | 


--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | © Anthropic PBC. All rights reserved. Use is subject to Anthropic's [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms).
2 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Claude Code
 2 | 
 3 | ![](https://img.shields.io/badge/Node.js-18%2B-brightgreen?style=flat-square) [![npm]](https://www.npmjs.com/package/@anthropic-ai/claude-code)
 4 | 
 5 | [npm]: https://img.shields.io/npm/v/@anthropic-ai/claude-code.svg?style=flat-square
 6 | 
 7 | Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows -- all through natural language commands. Use it in your terminal, IDE, or tag @claude on Github.
 8 | 
 9 | **Learn more in the [official documentation](https://docs.anthropic.com/en/docs/claude-code/overview)**.
10 | 
11 | <img src="./demo.gif" />
12 | 
13 | ## Get started
14 | 
15 | 1. Install Claude Code:
16 | 
17 | ```sh
18 | npm install -g @anthropic-ai/claude-code
19 | ```
20 | 
21 | 2. Navigate to your project directory and run `claude`.
22 | 
23 | ## Reporting Bugs
24 | 
25 | We welcome your feedback. Use the `/bug` command to report issues directly within Claude Code, or file a [GitHub issue](https://github.com/anthropics/claude-code/issues).
26 | 
27 | ## Data collection, usage, and retention
28 | 
29 | When you use Claude Code, we collect feedback, which includes usage data (such as code acceptance or rejections), associated conversation data, and user feedback submitted via the `/bug` command.
30 | 
31 | ### How we use your data
32 | 
33 | We may use feedback to improve our products and services, but we will not train generative models using your feedback from Claude Code. Given their potentially sensitive nature, we store user feedback transcripts for only 30 days.
34 | 
35 | If you choose to send us feedback about Claude Code, such as transcripts of your usage, Anthropic may use that feedback to debug related issues and improve Claude Code's functionality (e.g., to reduce the risk of similar bugs occurring in the future).
36 | 
37 | ### Privacy safeguards
38 | 
39 | We have implemented several safeguards to protect your data, including limited retention periods for sensitive information, restricted access to user session data, and clear policies against using feedback for model training.
40 | 
41 | For full details, please review our [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms) and [Privacy Policy](https://www.anthropic.com/legal/privacy).
42 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | # Security Policy
 2 | Thank you for helping us keep Claude Code secure!
 3 | 
 4 | ## Reporting Security Issues
 5 | 
 6 | The security of our systems and user data is Anthropic's top priority. We appreciate the work of security researchers acting in good faith in identifying and reporting potential vulnerabilities.
 7 | 
 8 | Our security program is managed on HackerOne and we ask that any validated vulnerability in this functionality be reported through their [submission form](https://hackerone.com/anthropic-vdp/reports/new?type=team&report_type=vulnerability).
 9 | 
10 | ## Vulnerability Disclosure Program
11 | 
12 | Our Vulnerability Program Guidelines are defined on our [HackerOne program page](https://hackerone.com/anthropic-vdp).
13 | 


--------------------------------------------------------------------------------
/Script/run_devcontainer_claude_code.ps1:
--------------------------------------------------------------------------------
  1 | <#
  2 | .SYNOPSIS
  3 |     Automates the setup and connection to a DevContainer environment using either Docker or Podman on Windows.
  4 | 
  5 | .DESCRIPTION
  6 |     This script automates the process of initializing, starting, and connecting to a DevContainer
  7 |     using either Docker or Podman as the container backend. It must be executed from the root
  8 |     directory of your project and assumes the script is located in a 'Script' subdirectory.
  9 | 
 10 | .PARAMETER Backend
 11 |     Specifies the container backend to use. Valid values are 'docker' or 'podman'.
 12 | 
 13 | .EXAMPLE
 14 |     .\Script\run_devcontainer_claude_code.ps1 -Backend docker
 15 |     Uses Docker as the container backend.
 16 | 
 17 | .EXAMPLE
 18 |     .\Script\run_devcontainer_claude_code.ps1 -Backend podman
 19 |     Uses Podman as the container backend.
 20 | 
 21 | .NOTES
 22 |     Project Structure:
 23 |     Project/
 24 |     ├── .devcontainer/
 25 |     └── Script/
 26 |         └── run_devcontainer_claude_code.ps1
 27 | #>
 28 | 
 29 | [CmdletBinding()]
 30 | param(
 31 |     [Parameter(Mandatory=$true)]
 32 |     [ValidateSet('docker','podman')]
 33 |     [string]$Backend
 34 | )
 35 | 
 36 | # Notify script start
 37 | Write-Host "--- DevContainer Startup & Connection Script ---"
 38 | Write-Host "Using backend: $($Backend)"
 39 | 
 40 | # --- Backend-Specific Initialization ---
 41 | if ($Backend -eq 'podman') {
 42 |     Write-Host "--- Podman Backend Initialization ---"
 43 | 
 44 |     # --- Step 1a: Initialize Podman machine ---
 45 |     Write-Host "Initializing Podman machine 'claudeVM'..."
 46 |     try {
 47 |         & podman machine init claudeVM
 48 |         Write-Host "Podman machine 'claudeVM' initialized or already exists."
 49 |     } catch {
 50 |         Write-Error "Failed to initialize Podman machine: $($_.Exception.Message)"
 51 |         exit 1 # Exit script on error
 52 |     }
 53 | 
 54 |     # --- Step 1b: Start Podman machine ---
 55 |     Write-Host "Starting Podman machine 'claudeVM'..."
 56 |     try {
 57 |         & podman machine start claudeVM -q
 58 |         Write-Host "Podman machine started or already running."
 59 |     } catch {
 60 |         Write-Error "Failed to start Podman machine: $($_.Exception.Message)"
 61 |         exit 1
 62 |     }
 63 | 
 64 |     # --- Step 2: Set default connection ---
 65 |     Write-Host "Setting default Podman connection to 'claudeVM'..."
 66 |     try {
 67 |         & podman system connection default claudeVM
 68 |         Write-Host "Default connection set."
 69 |     } catch {
 70 |         Write-Warning "Failed to set default Podman connection (may be already set or machine issue): $($_.Exception.Message)"
 71 |     }
 72 | 
 73 | } elseif ($Backend -eq 'docker') {
 74 |     Write-Host "--- Docker Backend Initialization ---"
 75 | 
 76 |     # --- Step 1 & 2: Check Docker Desktop ---
 77 |     Write-Host "Checking if Docker Desktop is running and docker command is available..."
 78 |     try {
 79 |         docker info | Out-Null
 80 |         Write-Host "Docker Desktop (daemon) is running."
 81 |     } catch {
 82 |         Write-Error "Docker Desktop is not running or docker command not found."
 83 |         Write-Error "Please ensure Docker Desktop is running."
 84 |         exit 1
 85 |     }
 86 | }
 87 | 
 88 | # --- Step 3: Bring up DevContainer ---
 89 | Write-Host "Bringing up DevContainer in the current folder..."
 90 | try {
 91 |     $arguments = @('up', '--workspace-folder', '.')
 92 |     if ($Backend -eq 'podman') {
 93 |         $arguments += '--docker-path', 'podman'
 94 |     }
 95 |     & devcontainer @arguments
 96 |     Write-Host "DevContainer startup process completed."
 97 | } catch {
 98 |     Write-Error "Failed to bring up DevContainer: $($_.Exception.Message)"
 99 |     exit 1
100 | }
101 | 
102 | # --- Step 4: Get DevContainer ID ---
103 | Write-Host "Finding the DevContainer ID..."
104 | $currentFolder = (Get-Location).Path
105 | 
106 | try {
107 |     $containerId = (& $Backend ps --filter "label=devcontainer.local_folder=$currentFolder" --format '{{.ID}}').Trim()
108 | } catch {
109 |     $displayCommand = "$Backend ps --filter `"label=devcontainer.local_folder=$currentFolder`" --format '{{.ID}}'"
110 |     Write-Error "Failed to get container ID (Command: $displayCommand): $($_.Exception.Message)"
111 |     exit 1
112 | }
113 | 
114 | if (-not $containerId) {
115 |     Write-Error "Could not find DevContainer ID for the current folder ('$currentFolder')."
116 |     Write-Error "Please check if 'devcontainer up' was successful and the container is running."
117 |     exit 1
118 | }
119 | Write-Host "Found container ID: $containerId"
120 | 
121 | # --- Step 5 & 6: Execute command and enter interactive shell inside container ---
122 | Write-Host "Executing 'claude' command and then starting zsh session inside container $($containerId)..."
123 | try {
124 |     & $Backend exec -it $containerId zsh -c 'claude; exec zsh'
125 |     Write-Host "Interactive session ended."
126 | } catch {
127 |     $displayCommand = "$Backend exec -it $containerId zsh -c 'claude; exec zsh'"
128 |     Write-Error "Failed to execute command inside container (Command: $displayCommand): $($_.Exception.Message)"
129 |     exit 1
130 | }
131 | 
132 | # Notify script completion
133 | Write-Host "--- Script completed ---"


--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anthropics/claude-code/d45bce242d8ac7ff13a2bab2e619558ab5f47dec/demo.gif


--------------------------------------------------------------------------------
/examples/hooks/bash_command_validator_example.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python3
 2 | """
 3 | Claude Code Hook: Bash Command Validator
 4 | =========================================
 5 | This hook runs as a PreToolUse hook for the Bash tool.
 6 | It validates bash commands against a set of rules before execution.
 7 | In this case it changes grep calls to using rg.
 8 | 
 9 | Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks
10 | 
11 | Make sure to change your path to your actual script.
12 | 
13 | {
14 |   "hooks": {
15 |     "PreToolUse": [
16 |       {
17 |         "matcher": "Bash",
18 |         "hooks": [
19 |           {
20 |             "type": "command",
21 |             "command": "python3 /path/to/claude-code/examples/hooks/bash_command_validator_example.py"
22 |           }
23 |         ]
24 |       }
25 |     ]
26 |   }
27 | }
28 | 
29 | """
30 | 
31 | import json
32 | import re
33 | import sys
34 | 
35 | # Define validation rules as a list of (regex pattern, message) tuples
36 | _VALIDATION_RULES = [
37 |     (
38 |         r"^grep\b(?!.*\|)",
39 |         "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
40 |     ),
41 |     (
42 |         r"^find\s+\S+\s+-name\b",
43 |         "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
44 |     ),
45 | ]
46 | 
47 | 
48 | def _validate_command(command: str) -> list[str]:
49 |     issues = []
50 |     for pattern, message in _VALIDATION_RULES:
51 |         if re.search(pattern, command):
52 |             issues.append(message)
53 |     return issues
54 | 
55 | 
56 | def main():
57 |     try:
58 |         input_data = json.load(sys.stdin)
59 |     except json.JSONDecodeError as e:
60 |         print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
61 |         # Exit code 1 shows stderr to the user but not to Claude
62 |         sys.exit(1)
63 | 
64 |     tool_name = input_data.get("tool_name", "")
65 |     if tool_name != "Bash":
66 |         sys.exit(0)
67 | 
68 |     tool_input = input_data.get("tool_input", {})
69 |     command = tool_input.get("command", "")
70 | 
71 |     if not command:
72 |         sys.exit(0)
73 | 
74 |     issues = _validate_command(command)
75 |     if issues:
76 |         for message in issues:
77 |             print(f"• {message}", file=sys.stderr)
78 |         # Exit code 2 blocks tool call and shows stderr to Claude
79 |         sys.exit(2)
80 | 
81 | 
82 | if __name__ == "__main__":
83 |     main()
84 | 


--------------------------------------------------------------------------------