diff --git a/action.yml b/action.yml index d1b9ad0fb..18806ab47 100644 --- a/action.yml +++ b/action.yml @@ -89,6 +89,10 @@ inputs: description: "Use just one comment to deliver issue/PR comments" required: false default: "false" + classify_inline_comments: + description: "Buffer inline comments without confirmed=true and classify them (real review vs test/probe) before posting after the session ends. Set to 'false' to post all inline comments immediately (pre-buffering behavior)." + required: false + default: "true" use_commit_signing: description: "Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands" required: false @@ -204,6 +208,7 @@ runs: EXCLUDE_COMMENTS_BY_ACTOR: ${{ inputs.exclude_comments_by_actor }} GITHUB_RUN_ID: ${{ github.run_id }} USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }} + CLASSIFY_INLINE_COMMENTS: ${{ inputs.classify_inline_comments }} DEFAULT_WORKFLOW_TOKEN: ${{ github.token }} USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }} SSH_SIGNING_KEY: ${{ inputs.ssh_signing_key }} @@ -282,6 +287,18 @@ runs: run: | bun run ${GITHUB_ACTION_PATH}/src/entrypoints/cleanup-ssh-signing.ts + - name: Post buffered inline comments + if: always() && inputs.classify_inline_comments != 'false' + shell: bash + env: + GITHUB_TOKEN: ${{ steps.run.outputs.github_token || inputs.github_token || github.token }} + REPO_OWNER: ${{ github.event.repository.owner.login }} + REPO_NAME: ${{ github.event.repository.name }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} + run: | + bun run ${GITHUB_ACTION_PATH}/src/entrypoints/post-buffered-inline-comments.ts + - name: Revoke app token if: always() && inputs.github_token == '' && steps.run.outputs.skipped_due_to_workflow_validation_mismatch != 'true' shell: bash diff --git a/docs/solutions.md b/docs/solutions.md index 98f5d7280..dd7fcb5b7 100644 --- a/docs/solutions.md +++ b/docs/solutions.md @@ -55,7 +55,7 @@ jobs: Note: The PR branch is already checked out in the current working directory. Use `gh pr comment` for top-level feedback. - Use `mcp__github_inline_comment__create_inline_comment` to highlight specific code issues. + Use `mcp__github_inline_comment__create_inline_comment` (with `confirmed: true`) to highlight specific code issues. Only post GitHub comments - don't submit review text as messages. claude_args: | @@ -585,7 +585,7 @@ prompt: | ### Common Tool Permissions - **PR Comments**: `Bash(gh pr comment:*)` -- **Inline Comments**: `mcp__github_inline_comment__create_inline_comment` +- **Inline Comments**: `mcp__github_inline_comment__create_inline_comment` — pass `confirmed: true` to post immediately. When omitted, the comment is buffered and classified after the session ends (real review comments post, test/probe comments are filtered). This prevents subagent test comments from reaching PRs. To disable classification entirely, set `classify_inline_comments: 'false'` on the action. - **File Operations**: `Read,Write,Edit` - **Git Operations**: `Bash(git:*)` diff --git a/docs/usage.md b/docs/usage.md index f2ceadaf8..89677fa0d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -52,35 +52,36 @@ jobs: ## Inputs -| Input | Description | Required | Default | -| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------- | -| `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\* | - | -| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | -| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` | -| `include_fix_links` | Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue | No | `true` | -| `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | -| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | -| `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 | - | -| `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` | -| `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/` | -| `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 | "" | -| `use_commit_signing` | Enable commit signing using GitHub's API. Simple but cannot perform complex git operations like rebasing. See [Security](./security.md#commit-signing) | No | `false` | -| `ssh_signing_key` | SSH private key for signing commits. Enables signed commits with full git CLI support (rebasing, etc.). See [Security](./security.md#commit-signing) | No | "" | -| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID). Required with `ssh_signing_key` for verified commits | No | `41898282` | -| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name). Required with `ssh_signing_key` for verified commits | No | `claude[bot]` | -| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots. **⚠️ On public repos with `'*'`, external Apps may be able to invoke this action.** See [Security](./security.md) | No | "" | -| `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" | -| `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" | -| `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" | -| `plugin_marketplaces` | Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., see example in workflow above). Marketplaces are added before plugin installation | No | "" | -| `plugins` | Newline-separated list of Claude Code plugin names to install (e.g., see example in workflow above). Plugins are installed before Claude Code execution | No | "" | +| Input | Description | Required | Default | +| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------- | +| `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\* | - | +| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | +| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` | +| `include_fix_links` | Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue | No | `true` | +| `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | +| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | +| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | +| `classify_inline_comments` | Buffer inline comments without `confirmed: true` and classify them (real review vs test/probe) via Haiku before posting after the session ends. Prevents subagent test comments. Set `'false'` to post all inline comments immediately | No | `true` | +| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | 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` | +| `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/` | +| `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 | "" | +| `use_commit_signing` | Enable commit signing using GitHub's API. Simple but cannot perform complex git operations like rebasing. See [Security](./security.md#commit-signing) | No | `false` | +| `ssh_signing_key` | SSH private key for signing commits. Enables signed commits with full git CLI support (rebasing, etc.). See [Security](./security.md#commit-signing) | No | "" | +| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID). Required with `ssh_signing_key` for verified commits | No | `41898282` | +| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name). Required with `ssh_signing_key` for verified commits | No | `claude[bot]` | +| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots. **⚠️ On public repos with `'*'`, external Apps may be able to invoke this action.** See [Security](./security.md) | No | "" | +| `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" | +| `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" | +| `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" | +| `plugin_marketplaces` | Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., see example in workflow above). Marketplaces are added before plugin installation | No | "" | +| `plugins` | Newline-separated list of Claude Code plugin names to install (e.g., see example in workflow above). Plugins are installed before Claude Code execution | No | "" | ### Deprecated Inputs diff --git a/src/entrypoints/collect-inputs.ts b/src/entrypoints/collect-inputs.ts index a4637438e..079565c7b 100644 --- a/src/entrypoints/collect-inputs.ts +++ b/src/entrypoints/collect-inputs.ts @@ -23,6 +23,7 @@ export function collectActionInputsPresence(): string { github_token: "", max_turns: "", use_sticky_comment: "false", + classify_inline_comments: "true", use_commit_signing: "false", ssh_signing_key: "", }; diff --git a/src/entrypoints/post-buffered-inline-comments.ts b/src/entrypoints/post-buffered-inline-comments.ts new file mode 100644 index 000000000..763dae7fd --- /dev/null +++ b/src/entrypoints/post-buffered-inline-comments.ts @@ -0,0 +1,233 @@ +#!/usr/bin/env bun +/** + * Reads buffered inline-comment calls from /tmp/inline-comments-buffer.jsonl, + * classifies each as "real review" vs "test/probe" using Haiku, and posts + * only the real ones. Calls with confirmed=false are never posted. + * + * If the Anthropic API is unavailable (Bedrock/Vertex users without a direct + * key), falls back to posting everything with confirmed !== false. This + * preserves backward compatibility — before this change, all unconfirmed + * calls posted immediately. + */ +import { readFileSync } from "fs"; +import { createOctokit } from "../github/api/client"; + +const BUFFER_PATH = "/tmp/inline-comments-buffer.jsonl"; + +type BufferedComment = { + ts: string; + path: string; + line?: number; + startLine?: number; + side?: "LEFT" | "RIGHT"; + commit_id?: string; + body: string; + confirmed?: boolean; +}; + +const CLASSIFICATION_PROMPT = `You are classifying PR inline comments as either REAL code review feedback or TEST/PROBE calls. + +A TEST/PROBE call is when an automated agent is checking whether a commenting tool works. These typically: +- Start with phrases like "Test comment", "Testing if", "Can I", "Does this work", "Checking if" +- Have generic/placeholder content not specific to any code +- Exist to verify tool functionality, not to provide review feedback + +A REAL review comment: +- Discusses specific code, logic, bugs, or style +- Provides actionable feedback for the PR author +- References concrete aspects of the change + +For each numbered comment body below, respond with ONLY a JSON array of booleans where true = REAL review comment, false = test/probe. No other text. + +Comments: +`; + +async function classifyComments(bodies: string[]): Promise { + const apiKey = process.env.ANTHROPIC_API_KEY; + if (!apiKey) { + console.log( + "ANTHROPIC_API_KEY not set — skipping classification, posting all unconfirmed comments", + ); + return null; + } + + const prompt = + CLASSIFICATION_PROMPT + + bodies.map((b, i) => `${i + 1}. ${JSON.stringify(b)}`).join("\n"); + + try { + const res = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + headers: { + "content-type": "application/json", + "x-api-key": apiKey, + "anthropic-version": "2023-06-01", + }, + body: JSON.stringify({ + model: "claude-haiku-4-5", + max_tokens: 1024, + messages: [{ role: "user", content: prompt }], + }), + }); + + if (!res.ok) { + console.log( + `Classification API returned ${res.status} — posting all unconfirmed comments`, + ); + return null; + } + + const data = (await res.json()) as { + content: { type: string; text: string }[]; + }; + const text = data.content.find((c) => c.type === "text")?.text ?? ""; + const match = text.match(/\[[\s\S]*\]/); + if (!match) { + console.log( + "Could not parse classification response — posting all unconfirmed comments", + ); + return null; + } + const parsed = JSON.parse(match[0]); + if ( + !Array.isArray(parsed) || + parsed.length !== bodies.length || + !parsed.every((v) => typeof v === "boolean") + ) { + console.log( + "Classification response shape mismatch — posting all unconfirmed comments", + ); + return null; + } + return parsed; + } catch (e) { + console.log( + `Classification failed (${e instanceof Error ? e.message : String(e)}) — posting all unconfirmed comments`, + ); + return null; + } +} + +async function postComment( + octokit: ReturnType["rest"], + owner: string, + repo: string, + pull_number: number, + headSha: string, + c: BufferedComment, +): Promise { + const params: Parameters[0] = { + owner, + repo, + pull_number, + body: c.body, + path: c.path, + side: c.side || "RIGHT", + commit_id: c.commit_id || headSha, + }; + if (c.startLine) { + params.start_line = c.startLine; + params.start_side = c.side || "RIGHT"; + params.line = c.line; + } else { + params.line = c.line; + } + try { + await octokit.rest.pulls.createReviewComment(params); + return true; + } catch (e) { + console.log( + ` failed ${c.path}:${c.line}: ${e instanceof Error ? e.message : String(e)}`, + ); + return false; + } +} + +async function main() { + let raw: string; + try { + raw = readFileSync(BUFFER_PATH, "utf8"); + } catch { + console.log("No buffered inline comments"); + return; + } + + const comments: BufferedComment[] = raw + .split("\n") + .filter(Boolean) + .map((line) => JSON.parse(line)); + + if (comments.length === 0) { + console.log("No buffered inline comments"); + return; + } + + console.log(`Found ${comments.length} buffered inline comment(s)`); + + const githubToken = process.env.GITHUB_TOKEN; + const owner = process.env.REPO_OWNER; + const repo = process.env.REPO_NAME; + const prNumber = process.env.PR_NUMBER; + + if (!githubToken || !owner || !repo || !prNumber) { + console.log( + "::warning::Missing GITHUB_TOKEN/REPO_OWNER/REPO_NAME/PR_NUMBER — cannot post buffered comments", + ); + return; + } + + // Partition: confirmed=false are never posted; the rest are candidates + const neverPost = comments.filter((c) => c.confirmed === false); + const candidates = comments.filter((c) => c.confirmed !== false); + + if (neverPost.length > 0) { + console.log(` ${neverPost.length} with confirmed=false — not posting`); + } + + if (candidates.length === 0) { + return; + } + + // Classify candidates + const verdicts = await classifyComments(candidates.map((c) => c.body)); + const toPost = + verdicts === null + ? candidates + : candidates.filter((_, i) => verdicts[i] === true); + const filtered = + verdicts === null ? [] : candidates.filter((_, i) => verdicts[i] === false); + + if (filtered.length > 0) { + console.log( + `::warning::${filtered.length} buffered comment(s) classified as test/probe — NOT posted:`, + ); + for (const c of filtered) { + console.log(` [${c.path}:${c.line}] ${c.body.slice(0, 120)}`); + } + } + + if (toPost.length === 0) { + console.log("No real comments to post"); + return; + } + + const octokit = createOctokit(githubToken).rest; + const pull_number = parseInt(prNumber, 10); + const pr = await octokit.pulls.get({ owner, repo, pull_number }); + const headSha = pr.data.head.sha; + + console.log(`Posting ${toPost.length} classified-as-real comment(s)`); + let posted = 0; + for (const c of toPost) { + if (await postComment(octokit, owner, repo, pull_number, headSha, c)) { + console.log(` posted ${c.path}:${c.line}`); + posted++; + } + } + console.log(`Posted ${posted}/${toPost.length}`); +} + +main().catch((e) => { + console.error("post-buffered-inline-comments failed:", e); + process.exit(1); +}); diff --git a/src/github/context.ts b/src/github/context.ts index 936e70e91..a0b388cba 100644 --- a/src/github/context.ts +++ b/src/github/context.ts @@ -90,6 +90,7 @@ type BaseContext = { branchPrefix: string; branchNameTemplate?: string; useStickyComment: boolean; + classifyInlineComments: boolean; useCommitSigning: boolean; sshSigningKey: string; botId: string; @@ -150,6 +151,7 @@ export function parseGitHubContext(): GitHubContext { branchPrefix: process.env.BRANCH_PREFIX ?? "claude/", branchNameTemplate: process.env.BRANCH_NAME_TEMPLATE, useStickyComment: process.env.USE_STICKY_COMMENT === "true", + classifyInlineComments: process.env.CLASSIFY_INLINE_COMMENTS !== "false", useCommitSigning: process.env.USE_COMMIT_SIGNING === "true", sshSigningKey: process.env.SSH_SIGNING_KEY || "", botId: process.env.BOT_ID ?? String(CLAUDE_APP_BOT_ID), diff --git a/src/mcp/github-inline-comment-server.ts b/src/mcp/github-inline-comment-server.ts index 703cda2e0..535124f32 100644 --- a/src/mcp/github-inline-comment-server.ts +++ b/src/mcp/github-inline-comment-server.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { appendFileSync } from "fs"; import { z } from "zod"; import { createOctokit } from "../github/api/client"; import { sanitizeContent } from "../github/utils/sanitizer"; @@ -10,6 +11,13 @@ const REPO_OWNER = process.env.REPO_OWNER; const REPO_NAME = process.env.REPO_NAME; const PR_NUMBER = process.env.PR_NUMBER; +// Calls without confirmed=true are buffered here instead of posted. This +// prevents subagents from posting test/probe comments when they inherit this +// tool and probe it after hitting unrelated errors. The action's post-step +// reports the buffer count for diagnostics. +const BUFFER_PATH = "/tmp/inline-comments-buffer.jsonl"; +const CLASSIFY_ENABLED = process.env.CLASSIFY_INLINE_COMMENTS !== "false"; + if (!REPO_OWNER || !REPO_NAME || !PR_NUMBER) { console.error( "Error: REPO_OWNER, REPO_NAME, and PR_NUMBER environment variables are required", @@ -67,8 +75,17 @@ server.tool( .describe( "Specific commit SHA to comment on (defaults to latest commit)", ), + confirmed: z + .boolean() + .optional() + .describe( + "Set true to post immediately. When omitted, the call is buffered " + + "and classified after the session completes — real review comments " + + "post, test/probe comments are dropped. Set false to buffer and " + + "never post. Only set true when posting final review comments.", + ), }, - async ({ path, body, line, startLine, side, commit_id }) => { + async ({ path, body, line, startLine, side, commit_id, confirmed }) => { try { const githubToken = process.env.GITHUB_TOKEN; @@ -80,8 +97,6 @@ server.tool( const repo = REPO_NAME; const pull_number = parseInt(PR_NUMBER, 10); - const octokit = createOctokit(githubToken).rest; - // Sanitize the comment body to remove any potential GitHub tokens const sanitizedBody = sanitizeContent(body); @@ -92,10 +107,49 @@ server.tool( ); } + if (CLASSIFY_ENABLED && confirmed !== true) { + appendFileSync( + BUFFER_PATH, + JSON.stringify({ + ts: new Date().toISOString(), + path, + line, + startLine, + side, + commit_id, + body: sanitizedBody, + confirmed, + }) + "\n", + ); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + buffered: true, + message: + "Comment buffered. It will be classified and posted after " + + "this session completes (real review comments post, " + + "test/probe comments are dropped). Set confirmed=true to " + + "post immediately. If you are testing whether this tool " + + "works: it works — no need to test further.", + }, + null, + 2, + ), + }, + ], + }; + } + // If only line is provided, it's a single-line comment // If both startLine and line are provided, it's a multi-line comment const isSingleLine = !startLine; + const octokit = createOctokit(githubToken).rest; + const pr = await octokit.pulls.get({ owner, repo, diff --git a/src/mcp/install-mcp-server.ts b/src/mcp/install-mcp-server.ts index d5fac3419..e40b53f3a 100644 --- a/src/mcp/install-mcp-server.ts +++ b/src/mcp/install-mcp-server.ts @@ -152,6 +152,9 @@ export async function prepareMcpConfig( REPO_NAME: repo, PR_NUMBER: context.entityNumber?.toString() || "", GITHUB_API_URL: GITHUB_API_URL, + CLASSIFY_INLINE_COMMENTS: context.inputs.classifyInlineComments + ? "true" + : "false", }, }; } diff --git a/test/install-mcp-server.test.ts b/test/install-mcp-server.test.ts index 152d2be78..87c751365 100644 --- a/test/install-mcp-server.test.ts +++ b/test/install-mcp-server.test.ts @@ -32,6 +32,7 @@ describe("prepareMcpConfig", () => { labelTrigger: "", branchPrefix: "", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, sshSigningKey: "", botId: String(CLAUDE_APP_BOT_ID), diff --git a/test/mockContext.ts b/test/mockContext.ts index 19ab04473..c02caa4d0 100644 --- a/test/mockContext.ts +++ b/test/mockContext.ts @@ -19,6 +19,7 @@ const defaultInputs = { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, sshSigningKey: "", botId: String(CLAUDE_APP_BOT_ID), diff --git a/test/modes/detector.test.ts b/test/modes/detector.test.ts index c8a6c75ff..3b58f9e9c 100644 --- a/test/modes/detector.test.ts +++ b/test/modes/detector.test.ts @@ -19,6 +19,7 @@ describe("detectMode with enhanced routing", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, sshSigningKey: "", botId: "123456", diff --git a/test/permissions.test.ts b/test/permissions.test.ts index cf2efdb0e..1d6545755 100644 --- a/test/permissions.test.ts +++ b/test/permissions.test.ts @@ -67,6 +67,7 @@ describe("checkWritePermissions", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, sshSigningKey: "", botId: String(CLAUDE_APP_BOT_ID), diff --git a/test/trigger-validation.test.ts b/test/trigger-validation.test.ts index 36c41f287..f235928b8 100644 --- a/test/trigger-validation.test.ts +++ b/test/trigger-validation.test.ts @@ -34,6 +34,7 @@ describe("checkContainsTrigger", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, allowedBots: "", }, @@ -62,6 +63,7 @@ describe("checkContainsTrigger", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, allowedBots: "", }, @@ -274,6 +276,7 @@ describe("checkContainsTrigger", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, allowedBots: "", }, @@ -303,6 +306,7 @@ describe("checkContainsTrigger", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, allowedBots: "", }, @@ -332,6 +336,7 @@ describe("checkContainsTrigger", () => { labelTrigger: "", branchPrefix: "claude/", useStickyComment: false, + classifyInlineComments: true, useCommitSigning: false, allowedBots: "", },