diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a9b6d0558..b4f8261da 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,8 @@ ENV TZ="$TZ" ARG CLAUDE_CODE_VERSION=latest # Install basic development tools and iptables/ipset -RUN apt update && apt install -y less \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + less \ git \ procps \ sudo \ @@ -21,7 +22,10 @@ RUN apt update && apt install -y less \ iproute2 \ dnsutils \ aggregate \ - jq + jq \ + nano \ + vim \ + && apt-get clean && rm -rf /var/lib/apt/lists/* # Ensure default node user has access to /usr/local/share RUN mkdir -p /usr/local/share/npm-global && \ @@ -44,10 +48,11 @@ RUN mkdir -p /workspace /home/node/.claude && \ WORKDIR /workspace +ARG GIT_DELTA_VERSION=0.18.2 RUN ARCH=$(dpkg --print-architecture) && \ - wget "https://github.com/dandavison/delta/releases/download/0.18.2/git-delta_0.18.2_${ARCH}.deb" && \ - sudo dpkg -i "git-delta_0.18.2_${ARCH}.deb" && \ - rm "git-delta_0.18.2_${ARCH}.deb" + wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \ + sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \ + rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" # Set up non-root user USER node @@ -59,8 +64,13 @@ ENV PATH=$PATH:/usr/local/share/npm-global/bin # Set the default shell to zsh rather than sh ENV SHELL=/bin/zsh +# Set the default editor and visual +ENV EDITOR nano +ENV VISUAL nano + # Default powerline10k theme -RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.2.0/zsh-in-docker.sh)" -- \ +ARG ZSH_IN_DOCKER_VERSION=1.2.0 +RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \ -p git \ -p fzf \ -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c9ffd31ea..850e23baf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,9 @@ "dockerfile": "Dockerfile", "args": { "TZ": "${localEnv:TZ:America/Los_Angeles}", - "CLAUDE_CODE_VERSION": "latest" + "CLAUDE_CODE_VERSION": "latest", + "GIT_DELTA_VERSION": "0.18.2", + "ZSH_IN_DOCKER_VERSION": "1.2.0" } }, "runArgs": [ @@ -39,8 +41,8 @@ }, "remoteUser": "node", "mounts": [ - "source=claude-code-bashhistory,target=/commandhistory,type=volume", - "source=claude-code-config,target=/home/node/.claude,type=volume" + "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume", + "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume" ], "remoteEnv": { "NODE_OPTIONS": "--max-old-space-size=4096", diff --git a/.github/actions/claude-code-action/action.yml b/.github/actions/claude-code-action/action.yml deleted file mode 100644 index 8e57c63cb..000000000 --- a/.github/actions/claude-code-action/action.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: "Claude Code Action" -description: "Run Claude Code in GitHub Actions workflows" - -inputs: - github_token: - description: "GitHub token with repo and issues permissions" - required: true - anthropic_api_key: - description: "Anthropic API key" - required: true - prompt: - description: "The prompt to send to Claude Code" - required: false - default: "" - prompt_file: - description: "Path to a file containing the prompt to send to Claude Code" - required: false - default: "" - allowed_tools: - description: "Comma-separated list of allowed tools for Claude Code to use" - required: false - default: "" - output_file: - description: "File to save Claude Code output to (optional)" - required: false - default: "" - timeout_minutes: - description: "Timeout in minutes for Claude Code execution" - required: false - default: "10" - install_github_mcp: - description: "Whether to install the GitHub MCP server" - required: false - default: "false" - -runs: - using: "composite" - steps: - - name: Install Claude Code - shell: bash - run: npm install -g @anthropic-ai/claude-code - - - name: Install GitHub MCP Server - if: inputs.install_github_mcp == 'true' - shell: bash - run: | - claude mcp add-json github '{ - "command": "docker", - "args": [ - "run", - "-i", - "--rm", - "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN", - "ghcr.io/github/github-mcp-server:sha-ff3036d" - ], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.GITHUB_TOKEN }}" - } - }' - - - name: Prepare Prompt File - shell: bash - id: prepare_prompt - run: | - # Check if either prompt or prompt_file is provided - if [ -z "${{ inputs.prompt }}" ] && [ -z "${{ inputs.prompt_file }}" ]; then - echo "::error::Neither 'prompt' nor 'prompt_file' was provided. At least one is required." - exit 1 - fi - - # Determine which prompt source to use - if [ ! -z "${{ inputs.prompt_file }}" ]; then - # Check if the prompt file exists - if [ ! -f "${{ inputs.prompt_file }}" ]; then - echo "::error::Prompt file '${{ inputs.prompt_file }}' does not exist." - exit 1 - fi - - # Use the provided prompt file - PROMPT_PATH="${{ inputs.prompt_file }}" - else - mkdir -p /tmp/claude-action - PROMPT_PATH="/tmp/claude-action/prompt.txt" - echo "${{ inputs.prompt }}" > "$PROMPT_PATH" - fi - - # Verify the prompt file is not empty - if [ ! -s "$PROMPT_PATH" ]; then - echo "::error::Prompt is empty. Please provide a non-empty prompt." - exit 1 - fi - - # Save the prompt path for the next step - echo "PROMPT_PATH=$PROMPT_PATH" >> $GITHUB_ENV - - - name: Run Claude Code - shell: bash - id: run_claude - run: | - ALLOWED_TOOLS_ARG="" - if [ ! -z "${{ inputs.allowed_tools }}" ]; then - ALLOWED_TOOLS_ARG="--allowedTools ${{ inputs.allowed_tools }}" - fi - - # Set a timeout to ensure the command doesn't run indefinitely - timeout_seconds=$((${{ inputs.timeout_minutes }} * 60)) - - if [ -z "${{ inputs.output_file }}" ]; then - # Run Claude Code and output to console - timeout $timeout_seconds claude \ - -p \ - --verbose \ - --output-format stream-json \ - "$(cat ${{ env.PROMPT_PATH }})" \ - ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} - else - # Run Claude Code and tee output to console and file - timeout $timeout_seconds claude \ - -p \ - --verbose \ - --output-format stream-json \ - "$(cat ${{ env.PROMPT_PATH }})" \ - ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} | tee output.txt - - # Process output.txt into JSON in a separate step - jq -s '.' output.txt > output.json - - # Extract the result from the last item in the array (system message) - jq -r '.[-1].result' output.json > "${{ inputs.output_file }}" - - echo "Complete output saved to output.json, final response saved to ${{ inputs.output_file }}" - fi - env: - ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} - GITHUB_TOKEN: ${{ inputs.github_token }} diff --git a/.github/actions/claude-issue-triage-action/action.yml b/.github/actions/claude-issue-triage-action/action.yml deleted file mode 100644 index 666efa27f..000000000 --- a/.github/actions/claude-issue-triage-action/action.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: "Claude Issue Triage Action" -description: "Automatically triage GitHub issues using Claude Code" - -inputs: - timeout_minutes: - description: "Timeout in minutes for execution" - required: false - default: "5" - anthropic_api_key: - description: "Anthropic API key" - required: true - github_token: - description: "GitHub token with repo and issues permissions" - required: true - -runs: - using: "composite" - steps: - - name: Checkout repository code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Create prompt file - shell: bash - run: | - mkdir -p /tmp/claude-prompts - cat > /tmp/claude-prompts/claude-issue-triage-prompt.txt << 'EOF' - You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list. - - IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels. - - Issue Information: - - REPO: ${{ github.repository }} - - ISSUE_NUMBER: ${{ github.event.issue.number }} - - TASK OVERVIEW: - - 1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. - - 2. Next, use the GitHub tools to get context about the issue: - - You have access to these tools: - - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels - - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments - - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting) - - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues - - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled - - Start by using mcp__github__get_issue to get the issue details - - 3. Analyze the issue content, considering: - - The issue title and description - - The type of issue (bug report, feature request, question, etc.) - - Technical areas mentioned - - Severity or priority indicators - - User impact - - Components affected - - 4. Select appropriate labels from the available labels list provided above: - - Choose labels that accurately reflect the issue's nature - - Be specific but comprehensive - - Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority) - - Consider platform labels (android, ios) if applicable - - 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. - - 5. Apply the selected labels: - - Use mcp__github__update_issue to apply your selected labels - - DO NOT post any comments explaining your decision - - DO NOT communicate directly with users - - If no labels are clearly applicable, do not apply any labels - - IMPORTANT GUIDELINES: - - Be thorough in your analysis - - Only select labels from the provided list above - - DO NOT post any comments to the issue - - Your ONLY action should be to apply labels using mcp__github__update_issue - - It's okay to not add any labels if none are clearly applicable - EOF - - - name: Run Claude Code - uses: ./.github/actions/claude-code-action - with: - prompt_file: /tmp/claude-prompts/claude-issue-triage-prompt.txt - 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" - install_github_mcp: "true" - timeout_minutes: ${{ inputs.timeout_minutes }} - anthropic_api_key: ${{ inputs.anthropic_api_key }} - github_token: ${{ inputs.github_token }} diff --git a/.github/workflows/claude-issue-triage.yml b/.github/workflows/claude-issue-triage.yml index 36fd79ba9..d534053e5 100644 --- a/.github/workflows/claude-issue-triage.yml +++ b/.github/workflows/claude-issue-triage.yml @@ -1,6 +1,5 @@ name: Claude Issue Triage -description: "Automatically triage GitHub issues using Claude Code" - +description: Automatically triage GitHub issues using Claude Code on: issues: types: [opened] @@ -12,12 +11,96 @@ jobs: permissions: contents: read issues: write + steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@v4 - - name: Run Claude Issue Triage - uses: ./.github/actions/claude-issue-triage-action + - name: Create triage prompt + run: | + mkdir -p /tmp/claude-prompts + cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF' + You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list. + + IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels. + + Issue Information: + - REPO: ${{ github.repository }} + - ISSUE_NUMBER: ${{ github.event.issue.number }} + + TASK OVERVIEW: + + 1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. + + 2. Next, use the GitHub tools to get context about the issue: + - You have access to these tools: + - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels + - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments + - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting) + - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues + - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled + - Start by using mcp__github__get_issue to get the issue details + + 3. Analyze the issue content, considering: + - The issue title and description + - The type of issue (bug report, feature request, question, etc.) + - Technical areas mentioned + - Severity or priority indicators + - User impact + - Components affected + + 4. Select appropriate labels from the available labels list provided above: + - Choose labels that accurately reflect the issue's nature + - Be specific but comprehensive + - Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority) + - Consider platform labels (android, ios) if applicable + - 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. + + 5. Apply the selected labels: + - Use mcp__github__update_issue to apply your selected labels + - DO NOT post any comments explaining your decision + - DO NOT communicate directly with users + - If no labels are clearly applicable, do not apply any labels + + IMPORTANT GUIDELINES: + - Be thorough in your analysis + - Only select labels from the provided list above + - DO NOT post any comments to the issue + - Your ONLY action should be to apply labels using mcp__github__update_issue + - It's okay to not add any labels if none are clearly applicable + EOF + + - name: Setup GitHub MCP Server + run: | + mkdir -p /tmp/mcp-config + cat > /tmp/mcp-config/mcp-servers.json << 'EOF' + { + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server:sha-7aced2b" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}" + } + } + } + } + EOF + + - name: Run Claude Code for Issue Triage + uses: anthropics/claude-code-base-action@beta with: + prompt_file: /tmp/claude-prompts/triage-prompt.txt + 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" + timeout_minutes: "5" anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - github_token: ${{ secrets.GITHUB_TOKEN }} + mcp_config: /tmp/mcp-config/mcp-servers.json + claude_env: | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb6efeee..da15b50b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,201 @@ # Changelog +## 1.0.62 + +- Added @-mention support with typeahead for custom agents. @ to invoke it +- Hooks: Added SessionStart hook for new session initialization +- /add-dir command now supports typeahead for directory paths +- Improved network connectivity check reliability + +## 1.0.61 + +- Transcript mode (Ctrl+R): Changed Esc to exit transcript mode rather than interrupt +- Settings: Added `--settings` flag to load settings from a JSON file +- Settings: Fixed resolution of settings files paths that are symlinks +- OTEL: Fixed reporting of wrong organization after authentication changes +- Slash commands: Fixed permissions checking for allowed-tools with Bash +- IDE: Added support for pasting images in VSCode MacOS using ⌘+V +- IDE: Added `CLAUDE_CODE_AUTO_CONNECT_IDE=false` for disabling IDE auto-connection +- Added `CLAUDE_CODE_SHELL_PREFIX` for wrapping Claude and user-provided shell commands run by Claude Code + +## 1.0.60 + +- You can now create custom subagents for specialized tasks! Run /agents to get started + +## 1.0.59 + +- SDK: Added tool confirmation support with canUseTool callback +- SDK: Allow specifying env for spawned process +- Hooks: Exposed PermissionDecision to hooks (including "ask") +- Hooks: UserPromptSubmit now supports additionalContext in advanced JSON output +- Fixed issue where some Max users that specified Opus would still see fallback to Sonnet + +## 1.0.58 + +- Added support for reading PDFs +- MCP: Improved server health status display in 'claude mcp list' +- Hooks: Added CLAUDE_PROJECT_DIR env var for hook commands + +## 1.0.57 + +- Added support for specifying a model in slash commands +- Improved permission messages to help Claude understand allowed tools +- Fix: Remove trailing newlines from bash output in terminal wrapping + +## 1.0.56 + +- Windows: Enabled shift+tab for mode switching on versions of Node.js that support terminal VT mode +- Fixes for WSL IDE detection +- Fix an issue causing awsRefreshHelper changes to .aws directory not to be picked up + +## 1.0.55 + +- Clarified knowledge cutoff for Opus 4 and Sonnet 4 models +- Windows: fixed Ctrl+Z crash +- SDK: Added ability to capture error logging +- Add --system-prompt-file option to override system prompt in print mode + +## 1.0.54 + +- Hooks: Added UserPromptSubmit hook and the current working directory to hook inputs +- Custom slash commands: Added argument-hint to frontmatter +- Windows: OAuth uses port 45454 and properly constructs browser URL +- Windows: mode switching now uses alt + m, and plan mode renders properly +- Shell: Switch to in-memory shell snapshot to fix file-related errors + +## 1.0.53 + +- Updated @-mention file truncation from 100 lines to 2000 lines +- Add helper script settings for AWS token refresh: awsAuthRefresh (for foreground operations like aws sso login) and awsCredentialExport (for background operation with STS-like response). + +## 1.0.52 + +- Added support for MCP server instructions + +## 1.0.51 + +- Added support for native Windows (requires Git for Windows) +- Added support for Bedrock API keys through environment variable AWS_BEARER_TOKEN_BEDROCK +- Settings: /doctor can now help you identify and fix invalid setting files +- `--append-system-prompt` can now be used in interactive mode, not just --print/-p. +- Increased auto-compact warning threshold from 60% to 80% +- Fixed an issue with handling user directories with spaces for shell snapshots +- OTEL resource now includes os.type, os.version, host.arch, and wsl.version (if running on Windows Subsystem for Linux) +- Custom slash commands: Fixed user-level commands in subdirectories +- Plan mode: Fixed issue where rejected plan from sub-task would get discarded + +## 1.0.48 + +- Fixed a bug in v1.0.45 where the app would sometimes freeze on launch +- Added progress messages to Bash tool based on the last 5 lines of command output +- Added expanding variables support for MCP server configuration +- Moved shell snapshots from /tmp to ~/.claude for more reliable Bash tool calls +- Improved IDE extension path handling when Claude Code runs in WSL +- Hooks: Added a PreCompact hook +- Vim mode: Added c, f/F, t/T + +## 1.0.45 + +- Redesigned Search (Grep) tool with new tool input parameters and features +- Disabled IDE diffs for notebook files, fixing "Timeout waiting after 1000ms" error +- Fixed config file corruption issue by enforcing atomic writes +- Updated prompt input undo to Ctrl+\_ to avoid breaking existing Ctrl+U behavior, matching zsh's undo shortcut +- Stop Hooks: Fixed transcript path after /clear and fixed triggering when loop ends with tool call +- Custom slash commands: Restored namespacing in command names based on subdirectories. For example, .claude/commands/frontend/component.md is now /frontend:component, not /component. + +## 1.0.44 + +- New /export command lets you quickly export a conversation for sharing +- MCP: resource_link tool results are now supported +- MCP: tool annotations and tool titles now display in /mcp view +- Changed Ctrl+Z to suspend Claude Code. Resume by running `fg`. Prompt input undo is now Ctrl+U. + +## 1.0.43 + +- Fixed a bug where the theme selector was saving excessively +- Hooks: Added EPIPE system error handling + +## 1.0.42 + +- Added tilde (`~`) expansion support to `/add-dir` command + +## 1.0.41 + +- Hooks: Split Stop hook triggering into Stop and SubagentStop +- Hooks: Enabled optional timeout configuration for each command +- Hooks: Added "hook_event_name" to hook input +- Fixed a bug where MCP tools would display twice in tool list +- New tool parameters JSON for Bash tool in `tool_decision` event + +## 1.0.40 + +- Fixed a bug causing API connection errors with UNABLE_TO_GET_ISSUER_CERT_LOCALLY if `NODE_EXTRA_CA_CERTS` was set + +## 1.0.39 + +- New Active Time metric in OpenTelemetry logging + +## 1.0.38 + +- 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 + +## 1.0.37 + +- Remove ability to set `Proxy-Authorization` header via ANTHROPIC_AUTH_TOKEN or apiKeyHelper + +## 1.0.36 + +- Web search now takes today's date into context +- Fixed a bug where stdio MCP servers were not terminating properly on exit + +## 1.0.35 + +- Added support for MCP OAuth Authorization Server discovery + +## 1.0.34 + +- Fixed a memory leak causing a MaxListenersExceededWarning message to appear + +## 1.0.33 + +- Improved logging functionality with session ID support +- Added prompt input undo functionality (Ctrl+Z and vim 'u' command) +- Improvements to plan mode + +## 1.0.32 + +- Updated loopback config for litellm +- Added forceLoginMethod setting to bypass login selection screen + +## 1.0.31 + +- Fixed a bug where ~/.claude.json would get reset when file contained invalid JSON + +## 1.0.30 + +- Custom slash commands: Run bash output, @-mention files, enable thinking with thinking keywords +- Improved file path autocomplete with filename matching +- Added timestamps in Ctrl-r mode and fixed Ctrl-c handling +- Enhanced jq regex support for complex filters with pipes and select + +## 1.0.29 + +- Improved CJK character support in cursor navigation and rendering + +## 1.0.28 + +- Slash commands: Fix selector display during history navigation +- Resizes images before upload to prevent API size limit errors +- Added XDG_CONFIG_HOME support to configuration directory +- Performance optimizations for memory usage +- New attributes (terminal.type, language) in OpenTelemetry logging + ## 1.0.27 - Streamable HTTP MCP servers are now supported - Remote MCP servers (SSE and HTTP) now support OAuth - MCP resources can now be @-mentioned +- /resume slash command to switch conversations within Claude Code ## 1.0.25 diff --git a/Script/run_devcontainer_claude_code.ps1 b/Script/run_devcontainer_claude_code.ps1 new file mode 100644 index 000000000..5b99d7d8c --- /dev/null +++ b/Script/run_devcontainer_claude_code.ps1 @@ -0,0 +1,148 @@ +<# +.SYNOPSIS + Automates the setup and connection to a DevContainer environment using either Docker or Podman on Windows. + +.DESCRIPTION + This script automates the process of initializing, starting, and connecting to a DevContainer + using either Docker or Podman as the container backend. It must be executed from the root + directory of your project and assumes the script is located in a 'Script' subdirectory. + +.PARAMETER Backend + Specifies the container backend to use. Valid values are 'docker' or 'podman'. + +.EXAMPLE + .\Script\run_devcontainer_claude_code.ps1 -Backend docker + Uses Docker as the container backend. + +.EXAMPLE + .\Script\run_devcontainer_claude_code.ps1 -Backend podman + Uses Podman as the container backend. + +.NOTES + Project Structure: + Project/ + ├── .devcontainer/ + └── Script/ + └── run_devcontainer_claude_code.ps1 +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)] + [ValidateSet('docker','podman')] + [string]$Backend +) + +# Notify script start +Write-Host "--- DevContainer Startup & Connection Script ---" +Write-Host "Using backend: $($Backend)" + +# --- Prerequisite Check --- +Write-Host "Checking for required commands..." +try { + Get-Command $Backend -ErrorAction Stop | Out-Null + Write-Host "- $($Backend) command found." + Get-Command devcontainer -ErrorAction Stop | Out-Null + Write-Host "- devcontainer command found." +} +catch { + Write-Error "A required command is not installed or not in your PATH." + Write-Error "Please ensure '$($_.Exception.Message.Split(':')[0])' and 'devcontainer' are installed and accessible." + exit 1 +} + + +# --- Backend-Specific Initialization --- +if ($Backend -eq 'podman') { + Write-Host "--- Podman Backend Initialization ---" + + # --- Step 1a: Initialize Podman machine --- + Write-Host "Initializing Podman machine 'claudeVM'..." + try { + & podman machine init claudeVM + Write-Host "Podman machine 'claudeVM' initialized or already exists." + } catch { + Write-Error "Failed to initialize Podman machine: $($_.Exception.Message)" + exit 1 # Exit script on error + } + + # --- Step 1b: Start Podman machine --- + Write-Host "Starting Podman machine 'claudeVM'..." + try { + & podman machine start claudeVM -q + Write-Host "Podman machine started or already running." + } catch { + Write-Error "Failed to start Podman machine: $($_.Exception.Message)" + exit 1 + } + + # --- Step 2: Set default connection --- + Write-Host "Setting default Podman connection to 'claudeVM'..." + try { + & podman system connection default claudeVM + Write-Host "Default connection set." + } catch { + Write-Warning "Failed to set default Podman connection (may be already set or machine issue): $($_.Exception.Message)" + } + +} elseif ($Backend -eq 'docker') { + Write-Host "--- Docker Backend Initialization ---" + + # --- Step 1 & 2: Check Docker Desktop --- + Write-Host "Checking if Docker Desktop is running and docker command is available..." + try { + docker info | Out-Null + Write-Host "Docker Desktop (daemon) is running." + } catch { + Write-Error "Docker Desktop is not running or docker command not found." + Write-Error "Please ensure Docker Desktop is running." + exit 1 + } +} + +# --- Step 3: Bring up DevContainer --- +Write-Host "Bringing up DevContainer in the current folder..." +try { + $arguments = @('up', '--workspace-folder', '.') + if ($Backend -eq 'podman') { + $arguments += '--docker-path', 'podman' + } + & devcontainer @arguments + Write-Host "DevContainer startup process completed." +} catch { + Write-Error "Failed to bring up DevContainer: $($_.Exception.Message)" + exit 1 +} + +# --- Step 4: Get DevContainer ID --- +Write-Host "Finding the DevContainer ID..." +$currentFolder = (Get-Location).Path + +try { + $containerId = (& $Backend ps --filter "label=devcontainer.local_folder=$currentFolder" --format '{{.ID}}').Trim() +} catch { + $displayCommand = "$Backend ps --filter `"label=devcontainer.local_folder=$currentFolder`" --format '{{.ID}}'" + Write-Error "Failed to get container ID (Command: $displayCommand): $($_.Exception.Message)" + exit 1 +} + +if (-not $containerId) { + Write-Error "Could not find DevContainer ID for the current folder ('$currentFolder')." + Write-Error "Please check if 'devcontainer up' was successful and the container is running." + exit 1 +} +Write-Host "Found container ID: $containerId" + +# --- Step 5 & 6: Execute command and enter interactive shell inside container --- +Write-Host "Executing 'claude' command and then starting zsh session inside container $($containerId)..." +try { + & $Backend exec -it $containerId zsh -c 'claude; exec zsh' + Write-Host "Interactive session ended." +} catch { + $displayCommand = "$Backend exec -it $containerId zsh -c 'claude; exec zsh'" + Write-Error "Failed to execute command inside container (Command: $displayCommand): $($_.Exception.Message)" + exit 1 +} + +# Notify script completion +Write-Host "--- Script completed ---" \ No newline at end of file diff --git a/examples/hooks/bash_command_validator_example.py b/examples/hooks/bash_command_validator_example.py new file mode 100644 index 000000000..53ab7a829 --- /dev/null +++ b/examples/hooks/bash_command_validator_example.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" +Claude Code Hook: Bash Command Validator +========================================= +This hook runs as a PreToolUse hook for the Bash tool. +It validates bash commands against a set of rules before execution. +In this case it changes grep calls to using rg. + +Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks + +Make sure to change your path to your actual script. + +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python3 /path/to/claude-code/examples/hooks/bash_command_validator_example.py" + } + ] + } + ] + } +} + +""" + +import json +import re +import sys + +# Define validation rules as a list of (regex pattern, message) tuples +_VALIDATION_RULES = [ + ( + r"^grep\b(?!.*\|)", + "Use 'rg' (ripgrep) instead of 'grep' for better performance and features", + ), + ( + r"^find\s+\S+\s+-name\b", + "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance", + ), +] + + +def _validate_command(command: str) -> list[str]: + issues = [] + for pattern, message in _VALIDATION_RULES: + if re.search(pattern, command): + issues.append(message) + return issues + + +def main(): + try: + input_data = json.load(sys.stdin) + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON input: {e}", file=sys.stderr) + # Exit code 1 shows stderr to the user but not to Claude + sys.exit(1) + + tool_name = input_data.get("tool_name", "") + if tool_name != "Bash": + sys.exit(0) + + tool_input = input_data.get("tool_input", {}) + command = tool_input.get("command", "") + + if not command: + sys.exit(0) + + issues = _validate_command(command) + if issues: + for message in issues: + print(f"• {message}", file=sys.stderr) + # Exit code 2 blocks tool call and shows stderr to Claude + sys.exit(2) + + +if __name__ == "__main__": + main()