Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ inputs:
description: "Comma-separated list of usernames to allow without write permissions, or '*' to allow all users. Only works when github_token input is provided. WARNING: Use with extreme caution - this bypasses security checks and should only be used for workflows with very limited permissions (e.g., issue labeling)."
required: false
default: ""
excluded_comment_users:
description: "Comma-separated list of usernames whose comments should be excluded from the prompt context. Useful for filtering out bot comments or previous Claude responses."
required: false
default: ""

# Claude Code configuration
prompt:
Expand Down Expand Up @@ -165,6 +169,7 @@ runs:
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
ALLOWED_BOTS: ${{ inputs.allowed_bots }}
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
EXCLUDED_COMMENT_USERS: ${{ inputs.excluded_comment_users }}
GITHUB_RUN_ID: ${{ github.run_id }}
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
Expand Down
3 changes: 3 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ jobs:
# actions: read
# Optional: allow bot users to trigger the action
# allowed_bots: "dependabot[bot],renovate[bot]"
# Optional: exclude specific users' comments from context (e.g., bots)
# excluded_comment_users: "github-actions[bot],claude[bot]"
```

## Inputs
Expand Down Expand Up @@ -76,6 +78,7 @@ jobs:
| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name) | 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_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 | "" |
| `excluded_comment_users` | Comma-separated list of usernames whose comments should be excluded from the prompt context. Useful for filtering out bot comments or previous Claude responses. Only applies in agent mode. | 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 | "" |
Expand Down
14 changes: 13 additions & 1 deletion src/create-prompt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,20 @@ export function generateDefaultPrompt(

const { eventType, triggerContext } = getEventTypeAndContext(context);

// Parse excluded comment users from context
const excludedUsers = context.githubContext?.inputs?.excludedCommentUsers
? context.githubContext.inputs.excludedCommentUsers
.split(",")
.map((u) => u.trim().toLowerCase())
.filter((u) => u.length > 0)
: undefined;

const formattedContext = formatContext(contextData, eventData.isPR);
const formattedComments = formatComments(comments, imageUrlMap);
const formattedComments = formatComments(
comments,
imageUrlMap,
excludedUsers,
);
const formattedReviewComments = eventData.isPR
? formatReviewComments(reviewData, imageUrlMap)
: "";
Expand Down
1 change: 1 addition & 0 deletions src/entrypoints/collect-inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function collectActionInputsPresence(): void {
base_branch: "",
branch_prefix: "claude/",
allowed_bots: "",
excluded_comment_users: "",
mode: "tag",
model: "",
anthropic_model: "",
Expand Down
2 changes: 2 additions & 0 deletions src/github/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type BaseContext = {
botName: string;
allowedBots: string;
allowedNonWriteUsers: string;
excludedCommentUsers: string;
trackProgress: boolean;
};
};
Expand Down Expand Up @@ -149,6 +150,7 @@ export function parseGitHubContext(): GitHubContext {
botName: process.env.BOT_NAME ?? CLAUDE_BOT_LOGIN,
allowedBots: process.env.ALLOWED_BOTS ?? "",
allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "",
excludedCommentUsers: process.env.EXCLUDED_COMMENT_USERS ?? "",
trackProgress: process.env.TRACK_PROGRESS === "true",
},
};
Expand Down
15 changes: 14 additions & 1 deletion src/github/data/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,22 @@ export function formatBody(
export function formatComments(
comments: GitHubComment[],
imageUrlMap?: Map<string, string>,
excludedCommentUsers?: string[],
): string {
return comments
.filter((comment) => !comment.isMinimized)
.filter((comment) => {
// Filter out minimized comments
if (comment.isMinimized) return false;

// Filter out excluded users if specified
if (excludedCommentUsers && excludedCommentUsers.length > 0) {
return !excludedCommentUsers.includes(
comment.author.login.toLowerCase(),
);
}

return true;
})
.map((comment) => {
let body = comment.body;

Expand Down
87 changes: 87 additions & 0 deletions test/data-formatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,93 @@ describe("formatComments", () => {
const result = formatComments(comments);
expect(result).toBe("");
});

test("filters out comments from excluded users", () => {
const comments: GitHubComment[] = [
{
id: "1",
databaseId: "100001",
body: "Comment from user1",
author: { login: "user1" },
createdAt: "2023-01-01T00:00:00Z",
},
{
id: "2",
databaseId: "100002",
body: "Comment from bot",
author: { login: "github-actions[bot]" },
createdAt: "2023-01-02T00:00:00Z",
},
{
id: "3",
databaseId: "100003",
body: "Comment from user2",
author: { login: "user2" },
createdAt: "2023-01-03T00:00:00Z",
},
{
id: "4",
databaseId: "100004",
body: "Comment from claude",
author: { login: "claude[bot]" },
createdAt: "2023-01-04T00:00:00Z",
},
];

const excludedUsers = ["github-actions[bot]", "claude[bot]"];
const result = formatComments(comments, undefined, excludedUsers);
expect(result).toBe(
`[user1 at 2023-01-01T00:00:00Z]: Comment from user1\n\n[user2 at 2023-01-03T00:00:00Z]: Comment from user2`,
);
});

test("includes all comments when excluded users array is empty", () => {
const comments: GitHubComment[] = [
{
id: "1",
databaseId: "100001",
body: "First comment",
author: { login: "user1" },
createdAt: "2023-01-01T00:00:00Z",
},
{
id: "2",
databaseId: "100002",
body: "Second comment",
author: { login: "user2" },
createdAt: "2023-01-02T00:00:00Z",
},
];

const result = formatComments(comments, undefined, []);
expect(result).toBe(
`[user1 at 2023-01-01T00:00:00Z]: First comment\n\n[user2 at 2023-01-02T00:00:00Z]: Second comment`,
);
});

test("includes all comments when excluded users is undefined", () => {
const comments: GitHubComment[] = [
{
id: "1",
databaseId: "100001",
body: "First comment",
author: { login: "user1" },
createdAt: "2023-01-01T00:00:00Z",
},
{
id: "2",
databaseId: "100002",
body: "Second comment",
author: { login: "user2" },
createdAt: "2023-01-02T00:00:00Z",
},
];

const result = formatComments(comments, undefined, []);
expect(result).toBe(
`[user1 at 2023-01-01T00:00:00Z]: First comment\n\n[user2 at 2023-01-02T00:00:00Z]: Second comment`,
);
});
});

describe("formatReviewComments", () => {
Expand Down
1 change: 1 addition & 0 deletions test/install-mcp-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe("prepareMcpConfig", () => {
botName: CLAUDE_BOT_LOGIN,
allowedBots: "",
allowedNonWriteUsers: "",
excludedCommentUsers: "",
trackProgress: false,
},
};
Expand Down
1 change: 1 addition & 0 deletions test/mockContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const defaultInputs = {
botName: CLAUDE_BOT_LOGIN,
allowedBots: "",
allowedNonWriteUsers: "",
excludedCommentUsers: "",
trackProgress: false,
};

Expand Down
1 change: 1 addition & 0 deletions test/modes/detector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe("detectMode with enhanced routing", () => {
botName: "claude-bot",
allowedBots: "",
allowedNonWriteUsers: "",
excludedCommentUsers: "",
trackProgress: false,
},
};
Expand Down
1 change: 1 addition & 0 deletions test/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ describe("checkWritePermissions", () => {
botName: CLAUDE_BOT_LOGIN,
allowedBots: "",
allowedNonWriteUsers: "",
excludedCommentUsers: "",
trackProgress: false,
},
});
Expand Down