When an issue is created with the trigger label already applied,
Forgejo only fires issues/opened (not a separate labeled event).
Check payload.issue.labels for the trigger label during opened
events so creating an issue with the claude label works in one step.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Debug logging confirmed Forgejo sends 'label_updated' as eventAction
for issues/labeled events. Normalization fix in context.ts handles it.
Remove temporary debug lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo sends eventAction 'label_updated' instead of 'labeled' for
issue label events. Add FORGEJO_EVENT_ACTION_MAP in context.ts to
normalize at parse time (same pattern as event name normalization),
so all downstream code (trigger.ts, create-prompt) works unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo sends eventAction 'label_updated' instead of 'labeled'
for issue label events (discovered via debug logging, run 52786).
Accept both values in trigger.ts label check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporary debug logging to inspect eventAction and payload.label
on Forgejo for issues/labeled events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the _fsForTest/DI patterns with Bun's native mock.module() API.
This mocks fs/promises at the module loader level, which works reliably
on Linux where jest.fn() call tracking fails cross-module. The source
code is now clean with no testing artifacts.
Also switches from jest.fn() to mock() (Bun's native API) for Octokit
mocks in these tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The _fsForTest mutable object pattern fails on Linux Bun because
test and source files get different module instances. Switch to
passing fs operations as an optional parameter with defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Forgejo, pull_request_review_comment events create the tracking
comment as a regular issue comment (because the payload lacks
comment.id for reply-to-review). The update-comment-link step then
tried pulls.getReviewComment() first, which 404'd and threw before
reaching the issues.getComment() fallback.
Wrap the PR review comment fetch in its own try/catch so it falls
through to the issue comment fetch on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo's pull_request_review_comment payload doesn't include the
comment body. The Forgejo data fetcher retrieves it via REST instead.
Skip the COMMENT_BODY required check on Forgejo to avoid crashing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo's pull_request_review_comment event does NOT include a top-level
`comment` object in the webhook payload. This caused crashes at:
- create-initial.ts: context.payload.comment.id (for reply-to-review)
- fetcher.ts: context.payload.comment.created_at (for trigger timestamp)
Fix: use optional chaining and fall back gracefully — reply-to-review
becomes a regular issue comment, trigger timestamp returns undefined.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo's pull_request_review_comment event does not include a
top-level `comment` object in the payload (it's undefined), causing
a crash: "undefined is not an object (evaluating 'context.payload.comment.body')".
- trigger.ts: On Forgejo, always trigger for pull_request_review_comment
events since the workflow already gates on event name alone
- create-prompt/index.ts: Use optional chaining for comment fields,
fall back to actor/empty values (data fetcher provides full data)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo uses the per-repo sequential run index (GITHUB_RUN_NUMBER) in
action URLs, not the internal DB run ID (GITHUB_RUN_ID). This caused
"View job" links to return 404. Fixed in all 4 URL construction sites:
create-initial comment, update-comment-link, and both prompt generators.
Also pins Bun to 1.2.21 in CI — Bun 1.3.9 broke spyOn(fs, ...) which
failed 18 upstream image-downloader tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add bot_email action input for custom git commit email on Forgejo
- Update claude.yml to use CLAUDE_BOT_TOKEN with bot_id/bot_name/bot_email
- Update git-config.ts to use provided email over noreply format
- Add "Dedicated Bot User" setup guide to docs/forgejo.md
- Update CLAUDE.md with bot identity documentation
Infrastructure (done outside code):
- claude-bot user created (ID 48, non-admin, with avatar)
- Claude team with write access in all 12 orgs
- CLAUDE_BOT_TOKEN org-level secret in all 12 orgs
Progress: ~85% complete - pending E2E validation after merge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo doesn't have the flat GET /pulls/{number}/comments endpoint
(returns 404). Replace pulls.listReviewComments() with a 2-phase
approach: fetch reviews first, then pulls.listCommentsForReview()
per-review in parallel. Updates tests to mock the new per-review API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo uses /git/refs (plural) not /git/ref (singular), causing
git.getRef() to 404. Use repos.getBranch() on Forgejo instead,
which returns the same SHA data via a compatible endpoint.
Also adds the tag mode workflow (.forgejo/workflows/claude.yml)
for @claude mentions on issues, PR reviews, and label triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forgejo's user API doesn't return a `type` field (GitHub returns
"User" or "Bot"). The undefined type was incorrectly treated as
non-human, blocking all Forgejo workflows. Now treat undefined/empty
type as human — Forgejo doesn't have GitHub-style bot accounts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two issues found during M8 E2E testing on Forgejo:
1. CI test failures (31 tests): Unit tests assume GitHub context but
Forgejo runner sets GITHUB_SERVER_URL=git.wxs.ro, causing
detectPlatform() to return "forgejo" and module-level constants in
config.ts to use Forgejo URLs. Fixed by setting GITHUB_SERVER_URL
in the test step env. Forgejo-specific tests already override with
PLATFORM=forgejo in their beforeEach.
2. Permission check 403: Forgejo's getCollaboratorPermissionLevel
requires admin access. Added checkForgejoPermissions() that uses
GET /repos/{owner}/{repo} instead — returns a permissions object
with { admin, push, pull } booleans for the authenticated token.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add platform detection to prepareMcpConfig() via detectPlatform()
- Skip GitHub public MCP server (ghcr.io/github/github-mcp-server) on Forgejo
- Skip checkActionsReadPermission on Forgejo (workflow token has full access)
- Reuse existing github-actions-server.ts for both platforms (no duplication)
- Add 6 Forgejo-specific tests to install-mcp-server.test.ts
- Updated PRD checkboxes for M6
Progress: 67% complete (6/9 milestones) - M7 now unblocked
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 3 REST calls to Promise.all for PRs (listComments, listReviews,
listReviewComments) and 1 for issues (listComments)
- Add mapping helpers: mapRestComments, mapRestReviewComments,
mapRestReviews (groups inline comments by pull_request_review_id)
- Import and apply TOCTOU filtering (filterCommentsToTriggerTime,
filterReviewsToTriggerTime) and actor filtering from GitHub fetcher
- Add image processing pipeline via downloadCommentImages (no-op on
Forgejo due to URL pattern mismatch, architecturally correct)
- Deep-clone reviews before filtering to preserve contextData immutability
- Update test suite: 51 tests covering mapping helpers, full fetch
flow, TOCTOU filtering, actor filtering, and contextData immutability
- Updated PRD checkboxes for M4b completion
Progress: 44% complete (4/9 milestones) - next: M5 or M3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add src/platform/detector.ts with 3-tier detection (explicit input,
FORGEJO env var, GITHUB_SERVER_URL heuristic)
- Add 'platform' input to action.yml (auto/github/forgejo)
- Add 'Detect Platform' shell step as first composite action step
- Split bun installation: oven-sh/setup-bun for GitHub, curl fallback
for Forgejo runners
- Condition 'Revoke app token' step to skip on Forgejo
- Pass resolved PLATFORM env var to run step
- Add 14 unit tests covering all detection paths
Progress: M1 complete (1/9 milestones) - M2 and M5 now unblocked
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a `display_report` input parameter (default: "true") that controls
whether the Claude Code Report is written to the GitHub Step Summary.
Setting it to "false" allows users with custom formatting solutions to
avoid duplicate output in the step summary.
Closes#206
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
Reverts the following commits:
- f669191 fix: use unique local branch names for PR checkout to avoid conflicts (#931)
- 21e3fe0 Fix PR checkout to support fork PRs (#851)
Simplifies PR branch checkout back to using headRefName directly instead
of the pr-{number} local branch naming scheme introduced in #931 and the
GitHub pull ref fetch approach introduced in #851.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The previous implementation used the PR's original branch name when
fetching, which could conflict with existing local or remote branches
of the same name. This caused checkout failures for PRs with common
branch names like 'main' or 'feature/xyz'.
Changes:
- Use 'pr-{number}' format for local branch names (e.g., pr-385)
- Preserve original branch name for logging purposes
- Add detailed logging showing original -> local branch mapping
This ensures uniqueness since PR numbers are unique per repository,
while maintaining support for both same-repo and fork PRs via
GitHub's pull/{number}/head refs.
Fixes the issue introduced in #851 where fork PR support was added.
Co-authored-by: Yi-Cheng Wang <yicheng.wang@heph-ai.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace `Bash(git add:*)` syntax with `Bash(git add *)` in default
tool permissions for tag mode and create-prompt. The colon-prefixed
wildcard syntax is deprecated and causes SDK validation errors.
Closes#856
Co-authored-by: Dave-London <hello@os4us.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Use GitHub's PR refs (pull/NUMBER/head) instead of fetching branch
by name. This works for both same-repo and fork PRs because GitHub
automatically creates these refs in the base repository for all PRs.
The branch name doesn't exist on origin for fork PRs, causing:
fatal: couldn't find remote ref <branch-name>
Using pull/${entityNumber}/head:${branchName} fetches the PR head
and creates a local branch with the correct name.
Fixes issues with tag mode failing on fork PRs.
Replace the over-engineered Mode interface/registry/detector pattern with
straightforward inline logic. There are only 2 modes (tag and agent) and
the complexity wasn't justified.
- Delete Mode interface, registry, and prepare pass-through modules
- Export prepareTagMode() and prepareAgentMode() as standalone functions
- Inline trigger checking and mode dispatch in run.ts/prepare.ts
- Change generatePrompt/createPrompt to take modeName string instead of Mode
- Remove dead code (extractGitHubContext, unused detector helpers)
- Update CLAUDE.md to reflect new architecture
* refactor: unify action into single composite step with run.ts entrypoint
Consolidate the prepare and base-action phases into a single composite
step that runs src/entrypoints/run.ts. This simplifies the action.yml
from multiple steps to one execution step, while keeping the same
behavior.
Key changes:
- Add src/entrypoints/run.ts as unified entrypoint
- Simplify action.yml to single 'Run Claude Code Action' step
- Pass all inputs via environment variables
- Update base-action to accept inputs via env vars
- Support agent mode auto-detection from prompt input
* refactor: keep SSH signing cleanup and token revocation as separate action steps
Move SSH signing key cleanup and app token revocation back to separate
composite action steps in action.yml with always() conditions, rather
than handling them inside run.ts. This keeps these cleanup concerns
as independently visible steps in the workflow.
* fix: address PR review feedback
- Use path.dirname() instead of manual string slicing for executable path
- Differentiate prepare vs execution errors in catch block so tracking
comment accurately reflects which phase failed
- Update CLAUDE.md architecture docs to reflect unified run.ts entrypoint
and four-phase design
* fix: address PR review feedback
- Use path.dirname() instead of manual string slicing for executable path
- Differentiate prepare vs execution errors in catch block so tracking
comment accurately reflects which phase failed
- Rewrite CLAUDE.md to focus on mental model, key concepts, and gotchas
instead of exhaustive file listings
- Introduced `include_comments_by_actor` and `exclude_comments_by_actor` inputs in action.yml to allow filtering of comments based on actor usernames.
- Updated context parsing to handle new input fields.
- Implemented `filterCommentsByActor` function to filter comments according to specified inclusion and exclusion patterns.
- Modified `fetchGitHubData` to apply actor filters when retrieving comments from pull requests and issues.
- Added comprehensive tests for the new filtering functionality.
This enhancement provides more control over which comments are processed based on the actor, improving the flexibility of the workflow.