Skip to content

Commit 112643e

Browse files
authored
Merge pull request #50 from link-assistant/issue-49-bef878bb5d38
Fix: Suppress debug output in CLI commands unless --verbose is used
2 parents 9c639d9 + 4e37f56 commit 112643e

8 files changed

Lines changed: 481 additions & 16 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@link-assistant/agent': patch
3+
---
4+
5+
Fix debug output appearing in CLI commands - logs are now suppressed by default and only shown with --verbose flag. This fixes the issue where commands like `agent auth list` displayed debug messages that broke the clean CLI UI.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"author": {
3+
"id": "MDQ6VXNlcjE0MzE5MDQ=",
4+
"is_bot": false,
5+
"login": "konard",
6+
"name": "Konstantin Diachenko"
7+
},
8+
"body": "```\nkonard@MacBook-Pro-Konstantin ~ % agent auth list \n\n┌ Credentials ~/.local/share/opencode/auth.json\nINFO 2025-12-16T12:43:13 +38ms service=models.dev file={} refreshing\n\n● Anthropic oauth\n\n└ 1 credentials\n\nkonard@MacBook-Pro-Konstantin ~ % \n```\n\nDebug output detected:\n\n```\nINFO 2025-12-16T12:43:13 +38ms service=models.dev file={} refreshing\n```\n\nThe debug output should only be shown when --verbose option is provided.\n\nPlease make sure we double check all similar places for all types of CLI input for `agent` command.\n\nPlease download all logs and data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), in which we will reconstruct timeline/sequence of events, find root causes of the problem, and propose possible solutions.",
9+
"comments": [],
10+
"createdAt": "2025-12-16T12:46:08Z",
11+
"labels": [
12+
{
13+
"id": "LA_kwDOQYTy3M8AAAACQHoi-w",
14+
"name": "bug",
15+
"description": "Something isn't working",
16+
"color": "d73a4a"
17+
}
18+
],
19+
"number": 49,
20+
"state": "OPEN",
21+
"title": "By default no there in the CLI we should have not have any debug output that breaks CLI UI",
22+
"updatedAt": "2025-12-16T12:46:08Z"
23+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"author": {
3+
"id": "MDQ6VXNlcjE0MzE5MDQ=",
4+
"is_bot": false,
5+
"login": "konard",
6+
"name": "Konstantin Diachenko"
7+
},
8+
"baseRefName": "main",
9+
"body": "## 🤖 AI-Powered Solution Draft\n\nThis pull request is being automatically generated to solve issue #49.\n\n### 📋 Issue Reference\nFixes #49\n\n### 🚧 Status\n**Work in Progress** - The AI assistant is currently analyzing and implementing the solution draft.\n\n### 📝 Implementation Details\n_Details will be added as the solution draft is developed..._\n\n---\n*This PR was created automatically by the AI issue solver*",
10+
"createdAt": "2025-12-16T12:49:42Z",
11+
"headRefName": "issue-49-bef878bb5d38",
12+
"number": 50,
13+
"state": "OPEN",
14+
"title": "[WIP] By default no there in the CLI we should have not have any debug output that breaks CLI UI",
15+
"updatedAt": "2025-12-16T12:49:43Z"
16+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# CLI Debug Logging Best Practices Research
2+
3+
## Sources
4+
5+
### Stderr vs Stdout Separation
6+
7+
- [Configuring CLI output verbosity with logging and argparse](https://xahteiwi.eu/resources/hints-and-kinks/python-cli-logging-options/)
8+
- [The CLI's Essential '—Verbose' Option - Dojo Five](https://dojofive.com/blog/the-clis-essential-verbose-option/)
9+
10+
### Verbose Flag Implementation
11+
12+
- [Verbose Logging](https://medium.com/@pooja_virani/verbose-logging-a60cccb3eb66)
13+
- [Go Flags: Beyond the Basics](https://dzone.com/articles/go-flags-beyond-the-basics)
14+
15+
### Debug Output and User Experience
16+
17+
- [Debugging - Better CLI](https://bettercli.org/design/debugging/)
18+
- [Mastering CLI Design: Best Practices](https://jsschools.com/programming/mastering-cli-design-best-practices-for-powerful-/)
19+
20+
## Key Best Practices
21+
22+
### 1. Stderr vs Stdout Separation
23+
24+
Log output that tells users about what the program is doing should go to stderr, while output related to the program's results goes to stdout, giving users the ability to pipe stdout without interference.
25+
26+
### 2. Verbose Flag Implementation
27+
28+
By default, CLIs should log only warnings and errors to stderr, but when debugging issues, enable more verbose logging by passing a --verbose flag. Common patterns include:
29+
30+
- Using `-v` for INFO level and `-vv` for DEBUG level
31+
- Verbose logging with `-v` flag, with log levels between 1 and 11 for increasing verbosity
32+
33+
### 3. Default Behavior
34+
35+
Events of severity WARNING and greater should be printed to sys.stderr by default, which is regarded as the best default behavior.
36+
37+
### 4. Quiet Mode
38+
39+
Also define a `-q` or `--quiet` option that suppresses warnings and shows only errors.
40+
41+
### 5. Output Guidelines
42+
43+
Keep reporting short and sweet, especially on successful runs - only mention important parts, and be descriptive but short.
44+
45+
### 6. Debug Mode for Programs
46+
47+
Debug log messages from your program are hidden by default, but can be shown using the `-d` or `--debug` flag.
48+
49+
### 7. Standard Debug Flags
50+
51+
CLI tools commonly provide flags like -d/--debug, -v/--verbose, DEBUG environment variable or similar debugging options, and these flags should be implemented as global options, meaning they could be used in any command and in any combination.
52+
53+
### 8. Conditional Debug Output for Better UX
54+
55+
When handling errors in CLI tools, you can conditionally show detailed information: if debug mode is enabled, display full traceback information; otherwise, show a simple error message and suggest running with --debug for details. This approach prevents overwhelming users with technical output during normal operation while still making debugging information accessible when needed.
56+
57+
### 9. Impact on User Experience
58+
59+
How your CLI presents information significantly impacts user experience. Debug output can create challenges:
60+
61+
- **Console flooding**: Debug messages can overload the console, making it difficult to use the CLI effectively
62+
- **Performance impact**: Enabling debugging can disrupt operation when systems are experiencing high load conditions
63+
- **Usability issues**: Without proper controls, debug output can make tools difficult to use in production environments
64+
65+
### 10. Recommendations
66+
67+
The key is to keep debug output disabled by default and provide explicit flags for users who need it, ensuring that regular users enjoy a clean, focused experience while developers and troubleshooters can access detailed information when necessary.
68+
69+
## Application to Agent CLI
70+
71+
Based on these best practices, the Agent CLI should:
72+
73+
1. **Suppress all INFO/DEBUG logs by default** - Only show warnings and errors
74+
2. **Enable verbose logging with --verbose flag** - Show INFO/DEBUG when explicitly requested
75+
3. **Initialize logging early** - Before any commands execute, initialize the logging system
76+
4. **Use log files for background operations** - Write non-critical logs to files instead of stderr
77+
5. **Keep CLI output clean** - Only show user-relevant information by default
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Root Cause Analysis: Debug Output Breaking CLI UI
2+
3+
## Issue Summary
4+
5+
When running `agent auth list`, the command outputs debug logging information that breaks the CLI user interface:
6+
7+
```
8+
INFO 2025-12-16T12:43:13 +38ms service=models.dev file={} refreshing
9+
```
10+
11+
This debug output should only be shown when the `--verbose` flag is provided.
12+
13+
## Root Cause Analysis
14+
15+
### The Problem Chain
16+
17+
1. **Default Logging Behavior (src/util/log.ts:55)**
18+
19+
```typescript
20+
let write = (msg: any) => Bun.stderr.write(msg);
21+
```
22+
23+
- By default, all log messages write directly to `stderr`
24+
- This happens **before** `Log.init()` is called
25+
- The default log level is `INFO` (line 19)
26+
27+
2. **Missing Log Initialization for CLI Commands**
28+
- **Agent mode** (src/index.js:182-185): Properly calls `Log.init()`
29+
- **Run command** (src/cli/cmd/run.ts:115): Properly calls `Log.init()`
30+
- **Auth commands**: **NEVER** calls `Log.init()`
31+
- **Other CLI commands (mcp, models, stats, export, etc.)**: Also don't call `Log.init()`
32+
33+
3. **Early Logger Creation (src/provider/models.ts:8)**
34+
35+
```typescript
36+
const log = Log.create({ service: 'models.dev' });
37+
```
38+
39+
- Logger is created at module import time
40+
- Uses default `write` function pointing to `stderr`
41+
- Any calls to `log.info()` will output to stderr by default
42+
43+
4. **Auth List Command Flow**
44+
45+
**Call Chain:**
46+
47+
```
48+
user runs: agent auth list
49+
50+
src/index.js:519 → AuthCommand is invoked
51+
52+
src/cli/cmd/auth.ts:26 → AuthListCommand.handler()
53+
54+
src/cli/cmd/auth.ts:35 → ModelsDev.get() is called
55+
56+
src/provider/models.ts:71 → refresh() is called
57+
58+
src/provider/models.ts:81 → log.info('refreshing', { file })
59+
60+
PROBLEM: write() still points to Bun.stderr.write()
61+
62+
Debug output appears in CLI: "INFO 2025-12-16T12:43:13 +38ms service=models.dev file={} refreshing"
63+
```
64+
65+
5. **Why Log.init() Matters**
66+
67+
When `Log.init({ print: false })` is called (src/util/log.ts:57-75):
68+
69+
```typescript
70+
export async function init(options: Options) {
71+
if (options.level) level = options.level;
72+
cleanup(Global.Path.log);
73+
if (options.print) return; // If print=true, keep using stderr
74+
75+
// Create log file path
76+
logpath = path.join(Global.Path.log, ...);
77+
const logfile = Bun.file(logpath);
78+
const writer = logfile.writer();
79+
80+
// REDIRECT write function to log file instead of stderr
81+
write = async (msg: any) => {
82+
const num = writer.write(msg);
83+
writer.flush();
84+
return num;
85+
};
86+
}
87+
```
88+
89+
**Before Log.init()**: `write``Bun.stderr.write()` ✗ visible in CLI
90+
**After Log.init({ print: false })**: `write``logfile.writer()` ✓ hidden in log file
91+
92+
### Timeline of Events
93+
94+
1. **User runs command**: `agent auth list`
95+
2. **Module imports happen**:
96+
- `src/provider/models.ts` is imported
97+
- Line 8: `const log = Log.create({ service: 'models.dev' })` executes
98+
- Logger is created with default `write = Bun.stderr.write`
99+
3. **yargs parses command**: Identifies `auth list` subcommand
100+
4. **Command handler executes**: `AuthListCommand.handler()` at line 26
101+
5. **Models are fetched**: Line 35 calls `ModelsDev.get()`
102+
6. **Refresh is triggered**: Line 71 calls `refresh()`
103+
7. **Log output occurs**: Line 81 calls `log.info('refreshing', { file })`
104+
8. **Output goes to stderr**: Because `Log.init()` was never called
105+
9. **User sees debug output**: Breaking the clean CLI UI
106+
107+
### Additional Issues
108+
109+
**Background Refresh Timer (src/provider/models.ts:98)**
110+
111+
```typescript
112+
setInterval(() => ModelsDev.refresh(), 60 * 1000 * 60).unref();
113+
```
114+
115+
This timer runs every 60 minutes in the background and will also output to stderr if `Log.init()` hasn't been called, potentially interrupting long-running processes or piped output.
116+
117+
## Affected Commands
118+
119+
Based on analysis, the following CLI commands are affected (they don't initialize logging):
120+
121+
1. **auth** commands:
122+
- `agent auth list`
123+
- `agent auth login`
124+
- `agent auth logout`
125+
- `agent auth status`
126+
127+
2. **mcp** commands (src/cli/cmd/mcp.ts):
128+
- `agent mcp list`
129+
- `agent mcp install`
130+
- Any logging in MCP operations will leak to stderr
131+
132+
3. **models** commands (src/cli/cmd/models.ts):
133+
- Any model-related operations ✗
134+
135+
4. **stats** commands (src/cli/cmd/stats.ts):
136+
- Any stats operations ✗
137+
138+
5. **export** commands (src/cli/cmd/export.ts):
139+
- Any export operations ✗
140+
141+
**Not Affected:**
142+
143+
- `agent` (default mode) ✓ - Calls Log.init() at line 182
144+
- `agent run` ✓ - Calls Log.init() at line 115 (when verbose)
145+
146+
## Why This Violates Best Practices
147+
148+
According to CLI best practices research:
149+
150+
1. **Default Behavior**: CLIs should only show warnings and errors by default
151+
2. **Verbose Flag**: INFO/DEBUG logs should only appear with `--verbose` flag
152+
3. **Clean Output**: Regular users should enjoy a clean, focused experience
153+
4. **Stderr vs Stdout**: Debug output mixing with command output disrupts piping and scripting
154+
155+
The current behavior violates all of these principles for CLI commands that don't initialize logging.
156+
157+
## Solution Requirements
158+
159+
To fix this issue, we need to:
160+
161+
1. **Initialize logging early** for ALL CLI commands, not just agent mode
162+
2. **Respect the verbose flag** - Only print to stderr when `--verbose` is used
163+
3. **Default to file logging** - Write logs to files by default (not stderr)
164+
4. **Ensure consistency** - All commands should have the same logging behavior
165+
5. **Maintain backward compatibility** - Don't break existing verbose flag behavior
166+
167+
## Proposed Solution
168+
169+
Add a global middleware or initialization in `src/index.js` that:
170+
171+
- Calls `Log.init({ print: false })` before any commands execute
172+
- Allows `--verbose` flag to enable `Log.init({ print: true, level: 'DEBUG' })`
173+
- Applies to ALL commands, not just agent mode
174+
175+
This ensures:
176+
177+
- By default: logs go to files in `~/.local/share/opencode/log/`
178+
- With `--verbose`: logs print to stderr for debugging
179+
- Clean CLI UI for all commands without debug pollution

0 commit comments

Comments
 (0)