surshar / claude-code
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.
Built by Anthropic · github.com/anthropics
| 1 | name: "Claude Code Action" |
| 2 | description: "Run Claude Code in GitHub Actions workflows" |
| 3 | |
| 4 | inputs: |
| 5 | github_token: |
| 6 | description: "GitHub token with repo and issues permissions" |
| 7 | required: true |
| 8 | anthropic_api_key: |
| 9 | description: "Anthropic API key" |
| 10 | required: true |
| 11 | prompt: |
| 12 | description: "The prompt to send to Claude Code" |
| 13 | required: false |
| 14 | default: "" |
| 15 | prompt_file: |
| 16 | description: "Path to a file containing the prompt to send to Claude Code" |
| 17 | required: false |
| 18 | default: "" |
| 19 | allowed_tools: |
| 20 | description: "Comma-separated list of allowed tools for Claude Code to use" |
| 21 | required: false |
| 22 | default: "" |
| 23 | output_file: |
| 24 | description: "File to save Claude Code output to (optional)" |
| 25 | required: false |
| 26 | default: "" |
| 27 | timeout_minutes: |
| 28 | description: "Timeout in minutes for Claude Code execution" |
| 29 | required: false |
| 30 | default: "10" |
| 31 | install_github_mcp: |
| 32 | description: "Whether to install the GitHub MCP server" |
| 33 | required: false |
| 34 | default: "false" |
| 35 | |
| 36 | runs: |
| 37 | using: "composite" |
| 38 | steps: |
| 39 | - name: Install Claude Code |
| 40 | shell: bash |
| 41 | run: npm install -g @anthropic-ai/claude-code |
| 42 | |
| 43 | - name: Install GitHub MCP Server |
| 44 | if: inputs.install_github_mcp == 'true' |
| 45 | shell: bash |
| 46 | run: | |
| 47 | claude mcp add-json github '{ |
| 48 | "command": "docker", |
| 49 | "args": [ |
| 50 | "run", |
| 51 | "-i", |
| 52 | "--rm", |
| 53 | "-e", |
| 54 | "GITHUB_PERSONAL_ACCESS_TOKEN", |
| 55 | "ghcr.io/github/github-mcp-server:sha-ff3036d" |
| 56 | ], |
| 57 | "env": { |
| 58 | "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.GITHUB_TOKEN }}" |
| 59 | } |
| 60 | }' |
| 61 | |
| 62 | - name: Prepare Prompt File |
| 63 | shell: bash |
| 64 | id: prepare_prompt |
| 65 | run: | |
| 66 | # Check if either prompt or prompt_file is provided |
| 67 | if [ -z "${{ inputs.prompt }}" ] && [ -z "${{ inputs.prompt_file }}" ]; then |
| 68 | echo "::error::Neither 'prompt' nor 'prompt_file' was provided. At least one is required." |
| 69 | exit 1 |
| 70 | fi |
| 71 | |
| 72 | # Determine which prompt source to use |
| 73 | if [ ! -z "${{ inputs.prompt_file }}" ]; then |
| 74 | # Check if the prompt file exists |
| 75 | if [ ! -f "${{ inputs.prompt_file }}" ]; then |
| 76 | echo "::error::Prompt file '${{ inputs.prompt_file }}' does not exist." |
| 77 | exit 1 |
| 78 | fi |
| 79 | |
| 80 | # Use the provided prompt file |
| 81 | PROMPT_PATH="${{ inputs.prompt_file }}" |
| 82 | else |
| 83 | mkdir -p /tmp/claude-action |
| 84 | PROMPT_PATH="/tmp/claude-action/prompt.txt" |
| 85 | echo "${{ inputs.prompt }}" > "$PROMPT_PATH" |
| 86 | fi |
| 87 | |
| 88 | # Verify the prompt file is not empty |
| 89 | if [ ! -s "$PROMPT_PATH" ]; then |
| 90 | echo "::error::Prompt is empty. Please provide a non-empty prompt." |
| 91 | exit 1 |
| 92 | fi |
| 93 | |
| 94 | # Save the prompt path for the next step |
| 95 | echo "PROMPT_PATH=$PROMPT_PATH" >> $GITHUB_ENV |
| 96 | |
| 97 | - name: Run Claude Code |
| 98 | shell: bash |
| 99 | id: run_claude |
| 100 | run: | |
| 101 | ALLOWED_TOOLS_ARG="" |
| 102 | if [ ! -z "${{ inputs.allowed_tools }}" ]; then |
| 103 | ALLOWED_TOOLS_ARG="--allowedTools ${{ inputs.allowed_tools }}" |
| 104 | fi |
| 105 | |
| 106 | # Set a timeout to ensure the command doesn't run indefinitely |
| 107 | timeout_seconds=$((${{ inputs.timeout_minutes }} * 60)) |
| 108 | |
| 109 | if [ -z "${{ inputs.output_file }}" ]; then |
| 110 | # Run Claude Code and output to console |
| 111 | timeout $timeout_seconds claude \ |
| 112 | -p \ |
| 113 | --verbose \ |
| 114 | --output-format stream-json \ |
| 115 | "$(cat ${{ env.PROMPT_PATH }})" \ |
| 116 | ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} |
| 117 | else |
| 118 | # Run Claude Code and tee output to console and file |
| 119 | timeout $timeout_seconds claude \ |
| 120 | -p \ |
| 121 | --verbose \ |
| 122 | --output-format stream-json \ |
| 123 | "$(cat ${{ env.PROMPT_PATH }})" \ |
| 124 | ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} | tee output.txt |
| 125 | |
| 126 | # Process output.txt into JSON in a separate step |
| 127 | jq -s '.' output.txt > output.json |
| 128 | |
| 129 | # Extract the result from the last item in the array (system message) |
| 130 | jq -r '.[-1].result' output.json > "${{ inputs.output_file }}" |
| 131 | |
| 132 | echo "Complete output saved to output.json, final response saved to ${{ inputs.output_file }}" |
| 133 | fi |
| 134 | env: |
| 135 | ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} |
| 136 | GITHUB_TOKEN: ${{ inputs.github_token }} |