chore: merge 7 upstream commits from anthropics/claude-code-action
Conflicts resolved: - docs/usage.md: kept fork's `platform` row, added upstream's new `classify_inline_comments` row, updated `allowed_bots` description with upstream's security warning - src/create-prompt/index.ts: kept fork's DISALLOWED_TOOLS export (consumed by run.ts) vs upstream's dead-code comment - src/modes/tag/index.ts: merged both fork's userAllowedTools and upstream's gitPushWrapper variable (both needed by shared code) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
ec394266fc
25 files changed
+421
-57
No files matched your search
+18
-1
@@ -28,7 +28,7 @@ inputs:
|
||||
required: false
|
||||
default: ""
|
||||
allowed_bots:
|
||||
description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots."
|
||||
description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots. WARNING: On public repos with '*', external Apps may be able to invoke this action with prompts they control. See docs/security.md."
|
||||
required: false
|
||||
default: ""
|
||||
allowed_non_write_users:
|
||||
@@ -97,6 +97,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
|
||||
@@ -252,6 +256,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 }}
|
||||
@@ -334,6 +339,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' && steps.detect-platform.outputs.platform != 'forgejo'
|
||||
shell: bash
|
||||
|
||||
@@ -124,7 +124,7 @@ runs:
|
||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
run: |
|
||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||
CLAUDE_CODE_VERSION="2.1.71"
|
||||
CLAUDE_CODE_VERSION="2.1.76"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "@anthropic-ai/claude-code-base-action",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.71",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.76",
|
||||
"shell-quote": "^1.8.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.71", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-pIsQJnM7Y+cJHL7aFY6SCCW3FIni218gVEpPqG8XGowfYxboFNBbNssWiUNRwthT8bp9jypcX7q5kx0Xsw14xg=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.76", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-HZxvnT8ZWkzCnQygaYCA0dl8RSUzuVbxE1YG4ecy6vh4nQbTT36CxUxBy+QVdR12pPQluncC0mCOLhI2918Eaw=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.71",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.76",
|
||||
"shell-quote": "^1.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.71",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.76",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.71", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-pIsQJnM7Y+cJHL7aFY6SCCW3FIni218gVEpPqG8XGowfYxboFNBbNssWiUNRwthT8bp9jypcX7q5kx0Xsw14xg=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.76", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-HZxvnT8ZWkzCnQygaYCA0dl8RSUzuVbxE1YG4ecy6vh4nQbTT36CxUxBy+QVdR12pPQluncC0mCOLhI2918Eaw=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
- **Repository Access**: The action can only be triggered by users with write access to the repository
|
||||
- **Bot User Control**: By default, GitHub Apps and bots cannot trigger this action for security reasons. Use the `allowed_bots` parameter to enable specific bots or all bots
|
||||
- **⚠️ Allowed bots are not checked for repository permissions.** A bot that matches an entry does **not** need to be installed on your repository or have write access. On a **public repository**, external parties — including GitHub Apps created by anyone — may be able to trigger workflow events such as opening issues, commenting, or reviewing pull requests. If your workflow listens on those events and `allowed_bots` is set to `'*'`, any such App can invoke this action with a prompt it controls.
|
||||
- Prefer an explicit list over `'*'`
|
||||
- Only list App names you trust
|
||||
- If you need `'*'`, scope workflow `permissions:` to the minimum required
|
||||
- **⚠️ Non-Write User Access (RISKY)**: The `allowed_non_write_users` parameter allows bypassing the write permission requirement. **This is a significant security risk and should only be used for workflows with extremely limited permissions** (e.g., issue labeling workflows that only have `issues: write` permission). This feature:
|
||||
- Only works when `github_token` is provided as input (not with GitHub App authentication)
|
||||
- Accepts either a comma-separated list of specific usernames or `*` to allow all users
|
||||
|
||||
+2
-2
@@ -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:*)`
|
||||
|
||||
|
||||
+3
-2
@@ -53,7 +53,7 @@ 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 | - |
|
||||
@@ -62,6 +62,7 @@ jobs:
|
||||
| `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` |
|
||||
@@ -75,7 +76,7 @@ jobs:
|
||||
| `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 | No | "" |
|
||||
| `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 | "" |
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.71",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.76",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Wrapper around `git push` that only allows `origin <ref>` with no flags.
|
||||
# Defends against --receive-pack / --exec RCE and arbitrary-remote exfiltration
|
||||
# (H1 #3556799). `git push:*` in allowedTools permits `git push --receive-pack='sh -c ...' ext::sh`
|
||||
# which runs arbitrary shell on the Actions runner. This wrapper closes that.
|
||||
#
|
||||
# Usage:
|
||||
# git-push.sh origin HEAD
|
||||
# git-push.sh origin claude/issue-123-20260304
|
||||
|
||||
if [[ $# -ne 2 ]]; then
|
||||
echo "Error: exactly two arguments required: origin <ref>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == -* ]]; then
|
||||
echo "Error: flags are not allowed (got: $arg)" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$1" != "origin" ]]; then
|
||||
echo "Error: remote must be 'origin' (got: $1)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REF="$2"
|
||||
if [[ "$REF" != "HEAD" ]] && ! git check-ref-format --branch "$REF" >/dev/null 2>&1; then
|
||||
echo "Error: invalid ref: $REF" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec git push origin "$REF"
|
||||
+10
-14
@@ -26,19 +26,15 @@ import { extractUserRequest } from "../utils/extract-user-request";
|
||||
import { parseToolsInput } from "../utils/parse-tools-input";
|
||||
export type { CommonFields, PreparedContext } from "./types";
|
||||
|
||||
const GIT_PUSH_WRAPPER = `${process.env.GITHUB_ACTION_PATH}/scripts/git-push.sh`;
|
||||
|
||||
/** Filename for the user request file, read by the SDK runner */
|
||||
const USER_REQUEST_FILENAME = "claude-user-request.txt";
|
||||
|
||||
// Tag mode defaults - these tools are needed for tag mode to function
|
||||
const BASE_ALLOWED_TOOLS = [
|
||||
"Edit",
|
||||
"MultiEdit",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"LS",
|
||||
"Read",
|
||||
"Write",
|
||||
];
|
||||
// Tag mode defaults - these tools are needed for tag mode to function.
|
||||
// Edit/MultiEdit/Write are intentionally omitted: acceptEdits permission mode
|
||||
// auto-allows file edits inside $GITHUB_WORKSPACE and denies writes outside it.
|
||||
const BASE_ALLOWED_TOOLS = ["Glob", "Grep", "LS", "Read"];
|
||||
|
||||
export function buildAllowedToolsString(
|
||||
customAllowedTools?: string[],
|
||||
@@ -62,7 +58,7 @@ export function buildAllowedToolsString(
|
||||
baseTools.push(
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)",
|
||||
`Bash(${GIT_PUSH_WRAPPER}:*)`,
|
||||
"Bash(git status:*)",
|
||||
"Bash(git diff:*)",
|
||||
"Bash(git log:*)",
|
||||
@@ -443,7 +439,7 @@ function getCommitInstructions(
|
||||
Bash(git commit -m "<message>\\n\\n${coAuthorLine}")`
|
||||
: ""
|
||||
}
|
||||
- Push to the remote: Bash(git push origin HEAD)`;
|
||||
- Push to the remote: Bash(${GIT_PUSH_WRAPPER} origin HEAD)`;
|
||||
} else {
|
||||
const branchName = eventData.claudeBranch || eventData.baseBranch;
|
||||
return `
|
||||
@@ -457,7 +453,7 @@ function getCommitInstructions(
|
||||
Bash(git commit -m "<message>\\n\\n${coAuthorLine}")`
|
||||
: ""
|
||||
}
|
||||
- Push to the remote: Bash(git push origin ${branchName})`;
|
||||
- Push to the remote: Bash(${GIT_PUSH_WRAPPER} origin ${branchName})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -840,7 +836,7 @@ ${
|
||||
: `- Use git commands via the Bash tool for version control (remember that you have access to these git commands):
|
||||
- Stage files: Bash(git add <files>)
|
||||
- Commit changes: Bash(git commit -m "<message>")
|
||||
- Push to remote: Bash(git push origin <branch>) (NEVER force push)
|
||||
- Push to remote: Bash(${GIT_PUSH_WRAPPER} origin <branch>)
|
||||
- Delete files: Bash(git rm <files>) followed by commit and push
|
||||
- Check status: Bash(git status)
|
||||
- View diff: Bash(git diff)${eventData.isPR && eventData.baseBranch ? `\n - IMPORTANT: For PR diffs, use: Bash(git diff origin/${eventData.baseBranch}...HEAD)` : ""}`
|
||||
|
||||
@@ -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: "",
|
||||
};
|
||||
|
||||
@@ -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<boolean[] | null> {
|
||||
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<typeof createOctokit>["rest"],
|
||||
owner: string,
|
||||
repo: string,
|
||||
pull_number: number,
|
||||
headSha: string,
|
||||
c: BufferedComment,
|
||||
): Promise<boolean> {
|
||||
const params: Parameters<typeof octokit.rest.pulls.createReviewComment>[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);
|
||||
});
|
||||
@@ -54,7 +54,7 @@ async function installClaudeCode(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const claudeCodeVersion = "2.1.71";
|
||||
const claudeCodeVersion = "2.1.76";
|
||||
console.log(`Installing Claude Code v${claudeCodeVersion}...`);
|
||||
|
||||
// On musl/Alpine systems, the native binary may require a newer musl than
|
||||
|
||||
@@ -115,6 +115,7 @@ type BaseContext = {
|
||||
branchPrefix: string;
|
||||
branchNameTemplate?: string;
|
||||
useStickyComment: boolean;
|
||||
classifyInlineComments: boolean;
|
||||
useCommitSigning: boolean;
|
||||
sshSigningKey: string;
|
||||
botId: string;
|
||||
@@ -178,6 +179,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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -157,6 +157,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",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+11
-8
@@ -128,16 +128,17 @@ export async function prepareTagMode({
|
||||
// Merge user-provided allowed_tools input (fixes upstream #181, #287, #724)
|
||||
const userAllowedTools = parseToolsInput(process.env.ALLOWED_TOOLS_INPUT);
|
||||
|
||||
// Build claude_args for tag mode with required tools
|
||||
// Tag mode REQUIRES these tools to function properly
|
||||
const gitPushWrapper = `${process.env.GITHUB_ACTION_PATH}/scripts/git-push.sh`;
|
||||
|
||||
// Build claude_args for tag mode with required tools.
|
||||
// Edit/MultiEdit/Write are intentionally omitted: acceptEdits permission mode (set below)
|
||||
// auto-allows file edits inside $GITHUB_WORKSPACE and denies writes outside (e.g. ~/.bashrc).
|
||||
// Listing them here would grant blanket write access to the whole runner (Asana 1213310082312048).
|
||||
const tagModeTools = [
|
||||
"Edit",
|
||||
"MultiEdit",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"LS",
|
||||
"Read",
|
||||
"Write",
|
||||
"mcp__github_comment__update_claude_comment",
|
||||
"mcp__github_ci__get_ci_status",
|
||||
"mcp__github_ci__get_workflow_run_details",
|
||||
@@ -152,7 +153,7 @@ export async function prepareTagMode({
|
||||
tagModeTools.push(
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)",
|
||||
`Bash(${gitPushWrapper}:*)`,
|
||||
"Bash(git status:*)",
|
||||
"Bash(git diff:*)",
|
||||
"Bash(git log:*)",
|
||||
@@ -186,8 +187,10 @@ export async function prepareTagMode({
|
||||
const escapedOurConfig = ourMcpConfig.replace(/'/g, "'\\''");
|
||||
claudeArgs = `--mcp-config '${escapedOurConfig}'`;
|
||||
|
||||
// Add required tools for tag mode
|
||||
claudeArgs += ` --allowedTools "${tagModeTools.join(",")}"`;
|
||||
// Add required tools for tag mode.
|
||||
// acceptEdits: file edits auto-allowed inside cwd ($GITHUB_WORKSPACE), denied outside.
|
||||
// Headless SDK has no prompt handler, so anything that falls through to "ask" is denied.
|
||||
claudeArgs += ` --permission-mode acceptEdits --allowedTools "${tagModeTools.join(",")}"`;
|
||||
|
||||
// Append user's claude_args (which may have more --mcp-config flags)
|
||||
if (userClaudeArgs) {
|
||||
|
||||
+23
-18
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { describe, test, expect, beforeAll } from "bun:test";
|
||||
import {
|
||||
generatePrompt,
|
||||
getEventTypeAndContext,
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
} from "../src/create-prompt";
|
||||
import type { PreparedContext } from "../src/create-prompt";
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.GITHUB_ACTION_PATH = "/test/action/path";
|
||||
});
|
||||
|
||||
describe("generatePrompt", () => {
|
||||
const mockGitHubData = {
|
||||
contextData: {
|
||||
@@ -505,7 +509,7 @@ describe("generatePrompt", () => {
|
||||
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
|
||||
|
||||
// Should contain PR-specific instructions (git commands when not using signing)
|
||||
expect(prompt).toContain("git push");
|
||||
expect(prompt).toContain("scripts/git-push.sh origin");
|
||||
expect(prompt).toContain(
|
||||
"Always push to the existing branch when triggered on a PR",
|
||||
);
|
||||
@@ -643,7 +647,7 @@ describe("generatePrompt", () => {
|
||||
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
|
||||
|
||||
// Should contain open PR instructions (git commands when not using signing)
|
||||
expect(prompt).toContain("git push");
|
||||
expect(prompt).toContain("scripts/git-push.sh origin");
|
||||
expect(prompt).toContain(
|
||||
"Always push to the existing branch when triggered on a PR",
|
||||
);
|
||||
@@ -757,7 +761,7 @@ describe("generatePrompt", () => {
|
||||
expect(prompt).toContain("Use git commands via the Bash tool");
|
||||
expect(prompt).toContain("git add");
|
||||
expect(prompt).toContain("git commit");
|
||||
expect(prompt).toContain("git push");
|
||||
expect(prompt).toContain("scripts/git-push.sh origin");
|
||||
|
||||
// Should use the minimal comment tool
|
||||
expect(prompt).toContain("mcp__github_comment__update_claude_comment");
|
||||
@@ -886,17 +890,18 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString();
|
||||
|
||||
// The base tools should be in the result
|
||||
expect(result).toContain("Edit");
|
||||
// Edit/MultiEdit/Write are NOT in allowedTools — acceptEdits permission mode handles them
|
||||
expect(result).not.toContain("Edit");
|
||||
expect(result).not.toContain("Write");
|
||||
expect(result).toContain("Glob");
|
||||
expect(result).toContain("Grep");
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
|
||||
// Default is no commit signing, so should have specific Bash git commands
|
||||
expect(result).toContain("Bash(git add:*)");
|
||||
expect(result).toContain("Bash(git commit:*)");
|
||||
expect(result).toContain("Bash(git push:*)");
|
||||
expect(result).toContain("scripts/git-push.sh:*)");
|
||||
expect(result).toContain("mcp__github_comment__update_claude_comment");
|
||||
|
||||
// Should not have commit signing tools
|
||||
@@ -908,12 +913,12 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString([], false, false);
|
||||
|
||||
// The base tools should be in the result
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).not.toContain("Edit");
|
||||
expect(result).toContain("Glob");
|
||||
expect(result).toContain("Grep");
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
expect(result).not.toContain("Write");
|
||||
|
||||
// Should have specific Bash git commands for non-signing mode
|
||||
expect(result).toContain("Bash(git add:*)");
|
||||
@@ -930,7 +935,7 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString(customTools);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Glob");
|
||||
|
||||
// Custom tools should be appended
|
||||
@@ -950,7 +955,7 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString([], true);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Glob");
|
||||
|
||||
// GitHub Actions tools should be included
|
||||
@@ -964,7 +969,7 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString(customTools, true);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).toContain("Read");
|
||||
|
||||
// Custom tools should be included
|
||||
expect(result).toContain("Tool1");
|
||||
@@ -980,12 +985,12 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString([], false, true);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).not.toContain("Edit");
|
||||
expect(result).toContain("Glob");
|
||||
expect(result).toContain("Grep");
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
expect(result).not.toContain("Write");
|
||||
|
||||
// Commit signing tools should be included
|
||||
expect(result).toContain("mcp__github_file_ops__commit_files");
|
||||
@@ -1001,17 +1006,17 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString([], false, false);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).not.toContain("Edit");
|
||||
expect(result).toContain("Glob");
|
||||
expect(result).toContain("Grep");
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
expect(result).not.toContain("Write");
|
||||
|
||||
// Specific Bash git commands should be included
|
||||
expect(result).toContain("Bash(git add:*)");
|
||||
expect(result).toContain("Bash(git commit:*)");
|
||||
expect(result).toContain("Bash(git push:*)");
|
||||
expect(result).toContain("scripts/git-push.sh:*)");
|
||||
expect(result).toContain("Bash(git status:*)");
|
||||
expect(result).toContain("Bash(git diff:*)");
|
||||
expect(result).toContain("Bash(git log:*)");
|
||||
@@ -1030,7 +1035,7 @@ describe("buildAllowedToolsString", () => {
|
||||
const result = buildAllowedToolsString(customTools, true, false);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Bash(git add:*)");
|
||||
|
||||
// Custom tools should be included
|
||||
|
||||
@@ -33,6 +33,7 @@ describe("prepareMcpConfig", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
sshSigningKey: "",
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
|
||||
@@ -19,6 +19,7 @@ const defaultInputs = {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
sshSigningKey: "",
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
|
||||
@@ -19,6 +19,7 @@ describe("detectMode with enhanced routing", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
sshSigningKey: "",
|
||||
botId: "123456",
|
||||
|
||||
@@ -68,6 +68,7 @@ describe("checkWritePermissions", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
sshSigningKey: "",
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
|
||||
@@ -135,7 +135,7 @@ describe("pull_request_target event support", () => {
|
||||
const prompt = generatePrompt(envVars, mockGitHubData, false, "tag");
|
||||
|
||||
// Should include git commands for non-commit-signing mode
|
||||
expect(prompt).toContain("git push");
|
||||
expect(prompt).toContain("scripts/git-push.sh origin");
|
||||
expect(prompt).toContain(
|
||||
"Always push to the existing branch when triggered on a PR",
|
||||
);
|
||||
|
||||
@@ -35,6 +35,7 @@ describe("checkContainsTrigger", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
allowedBots: "",
|
||||
},
|
||||
@@ -63,6 +64,7 @@ describe("checkContainsTrigger", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
allowedBots: "",
|
||||
},
|
||||
@@ -275,6 +277,7 @@ describe("checkContainsTrigger", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
allowedBots: "",
|
||||
},
|
||||
@@ -304,6 +307,7 @@ describe("checkContainsTrigger", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
allowedBots: "",
|
||||
},
|
||||
@@ -333,6 +337,7 @@ describe("checkContainsTrigger", () => {
|
||||
labelTrigger: "",
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
classifyInlineComments: true,
|
||||
useCommitSigning: false,
|
||||
allowedBots: "",
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user