mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 06:54:13 +08:00
Compare commits
5 Commits
v0.0.54
...
feat/ttyd-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7680c1d501 | ||
|
|
dee63efcf4 | ||
|
|
7afc848186 | ||
|
|
6debac392b | ||
|
|
55fb6a96d0 |
16
.github/workflows/claude.yml
vendored
16
.github/workflows/claude.yml
vendored
@@ -9,10 +9,21 @@ on:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
cloudflare_tunnel_token:
|
||||
description: 'Cloudflare tunnel token to expose Claude UI via browser'
|
||||
required: false
|
||||
type: string
|
||||
direct_prompt:
|
||||
description: 'Direct instruction for Claude'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
@@ -36,4 +47,7 @@ jobs:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash(bun install),Bash(bun test:*),Bash(bun run format),Bash(bun typecheck)"
|
||||
custom_instructions: "You have also been granted tools for editing files and running bun commands (install, run, test, typecheck) for testing your changes: bun install, bun test, bun run format, bun typecheck."
|
||||
model: "claude-opus-4-20250514"
|
||||
model: "claude-opus-4-1-20250805"
|
||||
cloudflare_tunnel_token: ${{ github.event.inputs.cloudflare_tunnel_token }}
|
||||
direct_prompt: ${{ github.event.inputs.direct_prompt }}
|
||||
mode: ${{ github.event_name == 'workflow_dispatch' && 'agent' || 'tag' }}
|
||||
|
||||
@@ -114,6 +114,10 @@ inputs:
|
||||
description: "Restrict network access to these domains only (newline-separated). If not set, no restrictions are applied. Provider domains are auto-detected."
|
||||
required: false
|
||||
default: ""
|
||||
cloudflare_tunnel_token:
|
||||
description: "Cloudflare tunnel token to expose Claude UI via browser (optional)"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
execution_file:
|
||||
@@ -172,7 +176,7 @@ runs:
|
||||
echo "Base-action dependencies installed"
|
||||
cd -
|
||||
# Install Claude Code globally
|
||||
bun install -g @anthropic-ai/claude-code@1.0.69
|
||||
bun install -g @anthropic-ai/claude-code@1.0.70
|
||||
|
||||
- name: Setup Network Restrictions
|
||||
if: steps.prepare.outputs.contains_trigger == 'true' && inputs.experimental_allowed_domains != ''
|
||||
@@ -206,6 +210,7 @@ runs:
|
||||
INPUT_CLAUDE_ENV: ${{ inputs.claude_env }}
|
||||
INPUT_FALLBACK_MODEL: ${{ inputs.fallback_model }}
|
||||
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ github.action_path }}/slash-commands
|
||||
INPUT_CLOUDFLARE_TUNNEL_TOKEN: ${{ inputs.cloudflare_tunnel_token }}
|
||||
|
||||
# Model configuration
|
||||
ANTHROPIC_MODEL: ${{ inputs.model || inputs.anthropic_model }}
|
||||
|
||||
@@ -69,7 +69,7 @@ Add the following to your workflow file:
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt: "Review and fix TypeScript errors"
|
||||
model: "claude-opus-4-20250514"
|
||||
model: "claude-opus-4-1-20250805"
|
||||
fallback_model: "claude-sonnet-4-20250514"
|
||||
allowed_tools: "Bash(git:*),View,GlobTool,GrepTool,BatchTool"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
@@ -217,7 +217,7 @@ Provide the settings configuration directly as a JSON string:
|
||||
prompt: "Your prompt here"
|
||||
settings: |
|
||||
{
|
||||
"model": "claude-opus-4-20250514",
|
||||
"model": "claude-opus-4-1-20250805",
|
||||
"env": {
|
||||
"DEBUG": "true",
|
||||
"API_URL": "https://api.example.com"
|
||||
|
||||
@@ -87,6 +87,10 @@ inputs:
|
||||
description: "Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files)"
|
||||
required: false
|
||||
default: "false"
|
||||
cloudflare_tunnel_token:
|
||||
description: "Cloudflare tunnel token to expose Claude UI via browser (optional)"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
conclusion:
|
||||
@@ -118,7 +122,7 @@ runs:
|
||||
|
||||
- name: Install Claude Code
|
||||
shell: bash
|
||||
run: bun install -g @anthropic-ai/claude-code@1.0.69
|
||||
run: bun install -g @anthropic-ai/claude-code@1.0.70
|
||||
|
||||
- name: Run Claude Code Action
|
||||
shell: bash
|
||||
@@ -147,6 +151,7 @@ runs:
|
||||
INPUT_CLAUDE_ENV: ${{ inputs.claude_env }}
|
||||
INPUT_FALLBACK_MODEL: ${{ inputs.fallback_model }}
|
||||
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ inputs.experimental_slash_commands_dir }}
|
||||
INPUT_CLOUDFLARE_TUNNEL_TOKEN: ${{ inputs.cloudflare_tunnel_token }}
|
||||
|
||||
# Provider configuration
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { preparePrompt } from "./prepare-prompt";
|
||||
import { runClaude } from "./run-claude";
|
||||
import { setupClaudeCodeSettings } from "./setup-claude-code-settings";
|
||||
import { validateEnvironmentVariables } from "./validate-env";
|
||||
import { spawn } from "child_process";
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
@@ -21,7 +22,40 @@ async function run() {
|
||||
promptFile: process.env.INPUT_PROMPT_FILE || "",
|
||||
});
|
||||
|
||||
await runClaude(promptConfig.path, {
|
||||
// Setup ttyd and cloudflared tunnel if token provided
|
||||
let ttydProcess: any = null;
|
||||
let cloudflaredProcess: any = null;
|
||||
|
||||
if (process.env.INPUT_CLOUDFLARE_TUNNEL_TOKEN) {
|
||||
console.log("Setting up ttyd and cloudflared tunnel...");
|
||||
|
||||
// Start ttyd process in background
|
||||
ttydProcess = spawn("ttyd", ["-p", "7681", "-i", "0.0.0.0", "claude"], {
|
||||
stdio: "inherit",
|
||||
detached: true,
|
||||
});
|
||||
|
||||
ttydProcess.on("error", (error: Error) => {
|
||||
console.warn(`ttyd process error: ${error.message}`);
|
||||
});
|
||||
|
||||
// Start cloudflared tunnel
|
||||
cloudflaredProcess = spawn("cloudflared", ["tunnel", "run", "--token", process.env.INPUT_CLOUDFLARE_TUNNEL_TOKEN], {
|
||||
stdio: "inherit",
|
||||
detached: true,
|
||||
});
|
||||
|
||||
cloudflaredProcess.on("error", (error: Error) => {
|
||||
console.warn(`cloudflared process error: ${error.message}`);
|
||||
});
|
||||
|
||||
// Give processes time to start up
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
console.log("ttyd and cloudflared tunnel started");
|
||||
}
|
||||
|
||||
try {
|
||||
await runClaude(promptConfig.path, {
|
||||
allowedTools: process.env.INPUT_ALLOWED_TOOLS,
|
||||
disallowedTools: process.env.INPUT_DISALLOWED_TOOLS,
|
||||
maxTurns: process.env.INPUT_MAX_TURNS,
|
||||
@@ -32,6 +66,23 @@ async function run() {
|
||||
fallbackModel: process.env.INPUT_FALLBACK_MODEL,
|
||||
model: process.env.ANTHROPIC_MODEL,
|
||||
});
|
||||
} finally {
|
||||
// Clean up processes
|
||||
if (ttydProcess) {
|
||||
try {
|
||||
ttydProcess.kill("SIGTERM");
|
||||
} catch (e) {
|
||||
console.warn("Failed to terminate ttyd process");
|
||||
}
|
||||
}
|
||||
if (cloudflaredProcess) {
|
||||
try {
|
||||
cloudflaredProcess.kill("SIGTERM");
|
||||
} catch (e) {
|
||||
console.warn("Failed to terminate cloudflared process");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(`Action failed with error: ${error}`);
|
||||
core.setOutput("conclusion", "failure");
|
||||
|
||||
@@ -134,7 +134,7 @@ describe("setupClaudeCodeSettings", () => {
|
||||
// Then, add new settings
|
||||
const newSettings = JSON.stringify({
|
||||
newKey: "newValue",
|
||||
model: "claude-opus-4-20250514",
|
||||
model: "claude-opus-4-1-20250805",
|
||||
});
|
||||
|
||||
await setupClaudeCodeSettings(newSettings, testHomeDir);
|
||||
@@ -145,7 +145,7 @@ describe("setupClaudeCodeSettings", () => {
|
||||
expect(settings.enableAllProjectMcpServers).toBe(true);
|
||||
expect(settings.existingKey).toBe("existingValue");
|
||||
expect(settings.newKey).toBe("newValue");
|
||||
expect(settings.model).toBe("claude-opus-4-20250514");
|
||||
expect(settings.model).toBe("claude-opus-4-1-20250805");
|
||||
});
|
||||
|
||||
test("should copy slash commands to .claude directory when path provided", async () => {
|
||||
|
||||
@@ -252,7 +252,7 @@ You can provide Claude Code settings to customize behavior such as model selecti
|
||||
with:
|
||||
settings: |
|
||||
{
|
||||
"model": "claude-opus-4-20250514",
|
||||
"model": "claude-opus-4-1-20250805",
|
||||
"env": {
|
||||
"DEBUG": "true",
|
||||
"API_URL": "https://api.example.com"
|
||||
|
||||
@@ -103,6 +103,9 @@ export const reviewMode: Mode = {
|
||||
? formatBody(contextData.body, imageUrlMap)
|
||||
: "No description provided";
|
||||
|
||||
// Using a variable for code blocks to avoid escaping backticks in the template string
|
||||
const codeBlock = "```";
|
||||
|
||||
return `You are Claude, an AI assistant specialized in code reviews for GitHub pull requests. You are operating in REVIEW MODE, which means you should focus on providing thorough code review feedback using GitHub MCP tools for inline comments and suggestions.
|
||||
|
||||
<formatted_context>
|
||||
@@ -155,17 +158,46 @@ REVIEW MODE WORKFLOW:
|
||||
- This provides the full context and latest state of the code
|
||||
- Look at the changed_files section above to see which files were modified
|
||||
|
||||
2. Add comments:
|
||||
- use Bash(gh issue comment:*) to add top-level comments
|
||||
- Use mcp__github_inline_comment__create_inline_comment to add inline comments (prefer this where possible)
|
||||
- Parameters:
|
||||
* path: The file path (e.g., "src/index.js")
|
||||
* line: Line number for single-line comments
|
||||
* startLine & line: For multi-line comments (startLine is the first line, line is the last)
|
||||
* side: "LEFT" (old code) or "RIGHT" (new code)
|
||||
* subjectType: "line" for line-level comments
|
||||
* body: Your comment text
|
||||
2. Create review comments using GitHub MCP tools:
|
||||
- Use Bash(gh issue comment:*) for general PR-level comments
|
||||
- Use mcp__github_inline_comment__create_inline_comment for line-specific feedback (strongly preferred)
|
||||
|
||||
3. When creating inline comments with suggestions:
|
||||
CRITICAL: GitHub's suggestion blocks REPLACE the ENTIRE line range you select
|
||||
- For single-line comments: Use 'line' parameter only
|
||||
- For multi-line comments: Use both 'startLine' and 'line' parameters
|
||||
- The 'body' parameter should contain your comment and/or suggestion block
|
||||
|
||||
How to write code suggestions correctly:
|
||||
a) To remove a line (e.g., removing console.log on line 22):
|
||||
- Set line: 22
|
||||
- Body: ${codeBlock}suggestion
|
||||
${codeBlock}
|
||||
(Empty suggestion block removes the line)
|
||||
|
||||
b) To modify a single line (e.g., fixing line 22):
|
||||
- Set line: 22
|
||||
- Body: ${codeBlock}suggestion
|
||||
await this.emailInput.fill(email);
|
||||
${codeBlock}
|
||||
|
||||
c) To replace multiple lines (e.g., lines 21-23):
|
||||
- Set startLine: 21, line: 23
|
||||
- Body must include ALL lines being replaced:
|
||||
${codeBlock}suggestion
|
||||
async typeEmail(email: string): Promise<void> {
|
||||
await this.emailInput.fill(email);
|
||||
}
|
||||
${codeBlock}
|
||||
|
||||
COMMON MISTAKE TO AVOID:
|
||||
Never duplicate code in suggestions. For example, DON'T do this:
|
||||
${codeBlock}suggestion
|
||||
async typeEmail(email: string): Promise<void> {
|
||||
async typeEmail(email: string): Promise<void> { // WRONG: Duplicate signature!
|
||||
await this.emailInput.fill(email);
|
||||
}
|
||||
${codeBlock}
|
||||
|
||||
REVIEW GUIDELINES:
|
||||
|
||||
@@ -179,13 +211,11 @@ REVIEW GUIDELINES:
|
||||
|
||||
- Provide:
|
||||
* Specific, actionable feedback
|
||||
* Code suggestions when possible (following GitHub's format exactly)
|
||||
* Clear explanations of issues
|
||||
* Constructive criticism
|
||||
* Code suggestions using the exact format described above
|
||||
* Clear explanations of issues found
|
||||
* Constructive criticism with solutions
|
||||
* Recognition of good practices
|
||||
* For complex changes that require multiple modifications:
|
||||
- Create separate comments for each logical change
|
||||
- Or explain the full solution in text without a suggestion block
|
||||
* For complex changes: Create separate inline comments for each logical change
|
||||
|
||||
- Communication:
|
||||
* All feedback goes through GitHub's review system
|
||||
|
||||
Reference in New Issue
Block a user