-
Notifications
You must be signed in to change notification settings - Fork 1
feat(terminal): add quiet mode support for Claude Code environments #1409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Detect CLAUDECODE, REPL_ID, or AGENT env vars to suppress info/debug output - Warnings and errors still displayed in quiet mode - Enhanced note() to accept Error objects and automatically include stack traces
- Update spinner error handler to use note() with Error objects - Update CLI error handler to use note() with Error objects - Remove manual redBright() calls as note() handles colors automatically
Summary of ChangesHello @roderik, 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 introduces a 'quiet mode' for the CLI, designed to optimize the user experience within automated environments like Claude Code by reducing verbose output and focusing on critical information. Concurrently, it refines the Highlights
Using Gemini Code AssistThe 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
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 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
|
|
To view in Slack, search for: 1762518186.598049 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `sdk/utils/src/terminal/note.ts:48-51` </location>
<code_context>
+ }
+
+ const maskedMessage = maskTokens(messageText);
+ const _isQuietMode = process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT;
+
+ // Always print warnings and errors, even in quiet mode
</code_context>
<issue_to_address>
**suggestion:** Unused variable '_isQuietMode' can be removed.
Since '_isQuietMode' is not referenced elsewhere, please remove it to avoid confusion.
```suggestion
const maskedMessage = maskTokens(messageText);
// Always print warnings and errors, even in quiet mode
```
</issue_to_address>
### Comment 2
<location> `sdk/utils/src/terminal/note.ts:33` </location>
<code_context>
*/
-export const note = (message: string, level: "info" | "warn" = "info"): void => {
- if (!shouldPrint()) {
+export const note = (message: string | Error, level: "info" | "warn" | "error" = "info"): void => {
+ let messageText: string;
+ let _error: Error | undefined;
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring the note function by extracting colorization, quiet-mode logic, and message preparation into separate helper functions to simplify control flow and reduce duplication.
```suggestion
// 1) Extract colorizing logic to remove duplication:
function colorize(msg: string, level: "info" | "warn" | "error"): string {
if (msg.includes("\u001b[")) return msg
if (level === "warn") return yellowBright(msg)
if (level === "error") return redBright(msg)
return msg
}
// 2) Centralize “quiet‐mode” decision:
function canPrint(level: "info" | "warn" | "error"): boolean {
const isQuiet = !!(process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT)
// warnings/errors always print, infos respect shouldPrint()
return level !== "info" ? true : shouldPrint()
}
// 3) Pull out message preparation (masking + stack for Errors):
function prepareMessage(value: string | Error, level: "info" | "warn" | "error"): string {
let text = typeof value === "string"
? value
: `${value.message}${level === "error" && value.stack ? `\n\n${value.stack}` : ""}`
return maskTokens(text)
}
// 4) Simplified note():
export const note = (
input: string | Error,
level: "info" | "warn" | "error" = "info"
): void => {
if (!canPrint(level)) return
const msg = prepareMessage(input, level)
console.log("")
if (level === "warn") console.warn(colorize(msg, level))
else if (level === "error") console.error(colorize(msg, level))
else console.log(msg)
}
```
This removes nested branches, centralizes environment checks, colorization, and message formatting—keeping all existing behavior intact.
</issue_to_address>
### Comment 3
<location> `sdk/utils/src/terminal/note.ts:33` </location>
<code_context>
*/
-export const note = (message: string, level: "info" | "warn" = "info"): void => {
- if (!shouldPrint()) {
+export const note = (message: string | Error, level: "info" | "warn" | "error" = "info"): void => {
+ let messageText: string;
+ let _error: Error | undefined;
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The expanded signature now accepts both string and Error, but the JSDoc above does not document this change or clarify expected behavior for Error input.
Consider updating the JSDoc to explicitly mention that the function accepts both string and Error types for the message parameter, and describe how Error objects are handled (e.g., stack trace inclusion for errors). This will improve clarity for future maintainers.
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.ts,**/*.tsx,**/*.sol`
**Instructions:**
Code readability, maintainability, and clarity: Ensure function signatures and overloads are clear and documented, especially when expanding accepted types.
</details>
</issue_to_address>
### Comment 4
<location> `sdk/utils/src/terminal/note.ts:49` </location>
<code_context>
+ }
+
+ const maskedMessage = maskTokens(messageText);
+ const _isQuietMode = process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT;
+
+ // Always print warnings and errors, even in quiet mode
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The _isQuietMode variable is assigned but never used, which may confuse maintainers.
If _isQuietMode is not needed, please remove it. If it is intended for future use, add a comment explaining its purpose.
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.ts,**/*.tsx,**/*.sol`
**Instructions:**
Code readability and maintainability: Avoid unused variables and dead code.
</details>
</issue_to_address>
### Comment 5
<location> `sdk/utils/src/terminal/note.ts:54` </location>
<code_context>
+ // Always print warnings and errors, even in quiet mode
+ if (level === "warn" || level === "error") {
+ console.log("");
+ if (level === "warn") {
+ // Apply yellow color if not already colored (check if message contains ANSI codes)
+ const coloredMessage = maskedMessage.includes("\u001b[") ? maskedMessage : yellowBright(maskedMessage);
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The color application logic for warnings and errors is duplicated; consider refactoring to reduce repetition.
You could extract the color-checking and application logic into a small helper function to DRY up the code and improve maintainability.
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.ts,**/*.tsx,**/*.sol`
**Instructions:**
Code readability and maintainability: Avoid duplicating logic for color application; consider extracting repeated color-checking logic into a helper function.
</details>
</issue_to_address>
### Comment 6
<location> `sdk/utils/src/terminal/should-print.ts:8` </location>
<code_context>
*/
export function shouldPrint() {
- return process.env.SETTLEMINT_DISABLE_TERMINAL !== "true";
+ if (process.env.SETTLEMINT_DISABLE_TERMINAL === "true") {
+ return false;
+ }
</code_context>
<issue_to_address>
**suggestion (review_instructions):** The function now checks multiple environment variables to determine quiet mode, but the JSDoc could be clearer about the precedence and logic.
Consider updating the JSDoc to clarify the logic and precedence of environment variable checks for terminal output suppression.
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.ts,**/*.tsx,**/*.sol`
**Instructions:**
Code readability and maintainability: Ensure environment variable checks are consistent and documented.
</details>
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
📦 Packages
|
There was a problem hiding this 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 quiet mode for Claude Code environments and enhances the note function for better error handling. The changes are well-structured and achieve the stated goals. I've provided a couple of suggestions to remove some unused variables and redundant code, which will help improve maintainability.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughThe PR extends terminal behavior and quiet-mode handling: Possibly related PRs
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
Add test coverage for quiet mode functionality in executeCommand: - Verify output is suppressed on success in quiet mode - Verify output is shown on error in quiet mode - Verify silent: false override works in quiet mode - Test all quiet mode triggers (CLAUDECODE, REPL_ID, AGENT) All 15 tests pass with 100% coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this 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
🧹 Nitpick comments (3)
sdk/utils/src/terminal/execute-command.ts (2)
32-37: Consider extracting quiet mode detection to a shared utility.The quiet mode detection logic (
CLAUDECODE || REPL_ID || AGENT) appears to be duplicated from the logic inshould-print.ts. To maintain consistency and avoid divergence, consider extracting this into a shared utility function that both files can import.For example, create a shared helper:
// sdk/utils/src/terminal/is-quiet-mode.ts export function isQuietMode(): boolean { return !!(process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT); }Then import it in both
execute-command.tsandshould-print.ts.
66-68: Consider simplifying the output suppression logic for clarity.The nested ternary
quietMode ? (silent !== false) : !!silentis correct but could be more readable. Consider using explicit conditionals:const quietMode = isQuietMode(); - // In quiet mode, suppress output unless explicitly overridden with silent: false - const shouldSuppressOutput = quietMode ? (silent !== false) : !!silent; + // In quiet mode, suppress output unless explicitly overridden with silent: false + const shouldSuppressOutput = quietMode + ? silent !== false // In quiet mode: suppress unless explicitly set to false + : silent === true; // Normal mode: suppress only if explicitly trueAlternatively, using if/else:
let shouldSuppressOutput: boolean; if (quietMode) { // In quiet mode, suppress unless explicitly disabled shouldSuppressOutput = silent !== false; } else { // In normal mode, respect the silent flag shouldSuppressOutput = silent === true; }sdk/utils/src/terminal/execute-command.test.ts (1)
113-247: Consider extracting test helpers to reduce duplication.The five new quiet mode tests (lines 113-247) follow nearly identical patterns with significant code duplication. Consider extracting helper functions to improve maintainability:
// Helper for testing with environment variable async function withEnvVar<TResult>( envVar: string, value: string, fn: () => Promise<TResult>, ): Promise<TResult> { const original = process.env[envVar]; try { process.env[envVar] = value; return await fn(); } finally { if (original === undefined) { delete process.env[envVar]; } else { process.env[envVar] = original; } } } // Helper for tracking stdout writes function trackStdoutWrites(): { wasCalled: () => boolean; restore: () => void } { const originalWrite = process.stdout.write; let called = false; process.stdout.write = mock((_chunk: any) => { called = true; return true; }); return { wasCalled: () => called, restore: () => { process.stdout.write = originalWrite; }, }; }Then simplify tests:
test("quiet mode suppresses output on success", async () => { await withEnvVar("CLAUDECODE", "true", async () => { const tracker = trackStdoutWrites(); try { await executeCommand("echo", ["quiet mode test"]); expect(tracker.wasCalled()).toBe(false); } finally { tracker.restore(); } }); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
sdk/utils/src/terminal/execute-command.test.ts(1 hunks)sdk/utils/src/terminal/execute-command.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Do not use default exports in TypeScript (except when a framework requires it)
Useimport typefor type-only imports in TypeScript
Prefer interfaces over type aliases for object shapes
Never useany; useunknownor proper types instead
Use discriminated unions for error handling
File names should be kebab-case
Use camelCase for variable and function names
Use PascalCase for types, interfaces, and classes
Use UPPER_SNAKE_CASE for constants
Prefer nullish coalescing (??) over logical OR (||)
Use early returns to reduce nesting
Extract complex logic into well-named functions
Keep functions small and focused
Use structured logging with proper context
**/*.{ts,tsx}: Inside generic functions, using any for concise type narrowing is acceptable; outside of generics, use any extremely sparingly
Avoid default exports unless explicitly required by the framework (e.g., Next.js pages)
Use discriminated unions to model variant data and avoid 'bag of optionals'
Handle discriminated unions with switch statements over the discriminant
Do not introduce new enums; prefer as const objects for enum-like behavior; retain existing enums
Use top-level import type when importing types instead of inline import { type ... }
Prefer interface extends over intersection types (&) for modeling inheritance; use & only when extends is not possible
Use JSDoc comments to annotate functions and types when behavior isn’t obvious; keep comments concise
Use JSDoc inline @link to reference related functions and types within the same file
File names should be kebab-case (e.g., my-component.ts)
Use camelCase for variables and function names
Use UpperCamelCase (PascalCase) for classes, types, and interfaces
Use ALL_CAPS for constants (and enum values where enums already exist)
Prefix generic type parameters with T (e.g., TKey, TValue)
Be mindful of noUncheckedIndexedAccess: treat indexed object/array access as possibly undefined
Use optional properties sparingly; prefer required prop...
Files:
sdk/utils/src/terminal/execute-command.test.tssdk/utils/src/terminal/execute-command.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{test,spec}.{ts,tsx}: Write unit tests using Vitest
Mock external API calls in tests
Test error scenarios explicitly
Files:
sdk/utils/src/terminal/execute-command.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/bun.mdc)
Use
Bun.File()for file reading and writing
Files:
sdk/utils/src/terminal/execute-command.test.tssdk/utils/src/terminal/execute-command.ts
🧠 Learnings (8)
📚 Learning: 2025-10-06T23:17:22.726Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:22.726Z
Learning: Applies to test/**/*.ts : Write end-to-end tests for CLI commands
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:29.429Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:29.429Z
Learning: Applies to test/**/*.{ts,tsx} : Create end-to-end tests for CLI commands
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:27.585Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:27.585Z
Learning: Applies to test/** : Create end-to-end tests for CLI commands
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:29.429Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:29.429Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Test error scenarios explicitly
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:27.585Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:27.585Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Test error scenarios
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:27.585Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:27.585Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use test fixtures for consistency
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:20:01.574Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: .cursor/rules/solidity.mdc:0-0
Timestamp: 2025-10-06T23:20:01.574Z
Learning: Applies to test/**/*.sol : Test both happy paths and failure cases
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
📚 Learning: 2025-10-06T23:17:27.585Z
Learnt from: CR
Repo: settlemint/sdk PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-06T23:17:27.585Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Write unit tests using Vitest
Applied to files:
sdk/utils/src/terminal/execute-command.test.ts
🧬 Code graph analysis (2)
sdk/utils/src/terminal/execute-command.test.ts (1)
sdk/utils/src/terminal/execute-command.ts (1)
executeCommand(60-123)
sdk/utils/src/terminal/execute-command.ts (1)
sdk/utils/src/logging/mask-tokens.ts (1)
maskTokens(13-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Sourcery review
- GitHub Check: QA
🔇 Additional comments (3)
sdk/utils/src/terminal/execute-command.ts (2)
73-74: Good design: preserving stdout/stderr separation.The separate tracking of
stdoutOutputandstderrOutputis well-designed, allowing the error replay to correctly route output to the appropriate streams. This maintains proper stream semantics even when buffering output.Also applies to: 83-83, 91-91
94-105: LGTM: Error output replay logic is sound.The
showErrorOutput()helper correctly:
- Gates replay to quiet mode with suppressed output
- Preserves stream separation by writing stdout to stdout and stderr to stderr
- Only runs when there's actual output to show
One minor consideration: if a command produces very large output before failing, all buffered output will be replayed at once. This is likely acceptable for error scenarios where users need full context, but be aware of potential memory/console flooding with long-running commands.
sdk/utils/src/terminal/execute-command.test.ts (1)
113-172: Excellent test coverage for quiet mode behavior.The new tests comprehensively cover quiet mode scenarios:
- Output suppression on success (line 113)
- Output replay on error (line 138) - correctly mocks both stdout and stderr
- Override behavior with
silent: false(line 174)- All three environment variables: CLAUDECODE, REPL_ID, AGENT
The error scenario test (lines 138-172) properly verifies that buffered output is shown when commands fail, which is critical for debugging in quiet mode.
Based on learnings: Test error scenarios explicitly.
- Adjusted inputs and outputs for various tasks in turbo.json to enhance build efficiency. - Ensured consistent logging settings across tasks with "outputLogs": "new-only". - Updated dependencies for code generation and documentation tasks to include new schema and example paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues found across 5 files
- Remove unused `_isQuietMode` and `_error` variables - Extract helper functions to reduce complexity: - `colorize()` for ANSI color application - `canPrint()` for level-based filtering - `prepareMessage()` for Error handling and token masking - Enhance JSDoc documentation: - Document Error parameter behavior with examples - Clarify environment variable precedence in shouldPrint() - Add detailed descriptions for helper functions - Simplify control flow with early returns - Maintain backward compatibility All tests passing (108/108), no linting errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The `note(error, "error")` function already masks tokens through the `prepareMessage()` helper, making the `maskTokens()` call in `handleError()` redundant. - Remove redundant `maskTokens()` call in `handleError()` - Remove unused `maskTokens` import - Pass `error.message` directly to `SpinnerError` All tests passing (108/108), no linting errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Introduced a clearer mechanism for output suppression in quiet mode, ensuring that output is only displayed when explicitly allowed. - Updated the handling of the `silent` option to improve clarity and maintainability. All tests passing, no linting errors.
What
Adds quiet mode support to the CLI that automatically suppresses info/debug/status messages when running in Claude Code environments (detected via
CLAUDECODE,REPL_ID, orAGENTenvironment variables). Warnings and errors are still displayed to ensure critical information remains visible.Also enhances the
note()function to accept Error objects directly and automatically include stack traces for error-level messages, simplifying error handling throughout the codebase.Why
When the CLI is used in automated environments like Claude Code, the verbose output (ASCII art, status messages, spinner progress) creates noise and makes it harder to identify actual issues. This change provides a cleaner output while preserving visibility of warnings and errors.
The enhanced
note()API also reduces boilerplate code by automatically handling error formatting (colors, stack traces) that was previously done manually.How
shouldPrint()to check forCLAUDECODE,REPL_ID, orAGENTenvironment variablesstring | Erroras the first parameter"error"spinner.tsandindex.tsto use the enhancednote()API, removing manual color formatting and stack trace concatenationFiles Changed
sdk/utils/src/terminal/should-print.ts- Added quiet mode detectionsdk/utils/src/terminal/note.ts- Enhanced to accept Error objects and auto-include stack tracessdk/utils/src/terminal/spinner.ts- Simplified error handlingsdk/cli/src/commands/index.ts- Simplified error handlingsdk/utils/src/environment/write-env.test.ts- Formatting updatesBreaking Changes
None. The changes are backward compatible - existing code using
note()with string messages continues to work, and the new Error object support is additive.Related Linear Issues
None
Summary by Sourcery
Add quiet mode support for automated Claude Code environments and enhance error reporting by extending the note() API and simplifying error handling.
New Features:
Enhancements:
Tests:
Note
Adds quiet mode (CLAUDECODE/REPL_ID/AGENT) that suppresses non-critical output, enhances note() to handle Errors with stacks/colors, updates executeCommand/spinner/CLI error paths, and tunes Turbo logs.
shouldPrint()detectsCLAUDECODE/REPL_ID/AGENTto suppress info output.executeCommand()suppresses output in quiet mode (overridable withsilent: false) and reveals buffered stdout/stderr on errors.note()now acceptsError, auto-includes stack forerrorlevel, applies colors, and always prints warnings/errors.note(error, "error")and throwsSpinnerError.note(error, "error"); removes manual masking/coloring.execute-command; minor formatting cleanups in env tests.turbo.json: setoutputLogs: "new-only"across tasks.Written by Cursor Bugbot for commit 05ca0d6. This will update automatically on new commits. Configure here.