- Increase --max-turns from 20 to 30 (large PRs with 31 files need ~25 turns)
- Explicitly instruct Claude to use Write tool (not Edit/python3 fallback)
- Add READ-ONLY constraint to prevent source code modifications
- Previous run hit error_max_turns at turn 21 despite successfully writing review
Part of #818
- Upgrade review model from glm-5 to glm-5.1
- Add fallback step: if Claude doesn't write pr_review.md, extract
last assistant message from execution output as review content
- Upload claude-execution-output.json as artifact (7-day retention)
so review sessions can be inspected after the job completes
- Publish review comment runs on always() instead of only on success,
so fallback-extracted reviews still get posted
Allows seeing Claude's full output in CI logs instead of the
opaque "full output hidden for security" message. Helps debug
slow reviews and identify bottlenecks.
claude-code-action@v1 auto-updates its bundled Claude Code and Agent SDK.
The custom path_to_claude_code_executable forced the runner's stale CLI
(v2.1.39) to be used instead, causing validateHeaders auth failures when
the SDK updated to 0.2.85. Let the action manage its own installation.
Closes#810
Root cause: Bun's test runner fails to resolve exports from directory
index.ts files during parallel test execution. The api-command/index.ts
imports 8 subcommand modules — if any fail to load during concurrent
test runs, the entire module fails with "Export not found."
Fix: extract createApiCommandHandler into handler.ts (lightweight, only
imports named-command-router). Test imports from handler.ts directly,
bypassing the heavy index.ts that loads all subcommand modules.
Also:
- Remove stale api-command.ts barrel file
- Use explicit /index import paths in source
- Migrate CI validate to self-hosted runner with clean artifact step
- Disable Bun cache (no-cache: true) on self-hosted runner
Review prompt already contains self-contained CCS-specific rules.
Loading project CLAUDE.md caused dev-oriented rules (no-emoji, quality
gates, branching) to leak into review output. --bare makes the review
prompt the sole source of truth and saves ~2K tokens per run.
Multi-platform builds create a manifest index pointing to per-platform
images. The existing labels parameter only sets labels on per-platform
image configs, but GHCR reads annotations from the manifest index for
repository auto-linking. Add index-level annotations so the package
appears under the repo Packages tab instead of only the user profile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
External PRs could modify review-prompt.md to suppress findings or
manipulate the reviewer. Now loads prompt via git show from the base
branch (origin/dev or origin/main), never from PR-controlled checkout.
Also:
- Fallback prompt upgraded with adversarial framing and severity output
- Use printf instead of echo for content preservation
- Expand deep review trigger to all .github/ files (not just workflows/)
- Warning message now shows '.github/review-prompt.md' instead of just
'review-prompt.md' for easier debugging
- Deep review scope-aware trigger explicitly includes .github/workflows/
alongside auth/, middleware/, security/, crypto/, commands/, shared/
The maintainability gate (process.exit count + sync FS API count) was
a raw grep counter that provided no actionable signal for a CLI tool.
It blocked the v7.53.0 stable release because new features naturally
increased the counts beyond the baseline.
Removed:
- scripts/maintainability-baseline.js
- scripts/maintainability-check.js
- docs/metrics/maintainability-baseline.json
- All maintainability:* npm scripts
- Gate references from release, dev-release, and CI workflows
Quality gates retained: typecheck, eslint, prettier, full test suite.
semantic-release outputs ANSI color codes that break the grep pattern
for detecting "Published GitHub release", causing Discord notifications
to be skipped even on successful releases.
semantic-release needs to push version bump + CHANGELOG commits directly
to main. GITHUB_TOKEN cannot bypass branch protection rules, causing
@semantic-release/git to fail with "push declined due to repository rule
violations". PAT_TOKEN has admin bypass and resolves the issue.
* fix(cliproxy): migrate deprecated gemini-claude-* model names to upstream claude-* names (#515)
* fix(cliproxy): migrate deprecated gemini-claude-* model names to upstream claude-* names
CLIProxyAPI registry no longer recognizes the gemini-claude-* prefix convention.
Model names in catalog, base config, and user settings are migrated to upstream
claude-* names. Auto-migration in env-builder rewrites existing user settings on
load and persists the change.
Closes#513
* fix: address code review feedback — sync UI layer and add migration tests
- Sync UI isNativeGeminiModel() with backend (remove gemini-claude- exclusion)
- Update UI model catalog agy entries from gemini-claude-* to claude-*
- Update CI/CD workflow and code-reviewer default model names
- Add unit tests for migrateDeprecatedModelNames() logic
* fix(hooks): isolate image type check before error-prone processing (#514)
* fix(hooks): isolate image type check before error-prone processing
Restructure processHook() into two phases so non-image Read calls
never see hook error messages. Phase 1 defensively checks tool name
and file extension, exiting 0 silently on any failure. Phase 2 only
runs for confirmed image/PDF files where errors are relevant.
Closes#511
* fix(hooks): sync image analyzer hook file on every profile launch
Add installImageAnalyzerHook() call to cliproxy executor, matching
the existing installWebSearchHook() pattern. This ensures the .cjs
file in ~/.ccs/hooks/ gets refreshed from the npm package on every
launch, so users receive hook updates after npm update.
* chore(release): 7.41.0-dev.1 [skip ci]
* fix(cliproxy): add fork:true for Claude model aliases in config generator (#523)
Config generator now outputs fork:true for Claude model alias entries,
ensuring both upstream (claude-*) and aliased (gemini-claude-*) model
names appear in /v1/models listings. Also preserves fork flag when
parsing user-added aliases during config regeneration.
Bumps config version to v7 to trigger regeneration on next ccs doctor.
Closes#522
* chore(release): 7.41.0-dev.2 [skip ci]
* feat(cliproxy): add account safety guards to prevent Google account bans (#516)
* feat(cliproxy): add account safety guards to prevent Google account bans
Implements cross-provider isolation to prevent Google from flagging
concurrent OAuth usage across different client IDs (ref: #509, #512).
Three pillars:
1. Auto-pause enforcement at session launch — conflicting accounts in
other Google OAuth providers are paused so CLIProxyAPI can't use them,
restored on session exit with crash recovery via auto-paused.json
2. Ban/disable detection — error responses matching Google ban patterns
auto-pause the affected account to prevent further damage
3. Cross-provider conflict warnings during OAuth registration
Key design decisions:
- PID-based session tracking for crash recovery (dead PID = restore)
- Timestamp comparison prevents restoring ban-paused accounts on exit
- Schema validation on auto-paused.json prevents corrupted state
- Falls back to warn-only when another session is managing isolation
* fix(cliproxy): address code review feedback (attempt 1/5)
- Re-read auto-paused.json before write in enforceProviderIsolation to
reduce concurrent write race window
- Use actual email from registry for display instead of raw accountId
- Export maskEmail for testability
- Add 27 unit tests covering ban detection, email masking,
cross-provider duplicate detection, enforcement lifecycle,
crash recovery, and timestamp-guarded restore
* fix(cliproxy): address remaining review feedback (attempt 2/5)
- Add handleBanDetection test verifying account pause on ban error
- Add warnCrossProviderDuplicates tests (true/false/non-Google)
- Document PID reuse limitation in isPidAlive JSDoc comment
* chore(release): 7.41.0-dev.3 [skip ci]
* feat(cliproxy): runtime quota monitoring during active sessions (#529)
* feat(cliproxy): add runtime quota monitoring during active sessions
Adds adaptive background quota polling to detect and respond to quota
exhaustion during active CLIProxy sessions. Prevents rate-limit-driven
account bans by auto-cooling exhausted accounts and switching defaults.
- Adaptive polling: 300s normal, 60s at 20% threshold, stops at 0%
- Stderr warnings at 20%, boxed exhaustion alerts at 0%
- Cooldown + default switch on exhaustion (existing patterns)
- Configurable via quota_management.runtime_monitor in config.yaml
- Timer.unref() prevents blocking process exit
- monitorStopped guard for in-flight poll safety
Closes#524
* fix: address code review feedback (attempt 1/5)
- M1: Round quotaPercent display with Math.round() to avoid ugly floats
- M2: Rename exhaust_threshold -> exhaustion_threshold for consistency
with existing auto.exhaustion_threshold config field
- M3: Replace async not.toThrow() with direct await assertion pattern
* fix: address code review feedback (attempt 2/5)
- Remove .claude/agent-memory/ from tracking and add to .gitignore
- Unify cooldown_minutes default to 5 (was 10 in runtime_monitor, 5 in auto)
- Add threshold validation in startQuotaMonitor (warn > exhaustion)
- Document intentional post-switch monitoring gap in code comment
* chore(release): 7.41.0-dev.4 [skip ci]
* fix(cliproxy): mask email in ban detection and fix JSDoc default
- Use maskEmail() in handleBanDetection output for consistency
- Fix cooldown_minutes JSDoc: default is 5, not 10
* chore(release): 7.41.0-dev.5 [skip ci]
* fix(cliproxy): address all review feedback (Low + informational)
- Add sync constraint comment on process.exit handler (executor)
- Add TOCTOU race acceptability comment (account-safety)
- Mask email in handleQuotaExhaustion reason string
- Use realistic exhaustion_threshold (5) in test configs
* chore(release): 7.41.0-dev.6 [skip ci]
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Reverts to the original guided output format with:
- Visual hierarchy with emojis and separators
- Priority tags (High/Medium/Low)
- Security and code quality checklists
- Ultrathink directive
- Reaction comments for /review triggers
- Model signature in review footer
claude-code-action validates that anthropic_api_key is non-empty before
running. CLIProxy ignores this value and uses ANTHROPIC_AUTH_TOKEN for
actual authentication.
- Use anthropics/claude-code-action@v1 directly
- Remove ccs-reviewer wrapper dependency
- Match pr-review-comprehensive.yml pattern
- Keep CLIProxy env vars for model routing
- Restore comprehensive allowedTools for posting
- Add more allowed bash patterns: gh issue view, gh api, git commands
- Add base tools: Edit, LS for better flexibility
- Update prompt to write pr_review.md in working dir (not /tmp/)
- Add step to Read before Write to avoid security block
- Add explicit rules about avoiding shell operators (||, &&, <<)
- Allow rm pr_review.md for cleanup
Previous run had 9 tool rejections wasting ~5000 tokens due to:
- gh issue view not allowed (only gh pr view was)
- /tmp/ outside working directory
- Write without Read blocked
- Complex shell operators blocked
- Use /api/provider/agy endpoint for proper model routing
- Set ANTHROPIC_DEFAULT_*_MODEL to route Haiku/Sonnet/Opus correctly
- Disable telemetry, error reporting, bug command
- Set max output/thinking tokens
Add Bash(echo *) and Bash(cat *) patterns to allow heredocs and pipes.
This gives the AI reviewer flexibility to use any method for posting
multi-line review comments while remaining safe (shell-operator aware).
Changed pattern from `Bash(gh pr comment:*)` to `Bash(gh pr comment *)`
to match space-separated args. Updated prompt to discourage heredocs
and pipes which don't match the pattern, causing 6+ retry attempts.
Root cause: When the bot posts a review comment, GitHub fires an
issue_comment event that joins the same concurrency group and cancels
the in-progress run BEFORE job `if` conditions are evaluated.
Solution: Non-actionable triggers (bot comments, comments without
/review) get a unique per-run group via format('skip-{0}', github.run_id),
while legitimate triggers share the PR-based group for proper
cancellation of outdated reviews.
- Add `synchronize` event to trigger on new commits pushed to PR
- Add `reopened` event to trigger when PR is reopened
- Update documentation comments to reflect new behavior
- Keep cancel-in-progress to prevent stale reviews
Fixes issue where AI review only ran on PR creation, requiring
manual /review comment for subsequent commits.
Root cause of cancellations was track_progress posting comments that
triggered new workflow runs. Instead of complex concurrency isolation,
simply disable progress tracking.
Changes:
- track_progress: false (no intermediate comments)
- Simple concurrency: ai-review-<PR_NUMBER>
- Removed extra bot filters (no longer needed)
Result: Clean PRs with just the final review comment.
Previous fix was incomplete - both human /review comments and bot
progress comments were issue_comment events sharing the same
concurrency group.
Now includes comment author in concurrency group:
- Human: ai-review-issue_comment-kaitranntt-320
- Bot: ai-review-issue_comment-ccs-agy-reviewer[bot]-320
- PR/dispatch: ai-review-<event>-system-320
This completely isolates bot activity from human-triggered reviews.
Root cause: Bot progress comments triggered issue_comment events,
which created new workflow runs with the same concurrency group,
causing cancel-in-progress to kill the original review.
Fixes:
1. Add event_name to concurrency group (prevents cross-event cancellation)
2. Explicit filter for ccs-agy-reviewer[bot] comments (belt-and-suspenders)
Closes race condition causing 25+ cancelled AI review runs.
Bot progress comments were triggering new workflow runs,
which cancelled in-progress reviews due to concurrency group.
Added check for github.event.comment.user.type != 'Bot'.