Commit Graph
214 Commits
Author SHA1 Message Date
claudeandvtmocanu c80d3be21a fix: handle malformed JSONL lines gracefully in post-buffered-inline-comments
Wrap per-line JSON.parse in try-catch so a single truncated/malformed
entry (e.g. from an OOM mid-write) skips with a warning instead of
aborting the entire batch and dropping all comments.

Co-authored-by: vtmocanu <vtmocanu@users.noreply.github.com>
2026-03-16 08:32:04 +00:00
claude[bot]andClaude Sonnet 4.6 ec394266fc 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>
2026-03-16 08:22:39 +00:00
GitHub Actions cd77b50d2b chore: bump Claude Code to 2.1.76 and Agent SDK to 0.2.76 2026-03-14 01:29:31 +00:00
GitHub Actions 0e80d3c5b8 chore: bump Claude Code to 2.1.75 and Agent SDK to 0.2.75 2026-03-13 17:07:33 +00:00
kashyap murali f956510b1a Harden tag mode tool permissions against prompt injection (#1002)
Two defenses for tag mode where an attacker with repo write access could
craft a prompt injection payload in an issue/PR to gain RCE on the
Actions runner:

1. git-push wrapper (H1 #3556799)
   The Bash(git\ push:*) rule permitted arbitrary flags and remotes,
   including combinations that execute shell commands locally. Replaced
   with scripts/git-push.sh which allowlists exactly 'origin <ref>' with
   no flags, validates the ref via check-ref-format. Same pattern as
   scripts/gh.sh.

2. acceptEdits instead of blanket Write/Edit (Asana 1213310082312048)
   Edit/MultiEdit/Write in allowedTools granted write access to the
   whole runner filesystem (~/.bashrc etc). Removed from allowedTools
   and set --permission-mode acceptEdits, which auto-accepts edits
   inside cwd ($GITHUB_WORKSPACE) and denies outside. Headless SDK has
   no prompt handler so 'ask' becomes deny.

Also:
- Noted that create-prompt/index.ts exports ALLOWED_TOOLS env var that
  nothing reads. The live path is modes/tag/index.ts. Mirrored the fix
  in both so the file the H1 report likely points to stays in sync.
- Updated prompt text (3 callsites) to reference the wrapper.
- Updated tests (4 prompt-content asserts, 7 tool-list asserts).
2026-03-12 13:35:17 -07:00
kashyap murali 5d0cc745cd feat(inline-comment): add confirmed param + probe-pattern safety net (#1048)
* feat(inline-comment): add confirmed param + probe-pattern safety net

Subagents that inherit this tool sometimes probe it with test comments
('Test comment to see if I can create inline comments') after hitting
unrelated errors elsewhere. Recurring issue across customer PRs.

Adds two defenses:
1. confirmed param: set true to post (final review comments should pass
   this). When false, buffers to a JSONL file instead of posting.
2. Probe-pattern safety net: when confirmed is omitted (backward compat
   for existing prompts), the body is checked against obvious probe
   patterns ('test comment', 'can i', 'does this work', etc.). Matching
   calls are buffered instead of posted.

A post-run step in action.yml reports the buffered call count and bodies
as a workflow warning for diagnostics.

Backward compatibility:
- Existing single-agent prompts (no confirmed param) post normally unless
  the body happens to start with a probe phrase (unlikely for real
  review comments)
- The code-review skill is being updated to pass confirmed: true in its
  final posting step
- Subagent probes that would previously post now harmlessly buffer

* refactor: replace probe-regex with Haiku classification in post-step

The regex approach was narrow and could miss creative probe phrasings.
Replaced with a batch Haiku classification that runs after the session
completes.

Flow:
- MCP server: confirmed !== true -> buffer to JSONL (no classification
  in-band, no latency in the tool path)
- Post-step (src/entrypoints/post-buffered-inline-comments.ts): reads
  buffer, sends all bodies to a single Haiku call, posts only those
  classified as real review comments
- confirmed=false entries are never posted regardless of classification

Fail-open: if ANTHROPIC_API_KEY is unavailable (Bedrock/Vertex users)
or the classification call fails, posts all unconfirmed comments. This
matches pre-PR behavior where all calls posted immediately.

The post-step emits ::warning:: for each filtered comment so users can
see what was dropped and why.

* feat: add classify_inline_comments opt-out input

New action input classify_inline_comments (default 'true'). Setting to
'false' restores pre-buffering behavior: all inline comment calls post
immediately regardless of the confirmed param.

Threads through: action input -> CLASSIFY_INLINE_COMMENTS env ->
context.inputs.classifyInlineComments -> MCP server env ->
CLASSIFY_ENABLED module const.

Post-step is also gated on the input so it skips entirely when
classification is disabled.

* docs: document classify_inline_comments input and confirmed param

- usage.md: add classify_inline_comments to inputs table
- solutions.md: mention confirmed=true in the prompt example and explain
  buffering/classification in the tool permissions section
2026-03-12 00:12:55 -07:00
GitHub Actions 567be3da98 chore: bump Claude Code to 2.1.73 and Agent SDK to 0.2.73 2026-03-11 18:33:26 +00:00
GitHub Actions eb99fb38f0 chore: bump Claude Code to 2.1.72 and Agent SDK to 0.2.72 2026-03-10 00:49:35 +00:00
claude 12e1f33ef5 Merge remote-tracking branch 'upstream/main' into chore/upstream-sync-20260309 2026-03-09 08:00:20 +00:00
GitHub Actions 3428ca8991 chore: bump Claude Code to 2.1.71 and Agent SDK to 0.2.71 2026-03-07 00:11:30 +00:00
claude 4885138cff Merge remote-tracking branch 'upstream/main' into chore/upstream-sync-20260306 2026-03-06 20:31:30 +00:00
GitHub Actions 26ec041249 chore: bump Claude Code to 2.1.70 and Agent SDK to 0.2.70 2026-03-06 01:18:43 +00:00
claude 62f23790cb Merge remote-tracking branch 'upstream/main' into chore/upstream-sync-20260305 2026-03-05 12:30:25 +00:00
GitHub Actions 1fc90f3ed9 chore: bump Claude Code to 2.1.69 and Agent SDK to 0.2.69 2026-03-05 00:24:53 +00:00
GitHub Actions e763fe78de chore: bump Claude Code to 2.1.68 and Agent SDK to 0.2.68 2026-03-04 10:09:58 +00:00
GitHub Actions 5f8e5bfe5b chore: bump Claude Code to 2.1.66 and Agent SDK to 0.2.66 2026-03-04 01:17:58 +00:00
claude c160cc3ded Merge remote-tracking branch 'upstream/main' into chore/upstream-sync-20260228 2026-02-28 07:09:23 +00:00
Vlad Mocanu b8cb56d204 fix: use npm install for Claude Code CLI on musl/Alpine systems
The native binary (install.sh) requires posix_getdents which is only
available in Alpine edge (musl 1.2.5-r21+), not in any stable Alpine
release. Detect musl upfront via ldd and skip straight to npm install,
avoiding 15+ seconds of wasted retries on deterministic failures.

On glibc systems the native install path is unchanged with npm as a
fallback if it fails.
2026-02-28 09:08:26 +02:00
GitHub Actions ba7fa4bcf0 chore: bump Claude Code to 2.1.63 and Agent SDK to 0.2.63 2026-02-28 03:51:01 +00:00
claude b5cd1559f4 Merge remote-tracking branch 'upstream/main' into chore/upstream-sync-20260227 2026-02-27 11:58:44 +00:00
Vlad MocanuandClaude Opus 4.6 c97c9765c3 fix: allow Forgejo bot actor on non-comment events
The self-trigger guard blocked the claude bot on all events. Loop risk
only exists for comment/review events (tag mode). PR, workflow_dispatch,
and schedule events are safe — refined the guard to only block on
issue_comment, issues, pull_request_review, and
pull_request_review_comment events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:57:59 +02:00
Vlad MocanuandClaude Opus 4.6 2978878d82 fix: pass allowed_tools to SDK in agent mode
Agent mode never passed ALLOWED_TOOLS_INPUT to the Claude SDK —
only tag mode did (via --allowedTools in claudeArgs). This meant
the allowed_tools action input was silently ignored in agent mode,
causing all Bash commands to require interactive approval in CI.

Pass ALLOWED_TOOLS_INPUT directly as options.allowedTools, which
the SDK merges with any tools from claudeArgs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:58:09 +02:00
Vlad MocanuandClaude Opus 4.6 ed536bb8a3 feat: add human-readable CI log output for show_full_output mode
Replace raw JSON dumps with concise, scannable log lines when
show_full_output is enabled. Adds an onMessage callback to the SDK
runner that formats each message type into 1-2 line summaries with
emoji prefixes, tool-specific parameter display, result correlation
for parallel tool calls, and subagent progress tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:50:49 +02:00
Vlad MocanuandClaude Opus 4.6 df7afc08d4 fix: wire allowed_tools/disallowed_tools inputs through to Claude SDK
The disallowed tools env var read in run.ts was DISALLOWED_TOOLS_INPUT
(raw user input) instead of DISALLOWED_TOOLS (computed value including
WebSearch/WebFetch defaults). This meant default tool disallowance was
never enforced. Also removes orphaned ALLOWED_TOOLS env var export from
createPrompt() (tag mode passes allowed tools via claudeArgs instead),
extracts shared parseToolsInput utility to eliminate duplication, and
adds tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 11:16:38 +02:00
GitHub Actions 1dd74842e5 chore: bump Claude Code to 2.1.61 and Agent SDK to 0.2.61 2026-02-26 22:39:58 +00:00
Vlad MocanuandClaude Opus 4.6 29a1032aa6 fix: normalize Forgejo PR/issue state to uppercase
Forgejo REST API returns state as lowercase ("closed"/"open") but
branch.ts compares against GraphQL-style uppercase ("CLOSED"/"MERGED").
This caused @claude on closed/merged PRs to take the open-PR checkout
path, failing with "fatal: invalid reference: refs/pull/N/head".

Normalize state to uppercase in the Forgejo data fetcher, matching the
existing pattern used for review state (line 153).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 21:19:53 +02:00
Vlad MocanuandClaude Opus 4.6 2e1165ed90 fix: handle Forgejo pull_request_comment payload missing pull_request field
Forgejo's pull_request_comment event uses an issue_comment-like payload
with issue.number instead of pull_request.number. Add fallback to
extract entity number from issue field when pull_request is absent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:31:40 +02:00
Vlad MocanuandClaude Opus 4.6 741f12afaf fix(prd-7): M5 code quality review — remove dead re-exports, verify type safety
- Removed unused re-exports (CommentUpdateInput, ExecutionDetails,
  ensureProperlyEncodedUrl) from forgejo/comment-logic.ts
- Verified no `any` type escapes in Forgejo-specific code
- Confirmed naming conventions consistent with upstream
- No dead code, unused imports, or unreachable branches
- All complex decisions documented with comments
- Typecheck, format, and 834 tests pass clean

Progress: 83% complete — M6 (documentation accuracy) remaining

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:57:25 +02:00
Vlad MocanuandClaude Opus 4.6 6b0302d226 fix(prd-7): M3 security review — self-trigger guard, actor permissions, trigger bypass, platform validation
- Add self-mention/bot loop prevention in actor.ts (blocks actor matching bot_name)
- Switch Forgejo permission check to actor-level via getCollaboratorPermissionLevel
  with fallback to token-level repos.get() when collaborator API returns 403
- Close empty-body PR review comment trigger bypass (now returns false instead of true)
- Add platform detection cross-validation (reject PLATFORM=forgejo on github.com)
- Document URL builder defense-in-depth rationale in comment-logic.ts
- Update/add tests across 6 test files (actor, permissions, trigger, platform, mcp, m7)

Progress: 50% complete (3/6 milestones) — next: M4 test coverage review

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:31:12 +02:00
Vlad MocanuandClaude Opus 4.6 cefc229199 fix(prd-7): M2 error handling review — fix process.exit bypasses, TOCTOU gap, URL consistency
- F1: Replace process.exit(1) with throw in branch.ts (cleanup was bypassed)
- F2: Replace process.exit(1) with throw in install-mcp-server.ts (same issue)
- F2b: Replace process.exit(1) with throw in create-prompt/index.ts (same issue)
- F3: Use getServerUrl() instead of GITHUB_SERVER_URL constant in branch-cleanup.ts
- F4: Use updated_at for review TOCTOU filtering in Forgejo data fetcher
- Accepted notes: no pagination >100, no retry, unconditional trigger, image no-op

Progress: 33% complete — M2 done, M3 (security review) next

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:16:22 +02:00
Vlad MocanuandClaude Opus 4.6 ac58fb6c14 fix(prd-7): M1 architecture review — consolidate duplication, fix bugs, memoize detection
- F1: Consolidated updateForgejoCommentBody into unified updateCommentBody (~150 lines removed)
- F2: Fixed wrong run identifier in update-with-branch.ts (context.runId → getForgejoRunIndex())
- F3: Memoized detectPlatform() with _resetPlatformCache() for tests
- F4: Added getServerUrl() runtime helper to resolve constant-vs-function inconsistency
- F5: Fixed sticky comment bot matching on Forgejo via context.inputs.botName
- F6: Documented GHES heuristic misidentification in docs/forgejo.md
- Updated PRD M1 checkbox to complete

Progress: 17% complete — M1 done, M2 (error handling review) next

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:09:02 +02:00
Vlad Mocanu ae326659b1 Merge remote-tracking branch 'upstream/main' 2026-02-26 11:36:32 +02:00
Vlad MocanuandClaude Opus 4.6 2acaece6ae fix: use expand=1 instead of quick_pull=1 in PR URL on Forgejo
The prompt template hardcoded quick_pull=1 (GitHub-specific) in the
"Create a PR" URL. On Forgejo, this parameter is ignored so the
compare page doesn't auto-expand the PR creation form. Forgejo uses
expand=1 for this purpose.

Since Claude writes the PR URL into the comment body, and
updateForgejoCommentBody() extracts it (prLinkFromContent) with
priority over the URL generated by update-comment-link.ts, the
prompt template is the correct place to fix this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:32:53 +02:00
Vlad MocanuandClaude Opus 4.6 99624efacc fix: fall back to issue.assignee on Forgejo for assignee trigger
Forgejo's issues/assigned webhook payload doesn't include a top-level
assignee object. The assignee is only available under issue.assignee.
Fall back to that field when payload.assignee is missing.

Also removes M5 debug logging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:47:30 +02:00
Vlad MocanuandClaude Opus 4.6 001072c05c debug: add issue.assignee/assignees logging for M5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:46:25 +02:00
Vlad MocanuandClaude Opus 4.6 9efd06cc26 debug: add M5 debug logging for assignee trigger
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:45:15 +02:00
Vlad MocanuandClaude Opus 4.6 71fb686885 fix: gate Forgejo review/comment triggers on @claude mention
Previously, all pull_request_comment events on Forgejo triggered the
full Claude pipeline unconditionally — both in the workflow if-condition
and in trigger.ts. This wasted CI resources and API credits on reviews
without @claude mentions (e.g. plain "LGTM" reviews).

Workflow: add @claude check via contains() on comment.body and review.body.
Trigger validation: replace blind auto-trigger with payload-aware check
that inspects available body fields, falling back only when no body exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:45:09 +02:00
Vlad MocanuandClaude Opus 4.6 84cf34ff1f fix: route Forgejo review comment updates to issue comment API
On Forgejo, the tracking comment for pull_request_review_comment events
is always created as a regular issue comment (because Forgejo's webhook
payload lacks the top-level comment object). The MCP server was
incorrectly setting isPullRequestReviewComment=true based on the event
name, causing every update to try pulls.updateReviewComment first —
which 404'd and silently swallowed Claude's responses.

Three fixes:
- MCP server: detect Forgejo and always use issue comment path
- update-comment-link: skip pulls.getReviewComment on Forgejo (saves
  one wasted 404 API call)
- update-claude-comment: expand fallback to also catch 422 (defense
  in depth for Forgejo edge cases)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:45:09 +02:00
Vlad MocanuandClaude Opus 4.6 a6b3c94004 fix: use dependency injection for image-downloader fs mocks
mock.module("fs/promises") on Linux Bun poisons the entire mock
tracking system — not just fs mocks, but ALL mock/spy assertions
(octokit mocks, spyOn(global, "fetch"), etc.) stop recording calls.

Replace with dependency injection: downloadCommentImages() now accepts
an optional fsDeps parameter. Tests pass mock mkdir/writeFile directly.
Production callers omit it and get real fs. No mock.module anywhere.

Fixes 18 test failures blocking CI on Forgejo Linux runners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:45:08 +02:00
Vlad MocanuandClaude Opus 4.6 3cd73bccde revert: remove label check from opened events to prevent duplicates
Forgejo fires both issues/opened AND issues/label_updated when an
issue is created with a label. Having the label check on opened
caused duplicate Claude runs. Remove it — the label_updated event
handles label triggers correctly now that the workflow gate and
action normalization are fixed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:30:47 +02:00
Vlad MocanuandClaude Opus 4.6 1feec26c2e feat: check trigger label on issue opened events
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>
2026-02-26 10:29:05 +02:00
Vlad MocanuandClaude Opus 4.6 b37009b766 cleanup: remove M4 debug logging from run.ts
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>
2026-02-26 10:27:59 +02:00
Vlad MocanuandClaude Opus 4.6 baa007a3ec fix: normalize Forgejo 'label_updated' to 'labeled' in context
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>
2026-02-26 10:25:58 +02:00
Vlad MocanuandClaude Opus 4.6 fa0070ddcc fix: accept Forgejo 'label_updated' action for label triggers
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>
2026-02-26 10:24:01 +02:00
Vlad MocanuandClaude Opus 4.6 b020426956 debug: add issues event payload logging for M4 label trigger
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>
2026-02-26 10:22:57 +02:00
Vlad MocanuandClaude Opus 4.6 dab92ef0eb fix: use mock.module for image-downloader tests
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>
2026-02-26 09:05:39 +02:00
Vlad MocanuandClaude Opus 4.6 cff360d00a fix: use dependency injection for image-downloader fs mocks
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>
2026-02-26 08:54:58 +02:00
Vlad MocanuandClaude Opus 4.6 ac8e22e2f8 fix: fall through to issue comment when PR review comment fetch 404s
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>
2026-02-26 08:49:26 +02:00
Vlad MocanuandClaude Opus 4.6 e73c881c64 fix: skip commentBody validation for Forgejo review comment events
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>
2026-02-26 08:42:22 +02:00
Vlad MocanuandClaude Opus 4.6 9d8c96f01e fix: handle missing comment payload in Forgejo pull_request_review_comment
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>
2026-02-26 08:39:48 +02:00