Skip to content

Commit 646f564

Browse files
yamork779claude
andcommitted
feat(stderr-debug): add logging utilities and enhance CLI execution error handling
Add comprehensive logging infrastructure through new logger utility module to improve debugging visibility: - Create logger.ts with log(), logError(), logCommand(), and output channel management - Add command execution logging before running CLI processes - Log both stderr and stdout output from CLI execution (first 500 chars of stdout) - Enhanced error handling with detailed stderr/stdout information in error messages - Add "Show Logs" action to error dialogs to help users access debugging information - Replace console.log with logger.log() in extension activation - Update error type annotations to include stderr and stdout properties Improvements enable better troubleshooting of CLI execution failures while maintaining extension reliability. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8bfc3bc commit 646f564

3 files changed

Lines changed: 112 additions & 8 deletions

File tree

‎src/cli/execution.ts‎

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as path from "path";
66
import * as os from "os";
77
import { findClaudeCliPath } from "./detection";
88
import { ProgressCallback, Model } from "../types";
9+
import { log, logError, logCommand } from "../utils/logger";
910

1011
const execAsync = promisify(exec);
1112

@@ -43,12 +44,21 @@ export async function generateWithCLI(
4344
? `type "${promptFile}" | ${escapedCliPath} -p --model ${model}`
4445
: `cat "${promptFile}" | ${escapedCliPath} -p --model ${model}`;
4546

47+
logCommand(command);
48+
4649
const { stdout, stderr } = await execAsync(command, {
4750
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash",
4851
maxBuffer: 10 * 1024 * 1024,
4952
timeout: 120000,
5053
});
5154

55+
if (stderr) {
56+
log(`CLI stderr: ${stderr.trim()}`);
57+
}
58+
if (stdout) {
59+
log(`CLI stdout (first 500 chars): ${stdout.substring(0, 500)}`);
60+
}
61+
5262
if (stderr && !stdout) {
5363
throw new Error(`CLI error output: ${stderr.trim()}`);
5464
}
@@ -91,7 +101,7 @@ export async function generateWithCLI(
91101

92102
return lines[lines.length - 1] || "chore: update code";
93103
} catch (error) {
94-
const err = error as NodeJS.ErrnoException & { killed?: boolean };
104+
const err = error as NodeJS.ErrnoException & { killed?: boolean; stderr?: string; stdout?: string };
95105
if (err.killed) {
96106
throw new Error(
97107
"CLI process timed out after 2 minutes. Try a smaller diff or check your connection."
@@ -100,7 +110,21 @@ export async function generateWithCLI(
100110
if (err.code === "ENOENT") {
101111
throw new Error(`CLI executable not found at: ${cliPath}`);
102112
}
103-
throw error;
113+
// Provide detailed error information for debugging
114+
const stderr = err.stderr?.trim() || "";
115+
const stdout = err.stdout?.trim() || "";
116+
const details: string[] = [];
117+
if (stderr) {
118+
details.push(`stderr: ${stderr}`);
119+
}
120+
if (stdout) {
121+
details.push(`stdout: ${stdout}`);
122+
}
123+
const baseMessage = err.message || String(error);
124+
const detailStr = details.length > 0 ? ` [${details.join("; ")}]` : "";
125+
const fullError = `CLI execution failed: ${baseMessage}${detailStr}`;
126+
logError(fullError, error);
127+
throw new Error(fullError);
104128
} finally {
105129
try {
106130
await fs.promises.unlink(promptFile);
@@ -144,20 +168,29 @@ export async function generateWithCLIManaged(
144168
? `type "${promptFile}" | ${escapedCliPath} -p --model haiku`
145169
: `cat "${promptFile}" | ${escapedCliPath} -p --model haiku`;
146170

171+
logCommand(command);
172+
147173
const { stdout, stderr } = await execAsync(command, {
148174
shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash",
149175
maxBuffer: 10 * 1024 * 1024,
150176
timeout: 120000,
151177
cwd: repoPath,
152178
});
153179

180+
if (stderr) {
181+
log(`CLI stderr: ${stderr.trim()}`);
182+
}
183+
if (stdout) {
184+
log(`CLI stdout (first 500 chars): ${stdout.substring(0, 500)}`);
185+
}
186+
154187
if (stderr && !stdout) {
155188
throw new Error(`CLI error output: ${stderr.trim()}`);
156189
}
157190

158191
return stdout.trim() || "chore: update code";
159192
} catch (error) {
160-
const err = error as NodeJS.ErrnoException & { killed?: boolean };
193+
const err = error as NodeJS.ErrnoException & { killed?: boolean; stderr?: string; stdout?: string };
161194
if (err.killed) {
162195
throw new Error(
163196
"CLI process timed out after 2 minutes. Try a smaller diff or check your connection."
@@ -166,7 +199,21 @@ export async function generateWithCLIManaged(
166199
if (err.code === "ENOENT") {
167200
throw new Error(`CLI executable not found at: ${cliPath}`);
168201
}
169-
throw error;
202+
// Provide detailed error information for debugging
203+
const stderr = err.stderr?.trim() || "";
204+
const stdout = err.stdout?.trim() || "";
205+
const details: string[] = [];
206+
if (stderr) {
207+
details.push(`stderr: ${stderr}`);
208+
}
209+
if (stdout) {
210+
details.push(`stdout: ${stdout}`);
211+
}
212+
const baseMessage = err.message || String(error);
213+
const detailStr = details.length > 0 ? ` [${details.join("; ")}]` : "";
214+
const fullError = `CLI execution failed: ${baseMessage}${detailStr}`;
215+
logError(fullError, error);
216+
throw new Error(fullError);
170217
} finally {
171218
try {
172219
await fs.promises.unlink(promptFile);

‎src/extension.ts‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode";
22
import { generateCommitMessage, editCommitMessage } from "./generators/commit";
33
import type { GitRepository, GitAPI, Language } from "./types";
4+
import { log, logError, showOutputChannel, disposeOutputChannel } from "./utils/logger";
45

56
/**
67
* Show an information message that auto-closes after a specified timeout.
@@ -105,7 +106,7 @@ function getActiveRepository(
105106
}
106107

107108
export function activate(context: vscode.ExtensionContext): void {
108-
console.log("Claude Commit extension is now active");
109+
log("Claude Commit extension activated");
109110

110111
const generateCommit = vscode.commands.registerCommand(
111112
"claude-commit.generate",
@@ -180,9 +181,14 @@ export function activate(context: vscode.ExtensionContext): void {
180181
const errorMessage = generationError instanceof Error
181182
? generationError.message
182183
: String(generationError);
183-
vscode.window.showErrorMessage(
184-
`Failed to generate commit: ${errorMessage}`
184+
logError("Failed to generate commit", generationError);
185+
const action = await vscode.window.showErrorMessage(
186+
`Failed to generate commit: ${errorMessage}`,
187+
"Show Logs"
185188
);
189+
if (action === "Show Logs") {
190+
showOutputChannel();
191+
}
186192
return;
187193
}
188194

@@ -401,4 +407,6 @@ async function handleEditWithFeedback(
401407
);
402408
}
403409

404-
export function deactivate(): void {}
410+
export function deactivate(): void {
411+
disposeOutputChannel();
412+
}

‎src/utils/logger.ts‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as vscode from "vscode";
2+
3+
let outputChannel: vscode.OutputChannel | undefined;
4+
5+
export function getOutputChannel(): vscode.OutputChannel {
6+
if (!outputChannel) {
7+
outputChannel = vscode.window.createOutputChannel("Claude Commit");
8+
}
9+
return outputChannel;
10+
}
11+
12+
export function log(message: string): void {
13+
const channel = getOutputChannel();
14+
const timestamp = new Date().toISOString();
15+
channel.appendLine(`[${timestamp}] ${message}`);
16+
}
17+
18+
export function logError(message: string, error?: unknown): void {
19+
const channel = getOutputChannel();
20+
const timestamp = new Date().toISOString();
21+
channel.appendLine(`[${timestamp}] ERROR: ${message}`);
22+
if (error) {
23+
if (error instanceof Error) {
24+
channel.appendLine(` Message: ${error.message}`);
25+
if (error.stack) {
26+
channel.appendLine(` Stack: ${error.stack}`);
27+
}
28+
} else {
29+
channel.appendLine(` Details: ${String(error)}`);
30+
}
31+
}
32+
}
33+
34+
export function logCommand(command: string): void {
35+
const channel = getOutputChannel();
36+
const timestamp = new Date().toISOString();
37+
channel.appendLine(`[${timestamp}] Executing: ${command}`);
38+
}
39+
40+
export function showOutputChannel(): void {
41+
getOutputChannel().show();
42+
}
43+
44+
export function disposeOutputChannel(): void {
45+
if (outputChannel) {
46+
outputChannel.dispose();
47+
outputChannel = undefined;
48+
}
49+
}

0 commit comments

Comments
 (0)