diff --git a/action.yml b/action.yml index 3e47948e9..b62fe5197 100644 --- a/action.yml +++ b/action.yml @@ -110,6 +110,14 @@ inputs: description: "Use just one comment to deliver issue/PR comments" required: false default: "false" + sticky_comment_app_bot_id: + description: "The ID of the app bot that will be used to create the sticky comment. Defaults to the Claude app bot ID." + required: false + default: "209825114" + sticky_comment_app_bot_name: + description: "The name of the app bot that will be used to create the sticky comment. Defaults to 'claude'." + required: false + default: "claude" use_commit_signing: description: "Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands" required: false @@ -163,6 +171,8 @@ runs: ALLOWED_BOTS: ${{ inputs.allowed_bots }} GITHUB_RUN_ID: ${{ github.run_id }} USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }} + STICKY_COMMENT_APP_BOT_ID: ${{ inputs.sticky_comment_app_bot_id }} + STICKY_COMMENT_APP_BOT_NAME: ${{ inputs.sticky_comment_app_bot_name }} DEFAULT_WORKFLOW_TOKEN: ${{ github.token }} ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }} USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }} @@ -265,6 +275,8 @@ runs: PREPARE_SUCCESS: ${{ steps.prepare.outcome == 'success' }} PREPARE_ERROR: ${{ steps.prepare.outputs.prepare_error || '' }} USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }} + STICKY_COMMENT_APP_BOT_ID: ${{ inputs.sticky_comment_app_bot_id }} + STICKY_COMMENT_APP_BOT_NAME: ${{ inputs.sticky_comment_app_bot_name }} USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }} - name: Display Claude Code Report diff --git a/docs/faq.md b/docs/faq.md index 2f03b31a7..bb304c280 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -141,7 +141,7 @@ Comments appear as claude[bot] when the action uses its built-in authentication. **Solution**: Remove `github_token` from your workflow file unless you're using a custom GitHub App. -**Note**: The `use_sticky_comment` feature only works with claude[bot] authentication. If you're using a custom `github_token`, sticky comments won't update properly since they expect the claude[bot] username. +**Note**: The `use_sticky_comment` feature by default only works with claude[bot] authentication. If you're using a custom `github_token`, sticky comments won't update properly unless you also pass `sticky_comment_app_bot_id` or `sticky_comment_app_bot_name`. ## MCP Servers and Extended Functionality diff --git a/docs/usage.md b/docs/usage.md index 7e7708078..7768c72fe 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -48,37 +48,39 @@ jobs: ## Inputs -| Input | Description | Required | Default | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -| `mode` | Execution mode: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation), 'experimental-review' (for PR reviews) | No | `tag` | -| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | -| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | -| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - | -| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - | -| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | -| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - | -| `timeout_minutes` | Timeout in minutes for execution | No | `30` | -| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | -| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | -| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - | -| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - | -| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - | -| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | -| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | -| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" | -| `disallowed_tools` | Tools that Claude should never use | No | "" | -| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" | -| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | -| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | -| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | -| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | -| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | -| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" | -| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | -| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | -| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | -| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | -| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | +| Input | Description | Required | Default | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------- | +| `mode` | Execution mode: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation), 'experimental-review' (for PR reviews) | No | `tag` | +| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | +| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | +| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - | +| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - | +| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | +| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - | +| `timeout_minutes` | Timeout in minutes for execution | No | `30` | +| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | +| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | +| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - | +| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - | +| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - | +| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | +| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | +| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" | +| `disallowed_tools` | Tools that Claude should never use | No | "" | +| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" | +| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | +| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | +| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | +| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | +| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | +| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" | +| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | +| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | +| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | +| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | +| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | +| `sticky_comment_app_bot_id` | The ID of the app bot that will be used to create the sticky comment. Defaults to the Claude app bot ID. | No | `209825114` | +| `sticky_comment_app_bot_name` | The name of the app bot that will be used to create the sticky comment. Defaults to 'claude'. | No | `claude` | \*Required when using direct Anthropic API (default and when not using Bedrock or Vertex) diff --git a/src/github/context.ts b/src/github/context.ts index 15a7fb9ed..56147000f 100644 --- a/src/github/context.ts +++ b/src/github/context.ts @@ -78,6 +78,8 @@ type BaseContext = { additionalPermissions: Map; useCommitSigning: boolean; allowedBots: string; + stickyCommentAppBotId: number; + stickyCommentAppBotName: string; }; }; @@ -138,6 +140,11 @@ export function parseGitHubContext(): GitHubContext { ), useCommitSigning: process.env.USE_COMMIT_SIGNING === "true", allowedBots: process.env.ALLOWED_BOTS ?? "", + stickyCommentAppBotId: parseInt( + process.env.STICKY_COMMENT_APP_BOT_ID ?? "209825114", + ), + stickyCommentAppBotName: + process.env.STICKY_COMMENT_APP_BOT_NAME ?? "claude", }, }; diff --git a/src/github/operations/comments/create-initial.ts b/src/github/operations/comments/create-initial.ts index 1243035b7..692c8b8d0 100644 --- a/src/github/operations/comments/create-initial.ts +++ b/src/github/operations/comments/create-initial.ts @@ -14,8 +14,6 @@ import { } from "../../context"; import type { Octokit } from "@octokit/rest"; -const CLAUDE_APP_BOT_ID = 209825114; - export async function createInitialComment( octokit: Octokit, context: ParsedGitHubContext, @@ -39,10 +37,13 @@ export async function createInitialComment( issue_number: context.entityNumber, }); const existingComment = comments.data.find((comment) => { - const idMatch = comment.user?.id === CLAUDE_APP_BOT_ID; + const idMatch = + comment.user?.id === context.inputs.stickyCommentAppBotId; const botNameMatch = comment.user?.type === "Bot" && - comment.user?.login.toLowerCase().includes("claude"); + comment.user?.login + .toLowerCase() + .includes(context.inputs.stickyCommentAppBotName); const bodyMatch = comment.body === initialBody; return idMatch || botNameMatch || bodyMatch; diff --git a/test/install-mcp-server.test.ts b/test/install-mcp-server.test.ts index ded103055..6174770e0 100644 --- a/test/install-mcp-server.test.ts +++ b/test/install-mcp-server.test.ts @@ -2,6 +2,7 @@ import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test"; import { prepareMcpConfig } from "../src/mcp/install-mcp-server"; import * as core from "@actions/core"; import type { ParsedGitHubContext } from "../src/github/context"; +import { defaultStickyCommentInputs } from "./mockContext"; describe("prepareMcpConfig", () => { let consoleInfoSpy: any; @@ -35,6 +36,7 @@ describe("prepareMcpConfig", () => { overridePrompt: "", branchPrefix: "", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", diff --git a/test/mockContext.ts b/test/mockContext.ts index 47cdd1ea8..49abc3ba5 100644 --- a/test/mockContext.ts +++ b/test/mockContext.ts @@ -10,6 +10,11 @@ import type { PullRequestReviewCommentEvent, } from "@octokit/webhooks-types"; +export const defaultStickyCommentInputs = { + stickyCommentAppBotId: 209825114, + stickyCommentAppBotName: "claude", +}; + const defaultInputs = { mode: "tag" as const, triggerPhrase: "/claude", @@ -26,6 +31,7 @@ const defaultInputs = { timeoutMinutes: 30, branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", diff --git a/test/permissions.test.ts b/test/permissions.test.ts index c0395ad1c..7565df2bb 100644 --- a/test/permissions.test.ts +++ b/test/permissions.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"; import * as core from "@actions/core"; import { checkWritePermissions } from "../src/github/validation/permissions"; import type { ParsedGitHubContext } from "../src/github/context"; +import { defaultStickyCommentInputs } from "./mockContext"; describe("checkWritePermissions", () => { let coreInfoSpy: any; @@ -71,6 +72,7 @@ describe("checkWritePermissions", () => { overridePrompt: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", diff --git a/test/trigger-validation.test.ts b/test/trigger-validation.test.ts index 8f18319d5..e7093a1a3 100644 --- a/test/trigger-validation.test.ts +++ b/test/trigger-validation.test.ts @@ -20,6 +20,7 @@ import type { PullRequestReviewEvent, } from "@octokit/webhooks-types"; import type { ParsedGitHubContext } from "../src/github/context"; +import { defaultStickyCommentInputs } from "./mockContext"; describe("checkContainsTrigger", () => { describe("direct prompt trigger", () => { @@ -39,6 +40,7 @@ describe("checkContainsTrigger", () => { customInstructions: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", @@ -73,6 +75,7 @@ describe("checkContainsTrigger", () => { customInstructions: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", @@ -291,6 +294,7 @@ describe("checkContainsTrigger", () => { customInstructions: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", @@ -326,6 +330,7 @@ describe("checkContainsTrigger", () => { customInstructions: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "", @@ -361,6 +366,7 @@ describe("checkContainsTrigger", () => { customInstructions: "", branchPrefix: "claude/", useStickyComment: false, + ...defaultStickyCommentInputs, additionalPermissions: new Map(), useCommitSigning: false, allowedBots: "",