feat: add proxy support for network-restricted environments - #144
feat: add proxy support for network-restricted environments#144evergood2025 wants to merge 1 commit into
Conversation
WalkthroughProxy support was added to the fetch operations in both the Changes
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Summary of Changes
Hello @yonghao2011, 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 primarily introduces robust proxy support to the ccusage tool, enabling it to function correctly in network-restricted environments by automatically detecting and utilizing proxy settings for fetching essential pricing data. This resolves issues where users in certain regions could not fetch data without workarounds. Additionally, it includes a substantial cleanup and simplification of the project's package.json file.
Highlights
- Network Proxy Support: Implemented automatic proxy detection using
http_proxy,https_proxy,HTTP_PROXY, andHTTPS_PROXYenvironment variables to enableccusageto fetch pricing data in network-restricted environments. - Undici ProxyAgent Integration: Integrated
undici'sProxyAgentfor allfetchrequests that require proxying, ensuring proper routing of network traffic for external data retrieval. - Build-Time Proxy Support: Extended proxy functionality to the build process, specifically for pre-fetching Claude pricing data via
src/_macro.ts, resolving potential build failures in restricted networks. - Debugging & Observability: Added informative logging to
src/pricing-fetcher.tsto indicate when a proxy is detected and being used, aiding in troubleshooting network connectivity issues. - Project Configuration Cleanup: Performed a significant cleanup and reduction of the
package.jsonfile, removing various development-related scripts, dependencies, and configurations, streamlining the project setup.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| 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 issue 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 is currently in preview and 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 to provide feedback.
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
-
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. ↩
There was a problem hiding this comment.
Code Review
This pull request adds proxy support for fetching pricing data, which enhances the tool for users in network-restricted environments. The implementation uses environment variables and undici's ProxyAgent. The package.json file has been unintentionally stripped of essential development configuration, which needs to be reverted. The proxy detection and setup logic is duplicated across two files, which should be refactored into a shared utility function.
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | ||
| process.env.http_proxy || process.env.HTTP_PROXY; | ||
|
|
||
| const fetchOptions: RequestInit = {}; | ||
| if (proxyUrl) { | ||
| // For Node.js with undici, we need to use ProxyAgent | ||
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | ||
| } |
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | ||
| process.env.http_proxy || process.env.HTTP_PROXY; | ||
|
|
||
| const fetchOptions: RequestInit = {}; | ||
| if (proxyUrl) { | ||
| // For Node.js with undici, we need to use ProxyAgent | ||
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | ||
| logger.info(`Using proxy: ${proxyUrl}`); | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/pricing-fetcher.ts (1)
66-78: Consider adding tests for proxy functionality.The proxy support functionality is not covered by the existing test cases. Consider adding tests to verify:
- Proxy detection from environment variables
- ProxyAgent usage when proxy is configured
- Fallback behavior when no proxy is configured
Example test structure:
describe('proxy support', () => { it('should use proxy when environment variable is set', async () => { process.env.https_proxy = 'http://proxy.example.com:8080'; // Test implementation }); it('should work without proxy when no environment variable is set', async () => { delete process.env.https_proxy; // Test implementation }); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between bffbdc8 and 35306217bc62ddc5c64663b0d79c3429c51bb144.
📒 Files selected for processing (3)
package.json(1 hunks)src/_macro.ts(1 hunks)src/pricing-fetcher.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/_macro.ts (1)
src/_consts.ts (1)
LITELLM_PRICING_URL(7-8)
src/pricing-fetcher.ts (2)
src/logger.ts (1)
logger(18-18)src/_consts.ts (1)
LITELLM_PRICING_URL(7-8)
🔇 Additional comments (1)
package.json (1)
1-34: LGTM! Package.json simplified appropriately for published package.The removal of all development configurations (workspaces, scripts, devDependencies, etc.) is appropriate for a published npm package, focusing solely on essential package metadata. This simplification aligns well with the proxy support feature and improves package clarity.
| // Check for proxy configuration and create fetch options | ||
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | ||
| process.env.http_proxy || process.env.HTTP_PROXY; | ||
|
|
||
| const fetchOptions: RequestInit = {}; | ||
| if (proxyUrl) { | ||
| // For Node.js with undici, we need to use ProxyAgent | ||
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Proxy implementation is correct but consider refactoring for reusability.
The proxy detection and ProxyAgent setup logic is implemented correctly. However, this exact same code pattern is duplicated in src/pricing-fetcher.ts (lines 66-76), violating the DRY principle.
Consider extracting this logic into a shared utility function:
// src/proxy-utils.ts
export async function createFetchOptionsWithProxy(): Promise<RequestInit> {
const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY ||
process.env.http_proxy || process.env.HTTP_PROXY;
const fetchOptions: RequestInit = {};
if (proxyUrl) {
const { ProxyAgent } = await import('undici');
(fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl);
}
return fetchOptions;
}Then use it in both files:
const fetchOptions = await createFetchOptionsWithProxy();🤖 Prompt for AI Agents
In src/_macro.ts lines 18 to 27, the proxy detection and ProxyAgent setup code
is duplicated in src/pricing-fetcher.ts lines 66 to 76. To fix this, extract
this logic into a new shared async utility function (e.g.,
createFetchOptionsWithProxy) in a new file like src/proxy-utils.ts that returns
the fetch options with proxy if set. Then replace the duplicated code in both
src/_macro.ts and src/pricing-fetcher.ts with calls to this new utility function
to adhere to the DRY principle.
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for the dynamic import.
The dynamic import of ProxyAgent should include error handling in case the undici package is not available. Currently, if the import fails, it will throw an unhandled error.
- const { ProxyAgent } = await import('undici');
- (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl);
+ try {
+ const { ProxyAgent } = await import('undici');
+ (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl);
+ } catch (error) {
+ throw new Error(`Failed to load proxy support. Please install undici: ${error.message}`);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { ProxyAgent } = await import('undici'); | |
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | |
| try { | |
| const { ProxyAgent } = await import('undici'); | |
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | |
| } catch (error) { | |
| throw new Error(`Failed to load proxy support. Please install undici: ${error.message}`); | |
| } |
🤖 Prompt for AI Agents
In src/_macro.ts around lines 25 to 26, the dynamic import of ProxyAgent from
'undici' lacks error handling, which can cause unhandled exceptions if the
package is unavailable. Wrap the import statement in a try-catch block to catch
any errors during import, and handle the error gracefully, such as logging the
error or providing a fallback, to prevent the application from crashing.
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for the dynamic import (same as in _macro.ts).
The same error handling concern applies here as mentioned for src/_macro.ts. The dynamic import should be wrapped in try-catch to provide a meaningful error message if undici is not available.
🤖 Prompt for AI Agents
In src/pricing-fetcher.ts around lines 73 to 74, the dynamic import of 'undici'
lacks error handling. Wrap the import statement in a try-catch block to catch
any errors if the module is unavailable, and log or throw a meaningful error
message to aid debugging, similar to the approach used in src/_macro.ts.
| // Check for proxy configuration and create fetch options | ||
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | ||
| process.env.http_proxy || process.env.HTTP_PROXY; | ||
|
|
||
| const fetchOptions: RequestInit = {}; | ||
| if (proxyUrl) { | ||
| // For Node.js with undici, we need to use ProxyAgent | ||
| const { ProxyAgent } = await import('undici'); | ||
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | ||
| logger.info(`Using proxy: ${proxyUrl}`); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Address code duplication and security concern with proxy URL logging.
This proxy implementation duplicates the exact same logic from src/_macro.ts (lines 18-27). As mentioned in the previous file review, this should be extracted into a shared utility function.
Additionally, logging the proxy URL could expose sensitive information if the URL contains credentials (e.g., http://username:[email protected]:8080).
Apply this diff to avoid logging sensitive proxy credentials:
- logger.info(`Using proxy: ${proxyUrl}`);
+ const proxyHost = new URL(proxyUrl).host;
+ logger.info(`Using proxy: ${proxyHost}`);And consider the same utility function approach suggested for src/_macro.ts to eliminate duplication.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Check for proxy configuration and create fetch options | |
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | |
| process.env.http_proxy || process.env.HTTP_PROXY; | |
| const fetchOptions: RequestInit = {}; | |
| if (proxyUrl) { | |
| // For Node.js with undici, we need to use ProxyAgent | |
| const { ProxyAgent } = await import('undici'); | |
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | |
| logger.info(`Using proxy: ${proxyUrl}`); | |
| } | |
| // Check for proxy configuration and create fetch options | |
| const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || | |
| process.env.http_proxy || process.env.HTTP_PROXY; | |
| const fetchOptions: RequestInit = {}; | |
| if (proxyUrl) { | |
| // For Node.js with undici, we need to use ProxyAgent | |
| const { ProxyAgent } = await import('undici'); | |
| (fetchOptions as any).dispatcher = new ProxyAgent(proxyUrl); | |
| const proxyHost = new URL(proxyUrl).host; | |
| logger.info(`Using proxy: ${proxyHost}`); | |
| } |
🤖 Prompt for AI Agents
In src/pricing-fetcher.ts around lines 66 to 76, the proxy URL detection and
fetch options setup duplicates logic from src/_macro.ts and logs the full proxy
URL, risking exposure of sensitive credentials. Refactor by extracting the proxy
URL retrieval and fetch options creation into a shared utility function used by
both files. Also, modify the logging to avoid printing the full proxy URL;
instead, log a sanitized version that omits credentials or just indicate that a
proxy is used without revealing the URL.
- Add automatic proxy detection for http_proxy/https_proxy environment variables - Use undici ProxyAgent for fetch requests when proxy is configured - Support proxy for both runtime pricing fetches and build-time macro execution - Fix issue where npx ccusage@latest fails in regions with GitHub access restrictions - Add proxy usage logging for better debugging Fixes network connectivity issues in environments like mainland China where direct access to GitHub raw content may be blocked or slow. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/_macro.ts (1)
18-27: Code duplication and missing error handling already identified.The proxy detection logic and missing error handling for the dynamic import have already been flagged in previous reviews. Please refer to the existing comments about extracting this to a shared utility function and adding try-catch around the undici import.
src/pricing-fetcher.ts (1)
66-76: Code duplication, security concern, and missing error handling already identified.The proxy detection logic duplication, security issue with logging the full proxy URL (which may contain credentials), and missing error handling for the dynamic import have already been flagged in previous reviews. Please refer to the existing comments about these issues.
🧹 Nitpick comments (2)
src/_macro.ts (1)
19-20: Fix formatting and indentation issues.The code has several formatting problems that should be addressed:
- Mixed spaces and tabs for indentation
- Trailing spaces on lines
- Inconsistent operator placement
Apply this diff to fix the formatting issues:
- const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || - process.env.http_proxy || process.env.HTTP_PROXY; + const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY + || process.env.http_proxy || process.env.HTTP_PROXY;src/pricing-fetcher.ts (1)
67-68: Fix formatting and indentation issues.Similar to the other file, this code has formatting problems:
- Mixed spaces and tabs for indentation
- Trailing spaces on lines
- Inconsistent operator placement
Apply this diff to fix the formatting issues:
- const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || - process.env.http_proxy || process.env.HTTP_PROXY; + const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY + || process.env.http_proxy || process.env.HTTP_PROXY;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 35306217bc62ddc5c64663b0d79c3429c51bb144 and 913d975.
📒 Files selected for processing (2)
src/_macro.ts(1 hunks)src/pricing-fetcher.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/pricing-fetcher.ts (2)
src/logger.ts (1)
logger(18-18)src/_consts.ts (1)
LITELLM_PRICING_URL(7-8)
🪛 ESLint
src/_macro.ts
[error] 19-20: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 19-19: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 19-19: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 19-19: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 19-19: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 19-19: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 19-19: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 19-19: '||' should be placed at the beginning of the line.
(style/operator-linebreak)
[error] 19-19: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 20-20: Expected indentation of 2 tabs
(style/indent-binary-ops)
[error] 20-20: Mixed spaces and tabs.
(style/no-mixed-spaces-and-tabs)
[error] 20-20: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 20-20: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 20-20: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 20-20: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 20-20: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 21-21: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 23-23: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 25-25: Unsafe assignment of an any value.
(ts/no-unsafe-assignment)
[error] 26-26: Unsafe assignment of an any value.
(ts/no-unsafe-assignment)
[error] 26-26: Unsafe member access .dispatcher on an any value.
(ts/no-unsafe-member-access)
[error] 26-26: Unsafe construction of a(n) any typed value.
(ts/no-unsafe-call)
[error] 28-28: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 29-29: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 29-29: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
src/pricing-fetcher.ts
[error] 65-65: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 67-68: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 67-67: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 67-67: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 67-67: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 67-67: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 67-67: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 67-67: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 67-67: '||' should be placed at the beginning of the line.
(style/operator-linebreak)
[error] 67-67: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 68-68: Expected indentation of 4 tabs
(style/indent-binary-ops)
[error] 68-68: Mixed spaces and tabs.
(style/no-mixed-spaces-and-tabs)
[error] 68-68: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 68-68: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 68-68: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 68-68: Unexpected use of the global variable 'process'. Use 'require("process")' instead.
(node/prefer-global/process)
[error] 68-68: Unsafe member access .env on an error typed value.
(ts/no-unsafe-member-access)
[error] 69-69: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 71-71: Unexpected any value in conditional. An explicit comparison or type conversion is required.
(ts/strict-boolean-expressions)
[error] 73-73: Unsafe assignment of an any value.
(ts/no-unsafe-assignment)
[error] 74-74: Unsafe assignment of an any value.
(ts/no-unsafe-assignment)
[error] 74-74: Unsafe member access .dispatcher on an any value.
(ts/no-unsafe-member-access)
[error] 74-74: Unsafe construction of a(n) any typed value.
(ts/no-unsafe-call)
[error] 75-75: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 75-75: Unsafe member access .info on an error typed value.
(ts/no-unsafe-member-access)
[error] 77-77: Trailing spaces not allowed.
(style/no-trailing-spaces)
[error] 78-78: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 78-78: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
|
we discuss it before. |
Hi, I've tried adding the --offline parameter, but it doesn't work as expected. If my usage is incorrect, please let me know. I tested with my modified code (bunx ccusage-test) and it can successfully get the local proxy and use the proxy to access LiteLLM. Problem: Node.js My PR solution: Before making network requests, detect common proxy environment variables (http_proxy, https_proxy, etc.). If proxy configuration is detected, use The core issue is Node.js fetch()'s limitation - it doesn't automatically respect system proxy settings like other network tools (curl, browsers, etc.) do. My PR essentially fills this gap by adding the proxy support that fetch() should have had by default. |
|
@yonghao2011 Thank you. |
|
@yonghao2011 Also this is a good idea to use offline mode automatically if fetch fails. |
|
which offline mode doesn't work |
|
Implemented auto-offline mode |
|
|
Is it enough? Or do we need to consider proxy feature? |
|
For network-restricted regions (like mainland China), the current solution will 100% fall back to cached/stale data. If proxy support is available, getting real-time data is naturally the better approach. Using undici is a solid choice worth considering, but the tool is definitely usable now as-is. |
|
@yonghao2011 I'm really care about the bundle size actually. so how about adding a option to change the url. |
|
@yonghao2011 |
5f6191a to
8c2c97d
Compare


Summary
This PR adds automatic proxy support to resolve network connectivity issues in environments with restricted access to GitHub (such as mainland China).
Changes Made
http_proxy,https_proxy,HTTP_PROXY, andHTTPS_PROXYenvironment variablesFiles Modified
src/pricing-fetcher.ts: Added proxy detection and ProxyAgent usage in ensurePricingLoaded()src/_macro.ts: Added proxy support for build-time pricing data fetchingProblem Solved
Fixes the issue where
npx ccusage@latestfails in network-restricted environments due to inability to fetch pricing data from GitHub raw content. Users previously had to use--offlinemode or clone the repository locally to work around this limitation.Testing
Tested successfully with:
[email protected]via npx with proxy configurationUsage
No changes required for existing users. The tool will automatically detect and use proxy settings from environment variables when available.
Example:
🤖 Generated with Claude Code
Co-Authored-By: Claude [email protected]
Summary by CodeRabbit
New Features
Chores