Skip to content

feat(claude): add Claude Code Router (CCR) integration#750

Open
wcpaxx wants to merge 2 commits intoAutoMaker-Org:mainfrom
wcpaxx:support-ccr
Open

feat(claude): add Claude Code Router (CCR) integration#750
wcpaxx wants to merge 2 commits intoAutoMaker-Org:mainfrom
wcpaxx:support-ccr

Conversation

@wcpaxx
Copy link

@wcpaxx wcpaxx commented Feb 3, 2026

  • Add global setting to enable/disable CCR routing
  • Auto-detect CCR installation and server status
  • Route Claude Agent SDK requests through CCR when enabled
  • Add UI toggle with status indicator in settings

Summary by CodeRabbit

  • New Features

    • Added Claude Code Router (CCR) integration with enable/disable toggle in Settings.
    • CCR status display and a new status API to show installation/runtime health and endpoint info.
    • App will prefer CCR routing when available and automatically fall back to standard routing.
    • Settings now sync CCR preference between UI and server.
  • Bug Fixes

    • Fixed blank-screen issue on Windows by disabling hardware acceleration on startup.

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Adds Claude Code Router (CCR) support: server CCR utilities, CCR-aware Claude provider routing, settings endpoint, UI settings component and store flag, type additions, and a global settings accessor for centralized CCR toggling.

Changes

Cohort / File(s) Summary
CCR Core & Types
apps/server/src/lib/ccr.ts, libs/types/src/ccr.ts, libs/types/src/index.ts
New CCR utilities: detect installation, cached status query (2s TTL), read config, build SDK env; adds CCRStatus and CCRConfig types and exports.
Claude Provider Integration
apps/server/src/providers/claude-provider.ts, libs/types/src/provider.ts
buildEnv made async and gains ccrEnabledOverride; provider checks global setting and CCR status first, merges CCR-provided env when available; adds optional ccrEnabled on ExecuteOptions.
Global Settings Accessor & Init
apps/server/src/lib/global-settings-accessor.ts, apps/server/src/index.ts
Introduces singleton accessor for global settings with initGlobalSettingsAccessor, getGlobalSettingsFromAccessor, isCCREnabled, and resetGlobalSettingsAccessor; initialized at server start.
Server Routes
apps/server/src/routes/settings/routes/get-ccr-status.ts, apps/server/src/routes/settings/index.ts
Adds /api/settings/ccr/status endpoint returning CCRStatus (installed, running, port, apiEndpoint) and error handling.
UI Settings & Store
apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx, apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx, apps/ui/src/store/app-store.ts
New CCRSettings React component, integrated into Claude settings tab; adds ccrEnabled state and setCcrEnabled action with optimistic update and server sync.
Settings Sync & Migration
apps/ui/src/hooks/use-settings-sync.ts, apps/ui/src/hooks/use-settings-migration.ts, libs/types/src/settings.ts
Adds ccrEnabled to settings sync, migration, hydration, and DEFAULT_GLOBAL_SETTINGS (false).
Platform Workaround
apps/ui/src/main.ts
Windows-specific change: disable hardware acceleration on win32 startup to avoid blank screen issues.

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI Settings (React)
    participant Store as App Store (State)
    participant Server as Server (Node)
    participant CCRUtil as CCR Utils (ccr.ts)
    participant CCRServer as CCR CLI/Server (External)
    participant Claude as Claude Provider

    UI->>Server: GET /api/settings/ccr/status
    activate Server
    Server->>CCRUtil: getCCRStatus()
    activate CCRUtil
    CCRUtil->>CCRServer: run `ccr status` (or check install)
    alt CCR installed & running
        CCRServer-->>CCRUtil: parsed status (port, apiEndpoint)
        CCRUtil-->>Server: {installed:true,running:true,port,apiEndpoint}
    else not installed/running
        CCRUtil-->>Server: {installed:false,running:false,error?}
    end
    deactivate CCRUtil
    Server-->>UI: JSON status
    deactivate Server

    UI->>Store: setCcrEnabled(true)
    activate Store
    Store->>Server: POST /api/settings {ccrEnabled:true}
    activate Server
    Server->>Server: persist global settings
    Server-->>Store: 200 OK
    deactivate Server
    Store-->>UI: update state
    deactivate Store

    UI->>Server: POST /api/claude/execute {ccrEnabled:true,...}
    activate Server
    Server->>Claude: executeQuery(..., ccrEnabled:true)
    activate Claude
    Claude->>CCRUtil: getCCRStatus()
    activate CCRUtil
    CCRUtil-->>Claude: {installed:true,running:true,port,apiEndpoint}
    deactivate CCRUtil
    Claude->>CCRUtil: getCCREnvFromStatus(status, config)
    activate CCRUtil
    CCRUtil-->>Claude: env vars for routing
    deactivate CCRUtil
    Claude->>CCRServer: forward request via CCR endpoint
    CCRServer-->>Claude: response
    Claude-->>Server: result
    deactivate Claude
    Server-->>UI: response payload
    deactivate Server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

Enhancement

Suggested reviewers

  • Shironex

Poem

🐰 I hop through code with a curious twitch,
Found a router called Claude — what a nifty switch!
From settings to server, I nudge the right path,
CCR enabled — hooray, do the math! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(claude): add Claude Code Router (CCR) integration' accurately and concisely summarizes the main change—introducing CCR integration throughout the codebase for routing Claude Agent SDK requests.
Docstring Coverage ✅ Passed Docstring coverage is 80.65% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @wcpaxx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates the Claude Code Router (CCR) into the application, providing users with the option to route their Claude API requests through a local proxy. This enhancement aims to offer more control over API routing, potentially enabling features like model switching and cost optimization. The changes span across backend utilities for CCR detection and configuration, modifications to the Claude provider to respect the new routing preference, and a user-friendly interface for managing this setting.

Highlights

  • Claude Code Router (CCR) Integration: Introduced a global setting to enable or disable routing Claude Agent SDK requests through the Claude Code Router (CCR).
  • Automatic CCR Detection: Implemented functionality to automatically detect if the CCR CLI is installed and if the CCR server is currently running.
  • Dynamic API Routing: Modified the Claude Agent SDK query functions to conditionally route API requests via CCR when the feature is enabled and CCR is available.
  • User Interface for CCR Settings: Added a new UI component in the settings to toggle CCR routing, complete with a status indicator showing CCR's installation and running state.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • apps/server/src/lib/ccr.ts
    • Added a new utility file to handle CCR-related operations, including reading CCR configuration, checking CLI installation, determining server status, and generating environment variables for SDK integration.
  • apps/server/src/providers/claude-provider.ts
    • Updated the buildEnv function to incorporate CCR-specific environment variables when CCR routing is enabled, giving it the highest priority in API routing.
    • Extended the executeQuery options to accept a ccrEnabled flag, controlling whether requests are routed through CCR.
  • apps/server/src/providers/simple-query-service.ts
    • Modified SimpleQueryOptions to include a ccrEnabled flag, which is then passed to the Claude provider for API routing decisions.
  • apps/server/src/routes/settings/index.ts
    • Added a new API endpoint /api/settings/ccr/status to expose the current status of the Claude Code Router.
  • apps/server/src/routes/settings/routes/get-ccr-status.ts
    • Introduced a new route handler to retrieve and return the CCR installation and running status to the frontend.
  • apps/server/src/services/agent-service.ts
    • Integrated the ccrEnabled global setting into the agent service, ensuring that agent queries can leverage CCR routing.
  • apps/server/src/services/auto-mode-service.ts
    • Propagated the ccrEnabled setting to various auto-mode operations, allowing them to utilize CCR for API requests.
  • apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx
    • Created a new UI component to display CCR status (installed, running, port) and provide a toggle switch for enabling/disabling CCR routing.
  • apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx
    • Incorporated the CCRSettings component into the Claude settings tab, making the CCR toggle and status visible to users.
  • apps/ui/src/hooks/use-settings-sync.ts
    • Updated the settings synchronization logic to include the new ccrEnabled setting, ensuring it persists across sessions and is loaded from the server.
  • apps/ui/src/store/app-store.ts
    • Added ccrEnabled to the application's global state and provided an action to update and persist this setting.
  • libs/types/src/ccr.ts
    • Defined new TypeScript interfaces (CCRStatus, CCRConfig) to standardize the data structures for CCR status and configuration.
  • libs/types/src/index.ts
    • Exported the new CCRStatus and CCRConfig types for broader use within the application.
  • libs/types/src/provider.ts
    • Extended the ExecuteOptions interface with an optional ccrEnabled flag to control CCR usage during API calls.
  • libs/types/src/settings.ts
    • Added ccrEnabled as a property to the GlobalSettings interface and set its default value.
Activity
  • The pull request was created by wcpaxx with the intent to add Claude Code Router (CCR) integration, including a global setting, auto-detection, routing capabilities, and a UI toggle.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and valuable feature: integration with the Claude Code Router (CCR). The changes are well-structured, spanning the server, UI, and shared types to provide a complete end-to-end experience for enabling and using CCR. The implementation includes auto-detection of CCR, a new API endpoint for status checks, and a corresponding UI component in the settings. The code is generally of high quality. My review includes a couple of suggestions to improve the efficiency of the CCR utility functions on the server-side by avoiding redundant function calls and file system access. Overall, this is a solid contribution.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/server/src/lib/ccr.ts`:
- Around line 40-92: The current blocking execSync usage in isCCRInstalled and
getCCRStatus can block the Node event loop; replace both execSync calls with
non-blocking child_process.execFile (or exec) used with Promises/async/await,
move getCCRStatus to return Promise<CCRStatus>, and update callers accordingly;
additionally add a small in-memory cache (e.g., Map or module-scoped variable)
in getCCRStatus keyed by e.g. 'ccr-status' with a short TTL (1000–5000ms) to
serve repeated requests without re-running commands, use getCCRConfig() as
before to obtain port fallback, ensure you implement timeout handling and
surface errors as the existing error field so behavior remains consistent.

In `@apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx`:
- Around line 78-146: The toggle currently disables entirely when CCR isn't
installed/running (canEnable), preventing users from turning CCR off if it later
stops; change the logic so disabling only blocks enabling but still allows
disabling by using a condition like "disabled = !canEnable && !ccrEnabled" for
the Switch and update the Label opacity check from "!canEnable" to "!canEnable
&& !ccrEnabled" (referencing canEnable, ccrStatus, ccrEnabled,
onCcrEnabledChange, and the Switch/Label elements) so users can always turn CCR
off even if the service stops.
🧹 Nitpick comments (5)
apps/server/src/routes/settings/routes/get-ccr-status.ts (2)

7-9: Align CCR import with shared-package import rule.

This relative import conflicts with the project-wide TS/TSX import guideline. Consider re-exporting the CCR helpers from a shared package (or an approved alias) and importing via @automaker/* to avoid cross-module relative imports.

As per coding guidelines: **/*.{ts,tsx,js,jsx}: Always import from shared packages (@automaker/*), never from old paths or relative imports to other modules.


19-30: Route handler should delegate to a service and emit events.

This route performs CCR status logic directly and doesn’t emit events. Please move the business logic into a service and emit via createEventEmitter() to match server architecture expectations.

As per coding guidelines: apps/server/src/**/*.{ts,tsx} → Server business logic should be organized into services in services/, with routes delegating to services; all server operations should emit events using createEventEmitter() from lib/events.ts.

apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx (1)

7-7: Prefer barrel export for intra-app UI imports.

Please import CCRSettings from the components barrel rather than a direct path to keep intra-app imports stable and consistent.

Based on learnings: In the apps/ui codebase, when importing UI components within the same app, prefer barrel exports from ../components (i.e., import from the components index barrel) rather than direct path imports.

apps/server/src/providers/claude-provider.ts (1)

11-11: Use shared-package import path for CCR helpers.

The new relative import conflicts with the project import rule. Consider re-exporting CCR helpers via an @automaker/* entrypoint (or approved alias) and import from there.

As per coding guidelines: **/*.{ts,tsx,js,jsx}: Always import from shared packages (@automaker/*), never from old paths or relative imports to other modules.

apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx (1)

2-4: Prefer barrel exports for UI components.

Use the app’s components barrel for Label/Switch rather than direct file paths to keep imports stable.

Based on learnings: In the apps/ui codebase, when importing UI components within the same app, prefer barrel exports from ../components (i.e., import from the components index barrel) rather than direct path imports.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/server/src/lib/ccr.ts`:
- Around line 85-88: The current detection uses stdout.includes('running') which
yields false-positives for strings like "Not running"; update the check that
sets isRunning to use a case-insensitive word-boundary regex that excludes the
phrase "not running" (for example use a negative lookbehind like
/\b(?<!not\s)running\b/i or an equivalent pattern) to test stdout, then set
CCRStatus accordingly (referencing isRunning, stdout, and CCRStatus in the same
function) so only true "running" states match.
🧹 Nitpick comments (2)
apps/server/src/routes/settings/routes/get-ccr-status.ts (1)

7-23: Move CCR status logic into a service and import via a shared package export.

Route handlers should delegate to a service; also, the relative import to lib/ccr.js violates the shared-package import rule. Consider introducing a ccr-service in apps/server/src/services/ (which can emit a status event if needed) and re-exporting it through an @automaker/* package, then import it here.

As per coding guidelines, apps/server/src/**/*.{ts,tsx}: Server business logic should be organized into services in the services/ directory, with Express route handlers in routes/ that delegate to services; and **/*.{ts,tsx,js,jsx}: Always import from shared packages (@automaker/*), never from old paths or relative imports to other modules.

apps/server/src/providers/claude-provider.ts (1)

10-11: Avoid relative CCR imports; expose via a shared package.

The new ../lib/ccr.js relative import conflicts with the shared-package import rule. Please re-export CCR helpers through an @automaker/* package and import from there.

As per coding guidelines, **/*.{ts,tsx,js,jsx}: Always import from shared packages (@automaker/*), never from old paths or relative imports to other modules.

@wcpaxx wcpaxx force-pushed the support-ccr branch 2 times, most recently from 9caf5fe to 51cfb75 Compare February 3, 2026 15:04
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/server/src/lib/ccr.ts`:
- Around line 30-35: The stripAnsi function uses a regex literal containing
control characters (\u001b and \u009b) which Biome flags; replace the regex
literal with a RegExp constructed from an escaped string (e.g. new
RegExp('...','g')) so the control characters are represented as double-escaped
sequences (\\u001b, \\u009b) inside the string, remove the inline eslint
disable, and ensure the RegExp preserves the same pattern and global flag used
in stripAnsi.
🧹 Nitpick comments (2)
apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx (1)

2-3: Prefer the components barrel for UI imports.

This keeps intra-app imports stable and consistent.

♻️ Suggested change
-import { Label } from '@/components/ui/label';
-import { Switch } from '@/components/ui/switch';
+import { Label, Switch } from '@/components';
Based on learnings: In the apps/ui codebase, when importing UI components within the same app, prefer barrel exports from ../components (i.e., import from the components index barrel) rather than direct path imports. This improves import stability and refactorability within the app.
apps/server/src/services/auto-mode-service.ts (1)

2346-2347: Consider consolidating settings fetch to avoid potential duplicate call.

getPhaseModelWithOverrides (called at lines 2340-2345) likely fetches global settings internally. Fetching them again here for ccrEnabled may result in duplicate async calls.

💡 Potential optimization

If getPhaseModelWithOverrides already returns global settings or can be extended to include ccrEnabled, consolidate the fetch:

// Option 1: Extend getPhaseModelWithOverrides to return ccrEnabled
const {
  phaseModel: phaseModelEntry,
  provider: analysisClaudeProvider,
  credentials,
  ccrEnabled: analysisCcrEnabled, // Add to return type
} = await getPhaseModelWithOverrides(...);

// Option 2: Or keep as-is if the helper intentionally separates concerns

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/server/src/lib/ccr.ts`:
- Around line 85-109: The code currently hardcodes 127.0.0.1 when building the
CCR endpoint (see getCCRConfig usage and CCRStatus.apiEndpoint), so change
endpoint construction to honor CCRConfig.HOST: derive a host value like host =
config?.HOST ?? '127.0.0.1' and treat '0.0.0.0' as '127.0.0.1', then use that
host together with port to build apiEndpoint and any other base URLs (the same
change should be applied to the other block around lines 141-148 that also
builds a base URL).
🧹 Nitpick comments (1)
apps/ui/src/components/views/settings-view/claude/ccr-settings.tsx (1)

25-37: Consider adding fetch cleanup to prevent memory leaks.

The useEffect fetches CCR status on mount but doesn't handle component unmount during an in-flight request. While React 18+ handles this more gracefully, adding an AbortController would be cleaner.

♻️ Optional: Add cleanup with AbortController
 useEffect(() => {
+  const abortController = new AbortController();
   const fetchStatus = async () => {
     try {
-      const response = await apiGet<CCRStatus>('/api/settings/ccr/status');
+      const response = await apiGet<CCRStatus>('/api/settings/ccr/status', {
+        signal: abortController.signal,
+      });
       setCcrStatus(response);
     } catch (error) {
+      if (abortController.signal.aborted) return;
       setCcrStatus({ installed: false, running: false, error: 'Failed to check CCR status' });
     } finally {
+      if (!abortController.signal.aborted) {
         setIsLoading(false);
+      }
     }
   };
   fetchStatus();
+  return () => abortController.abort();
 }, []);

- Add global setting to enable/disable CCR routing
- Auto-detect CCR installation and server status
- Route Claude Agent SDK requests through CCR when enabled
- Add UI toggle with status indicator in settings
- Optimize with async checks and caching
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/src/routes/worktree/routes/generate-commit-message.ts (1)

201-213: ⚠️ Potential issue | 🟠 Major

Add resolveModelString() to resolve model aliases before executeQuery.

The code path where resolvePhaseModel() has a providerId set returns the model name unchanged without calling resolveModelString(). When combined with stripProviderPrefix(), which only removes prefixes and doesn't resolve aliases, an unresolved alias like "sonnet" could be passed directly to executeQuery(). Add the import and call resolveModelString(bareModel) to ensure full resolution before the API call.

Proposed fix
-import { resolvePhaseModel } from '@automaker/model-resolver';
+import { resolvePhaseModel, resolveModelString } from '@automaker/model-resolver';
@@
 const bareModel = stripProviderPrefix(model);
+const resolvedModel = resolveModelString(bareModel);
@@
-  model: bareModel,
+  model: resolvedModel,
🧹 Nitpick comments (8)
apps/server/src/routes/github/routes/validate-issue.ts (1)

192-194: Redundant getGlobalSettings() call.

settingsService.getGlobalSettings() is already called at line 158 to retrieve phaseModels. Consider reusing that result instead of making a second async call.

♻️ Suggested refactor to consolidate settings retrieval
     // Use request overrides if provided, otherwise fall back to settings
     let effectiveThinkingLevel: ThinkingLevel | undefined = thinkingLevel;
     let effectiveReasoningEffort: ReasoningEffort | undefined = reasoningEffort;
+    let globalSettings: Awaited<ReturnType<typeof settingsService.getGlobalSettings>> | null = null;
     if (!effectiveThinkingLevel || !effectiveReasoningEffort) {
-      const settings = await settingsService?.getGlobalSettings();
+      globalSettings = (await settingsService?.getGlobalSettings()) ?? null;
       const phaseModelEntry =
-        settings?.phaseModels?.validationModel || DEFAULT_PHASE_MODELS.validationModel;
+        globalSettings?.phaseModels?.validationModel || DEFAULT_PHASE_MODELS.validationModel;
       const resolved = resolvePhaseModel(phaseModelEntry);
       if (!effectiveThinkingLevel) {
         effectiveThinkingLevel = resolved.thinkingLevel;
       }
       if (!effectiveReasoningEffort && typeof phaseModelEntry !== 'string') {
         effectiveReasoningEffort = phaseModelEntry.reasoningEffort;
       }
+    } else if (settingsService) {
+      globalSettings = await settingsService.getGlobalSettings();
     }

     // ... provider resolution code ...

-    // Get CCR setting from global settings
-    const globalSettings = settingsService ? await settingsService.getGlobalSettings() : null;
     const ccrEnabled = globalSettings?.ccrEnabled ?? false;
apps/server/src/routes/suggestions/generate-suggestions.ts (1)

260-278: Optional simplification: Consider using settingsService auto-resolution.

The ccrEnabled propagation is correct. However, streamingQuery accepts an optional settingsService parameter that can auto-resolve ccrEnabled from globalSettings.ccrEnabled internally.

If the logging on line 236 is not critical, you could simplify by passing settingsService to streamingQuery:

Suggested refactoring
  const result = await streamingQuery({
    prompt: finalPrompt,
    model,
    cwd: projectPath,
    ...
-   ccrEnabled, // Enable Claude Code Router for API routing
+   settingsService, // Auto-resolves ccrEnabled from globalSettings
    ...
  });

This eliminates the manual getGlobalSettings() call on line 227. However, if you prefer explicit logging for observability, the current approach is equally valid.

apps/server/src/routes/features/routes/generate-title.ts (1)

64-84: Consider using settingsService auto-resolution instead of manual CCR flag extraction.

The simpleQuery function now supports a settingsService parameter that auto-resolves ccrEnabled from global settings when not explicitly provided. This could simplify the code:

♻️ Suggested simplification
-      // Get credentials and global settings for API calls (uses hardcoded haiku model, no phase setting)
       const credentials = await settingsService?.getCredentials();
-      const globalSettings = settingsService ? await settingsService.getGlobalSettings() : null;
-      const ccrEnabled = globalSettings?.ccrEnabled ?? false;
-
-      if (ccrEnabled) {
-        logger.info('CCR routing enabled');
-      }

       const userPrompt = `Generate a concise title for this feature:\n\n${trimmedDescription}`;

       // Use simpleQuery - provider abstraction handles all the streaming/extraction
       const result = await simpleQuery({
         prompt: `${systemPrompt}\n\n${userPrompt}`,
         model: CLAUDE_MODEL_MAP.haiku,
         cwd: process.cwd(),
         maxTurns: 1,
         allowedTools: [],
         credentials, // Pass credentials for resolving 'credentials' apiKeySource
-        ccrEnabled, // Enable Claude Code Router for API routing
+        settingsService, // Auto-resolves ccrEnabled from global settings
       });
apps/server/src/providers/simple-query-service.ts (1)

138-143: Duplicated CCR auto-resolution logic between simpleQuery and streamingQuery.

The auto-resolution logic is identical in both functions. Consider extracting to a helper:

♻️ Suggested helper extraction
// Helper function at module level
async function resolveCcrEnabled(
  ccrEnabled: boolean | undefined,
  settingsService?: { getGlobalSettings: () => Promise<{ ccrEnabled?: boolean } | null> }
): Promise<boolean> {
  if (ccrEnabled !== undefined) {
    return ccrEnabled;
  }
  if (settingsService) {
    const globalSettings = await settingsService.getGlobalSettings();
    return globalSettings?.ccrEnabled ?? false;
  }
  return false;
}

Then use in both functions:

const resolvedCcrEnabled = await resolveCcrEnabled(options.ccrEnabled, options.settingsService);

Also applies to: 231-236

apps/server/src/routes/enhance-prompt/routes/enhance.ts (1)

153-176: Logging level inconsistency and opportunity to use auto-resolution.

Two observations:

  1. Logging inconsistency: This file uses logger.debug for "CCR routing enabled" (line 159) while other files like generate-title.ts use logger.info. Consider standardizing across the codebase.

  2. Auto-resolution: Same as other routes, this could use the settingsService parameter to auto-resolve ccrEnabled.

♻️ Suggested simplification
-      // Get CCR setting from global settings
-      const globalSettings = settingsService ? await settingsService.getGlobalSettings() : null;
-      const ccrEnabled = globalSettings?.ccrEnabled ?? false;
-
       logger.debug(`Using model: ${resolvedModel}`);
-      if (ccrEnabled) {
-        logger.debug('CCR routing enabled');
-      }

       // Use simpleQuery - provider abstraction handles routing to correct provider
       const result = await simpleQuery({
         prompt: `${systemPrompt}\n\n${userPrompt}`,
         model: resolvedModel,
         cwd: process.cwd(),
         maxTurns: 1,
         allowedTools: [],
         thinkingLevel,
         readOnly: true,
         credentials,
         claudeCompatibleProvider,
-        ccrEnabled,
+        settingsService,
       });
apps/server/src/services/ideation-service.ts (1)

241-245: Consider a helper method for CCR flag resolution.

The same pattern for resolving ccrEnabled from global settings appears twice in this class (lines 241-245 and 710-715). Since this.settingsService is already available as a class member, consider adding a private helper method:

♻️ Suggested helper method
private async getCcrEnabled(): Promise<boolean> {
  if (!this.settingsService) return false;
  const globalSettings = await this.settingsService.getGlobalSettings();
  return globalSettings?.ccrEnabled ?? false;
}

Then simplify both call sites:

const ccrEnabled = await this.getCcrEnabled();

Also applies to: 710-715

apps/server/src/routes/app-spec/generate-spec.ts (1)

118-161: LGTM with suggestion to use auto-resolution.

The CCR integration is correctly implemented. The streamingQuery function also supports the settingsService parameter for auto-resolution, which could simplify this code similarly to other routes.

apps/server/src/routes/worktree/routes/generate-commit-message.ts (1)

173-183: Move CCR enablement resolution into a service layer.

This route now contains additional CCR business logic; consider delegating CCR enablement resolution to a service to keep routes thin and consistent.

As per coding guidelines, Server business logic should be organized into services in the services/ directory, with Express route handlers in routes/ that delegate to services.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/src/services/auto-mode-service.ts (1)

3436-3480: ⚠️ Potential issue | 🟡 Minor

Guard CCR lookup so settings failures don’t abort execution.
getGlobalSettings() failures will now fail the agent even though CCR is optional. Consider a defensive fallback to keep the run alive.

Suggested adjustment
-    const credentials = await this.settingsService?.getCredentials();
-    const globalSettings = await this.settingsService?.getGlobalSettings();
-    const ccrEnabled = globalSettings?.ccrEnabled ?? false;
+    const credentials = await this.settingsService?.getCredentials();
+    let ccrEnabled = false;
+    try {
+      const globalSettings = await this.settingsService?.getGlobalSettings();
+      ccrEnabled = globalSettings?.ccrEnabled ?? false;
+    } catch (error) {
+      logger.warn('Failed to load global settings for CCR; defaulting to disabled.', error);
+    }
🤖 Fix all issues with AI agents
In `@apps/server/src/providers/claude-provider.ts`:
- Around line 257-258: The log is using the raw ccrEnabled override instead of
the resolved value built by buildEnv; call buildEnv(...) once, capture its
return into a local (e.g., env or resolvedEnv), then use that
resolvedEnv.ccrEnabled (or equivalent property) for all logging and later logic
instead of the incoming ccrEnabled, and pass the same resolvedEnv to downstream
calls; update the log statements around where buildEnv is invoked (and the other
similar blocks referenced) to read the effective CCR flag from the resolved env
rather than the override.
🧹 Nitpick comments (2)
apps/server/src/lib/ccr.ts (1)

149-161: Consider documenting the expected CCR authentication header.

The environment variable ANTHROPIC_AUTH_TOKEN is set (line 153), but the typical Claude SDK uses ANTHROPIC_API_KEY. If CCR expects a different header name, this is correct; otherwise, consider aligning with the standard naming or adding a comment explaining the CCR-specific convention.

apps/server/src/lib/global-settings-accessor.ts (1)

44-46: Guard against accidental re-initialization of the accessor.
If init is called twice (tests, hot reload), the provider can be silently replaced. Consider a small guard or explicit reset.

Proposed tweak
 export function initGlobalSettingsAccessor(provider: GlobalSettingsProvider): void {
+  if (settingsProvider && settingsProvider !== provider) {
+    throw new Error('Global settings accessor already initialized');
+  }
   settingsProvider = provider;
 }

Replaced manual ccrEnabled propagation with a global settings accessor pattern.
This allows ClaudeProvider to automatically resolve CCR settings without
requiring every call site to pass the setting manually.

- Added apps/server/src/lib/global-settings-accessor.ts
- Initialized accessor in server startup (index.ts)
- Updated ClaudeProvider to use auto-resolved CCR setting
- Removed redundant ccrEnabled parameter passing from API routes

This ensures all future Claude Agent SDK usage automatically supports CCR
without additional boilerplate code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@wcpaxx wcpaxx force-pushed the support-ccr branch 2 times, most recently from c01c17b to c95cbc6 Compare February 4, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant