# CLAUDE.md ## Public Repository This repo is open-source and synced to Codeberg. When making changes: - **Never commit sensitive data** — no internal URLs, tokens, credentials, private infrastructure details, or instance-specific configuration in `src/`, `action.yml`, `base-action/`, or `docs/`. The `.forgejo/workflows/` directory is the only place with instance-specific config (runner labels, bot IDs, internal URLs) and that's fine — users write their own workflows. - **Keep Forgejo support generic** — all Forgejo code must work for any Forgejo instance, not just ours. Don't hardcode assumptions about our setup. Use action inputs and environment variables for instance-specific values. - **Use generic URLs in tests** — test fixtures use `forgejo.example.com` as the sample Forgejo URL. Never use real instance URLs in `src/` or `test/` code. ## Commands ```bash bun test # Run tests bun run typecheck # TypeScript type checking bun run format # Format with prettier bun run format:check # Check formatting ``` ## What This Is A GitHub Action that lets Claude respond to `@claude` mentions on issues/PRs (tag mode) or run tasks via `prompt` input (agent mode). Mode is auto-detected: if `prompt` is provided, it's agent mode; if triggered by a comment/issue event with `@claude`, it's tag mode. See `src/modes/detector.ts`. ## How It Runs Single entrypoint: `src/entrypoints/run.ts` orchestrates everything — prepare (auth, permissions, trigger check, branch/comment creation), install Claude Code CLI, execute Claude via `base-action/` functions (imported directly, not subprocess), then cleanup (update tracking comment, write step summary). SSH signing cleanup and token revocation are separate `always()` steps in `action.yml`. `base-action/` is also published standalone as `@anthropic-ai/claude-code-base-action`. Don't break its public API. It reads config from `INPUT_`-prefixed env vars (set by `action.yml`), not from action inputs directly. ## Key Concepts **Auth priority**: `github_token` input (user-provided) > GitHub App OIDC token (default). The `claude_code_oauth_token` and `anthropic_api_key` are for the Claude API, not GitHub. Token setup lives in `src/github/token.ts`. **Mode lifecycle**: `detectMode()` in `src/modes/detector.ts` picks the mode name ("tag" or "agent"). Trigger checking and prepare dispatch are inlined in `run.ts`: tag mode calls `prepareTagMode()` from `src/modes/tag/`, agent mode calls `prepareAgentMode()` from `src/modes/agent/`. **Prompt construction**: Tag mode's `prepareTagMode()` builds the prompt by fetching GitHub data (`src/github/data/fetcher.ts`), formatting it as markdown (`src/github/data/formatter.ts`), and writing it to a temp file via `createPrompt()`. Agent mode writes the user's prompt directly. The prompt includes issue/PR body, comments, diff, and CI status. This is the most important part of the action — it's what Claude sees. ## Things That Will Bite You - **Strict TypeScript**: `noUnusedLocals` and `noUnusedParameters` are enabled. Typecheck will fail on unused variables. - **Discriminated unions for GitHub context**: `GitHubContext` is a union type — call `isEntityContext(context)` before accessing entity-specific fields like `context.issue` or `context.pullRequest`. - **Token lifecycle matters**: The GitHub App token is obtained early and revoked in a separate `always()` step in `action.yml`. If you move token revocation into `run.ts`, it won't run if the process crashes. Same for SSH signing cleanup. - **Error phase attribution**: The catch block in `run.ts` uses `prepareCompleted` to distinguish prepare failures from execution failures. The tracking comment shows different messages for each. - **`action.yml` outputs reference step IDs**: Outputs like `execution_file`, `branch_name`, `github_token` reference `steps.run.outputs.*`. If you rename the step ID, update the outputs section too. - **Integration testing** happens in a separate repo (`install-test`), not here. The tests in this repo are unit tests. - **`allowed_tools` needs Bash sub-patterns**: Bare `Bash` in `allowed_tools` makes the tool available but individual commands still require interactive approval (which can't happen in CI). Use sub-patterns like `Bash(git:*)`, `Bash(bun:*)`, `Bash(curl:*)` to pre-approve command families. Tag mode adds `--allowedTools` to `claudeArgs` itself; agent mode relies on `run.ts` passing `ALLOWED_TOOLS_INPUT` as `options.allowedTools` to the SDK. ## Forgejo Platform Support This fork adds Forgejo support via a lean adapter pattern — only 4 genuinely different areas have Forgejo-specific code: - **`src/platform/detector.ts`** — Three-tier platform detection (explicit input > `FORGEJO` env > `GITHUB_SERVER_URL` heuristic) with cross-validation that rejects Forgejo override on github.com - **`src/forgejo/token.ts`** — Direct token handling (skips OIDC exchange) - **`src/forgejo/data/fetcher.ts`** — REST-based data fetching (replaces 3 GraphQL queries with 5-8 parallel REST calls) - **`src/forgejo/comment-logic.ts`** — Forgejo URL patterns for tracking comments All other `src/github/` modules work on Forgejo via Octokit REST as-is (permissions, comments, branches, git config, context, formatter). **Bot identity on Forgejo**: A dedicated `claude` user provides bot identity (like GitHub's `claude[bot]`). The `bot_id`, `bot_name`, and `bot_email` action inputs control git commit attribution. When `bot_email` is set, it overrides the default noreply email format — set it to the bot user's registered email so Forgejo links commits to the profile. See `docs/forgejo.md` for setup. **Key integration points** (these files have platform branches and are merge-conflict surface): - `src/entrypoints/run.ts` — Platform branch at top for token setup - `src/modes/tag/index.ts` — Data fetcher swap (`fetchForgejoData` vs `fetchGitHubData`) - `src/github/operations/comments/create-initial.ts` — Platform-aware spinner/URL format - `src/github/operations/comments/update-with-branch.ts` — Forgejo branch link in tracking comment - `src/github/operations/branch.ts` — Uses `repos.getBranch()` on Forgejo (Forgejo has `/git/refs` not `/git/ref`) - `src/github/validation/actor.ts` — Bot-type check skipped on Forgejo; self-trigger guard only blocks comment/review events (`issue_comment`, `issues`, `pull_request_review`, `pull_request_review_comment`). PR, workflow_dispatch, and schedule events pass through even when actor matches bot_name. - `src/entrypoints/update-comment-link.ts` — Forgejo PR compare URL (`expand=1` instead of `quick_pull=1`) - `src/mcp/install-mcp-server.ts` — Skips GitHub public MCP server on Forgejo **MCP server names stay the same** (`github_comment`, `github_ci`, etc.) even on Forgejo — changing them would require updating hardcoded tool name lists in `src/modes/tag/index.ts`. **CI**: `.forgejo/workflows/` contains `claude.yml` (tag mode), `ci.yml`, `test-action.yml`, and `upstream-sync.yml`. Unit tests run with `GITHUB_SERVER_URL=https://github.com` override on Forgejo runners. ## Code Conventions - Runtime is Bun, not Node. Use `bun test`, not `jest`. - `moduleResolution: "bundler"` — imports don't need `.js` extensions. - GitHub API calls should use retry logic (`src/utils/retry.ts`). - MCP servers are auto-installed at runtime to `~/.claude/mcp/github-{type}-server/`.