refactor(debug): replace ConsolePatcher with debugLogger and update error reporting

- Replace ConsolePatcher with centralized debugLogger utility
- Refactor errorReporting to use debugLogger instead of file-based reporting
- Remove user-facing console message components:
  - Delete ConsolePatcher.ts, useConsoleMessages.ts/hook
  - Delete ConsoleSummaryDisplay.tsx, DetailedMessagesDisplay.tsx
- Update all tests in packages/core and packages/cli:
  - Mock debugLogger where needed
  - Remove assertions for console output on non-critical errors
  - Keep debugLogger assertions for fatal/network errors
  - Use HOME directory mocking for hermetic file system tests

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
tanzhenxin 2026-02-02 17:37:54 +08:00
parent 135df54f27
commit 89e3c2cd7a
64 changed files with 1240 additions and 2416 deletions

View file

@ -66,7 +66,6 @@ describe('runNonInteractive', () => {
let mockToolRegistry: ToolRegistry;
let mockCoreExecuteToolCall: Mock;
let mockShutdownTelemetry: Mock;
let consoleErrorSpy: MockInstance;
let processStdoutSpy: MockInstance;
let processStderrSpy: MockInstance;
let mockGeminiClient: {
@ -83,7 +82,6 @@ describe('runNonInteractive', () => {
getCommands: mockGetCommands,
});
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
processStdoutSpy = vi
.spyOn(process.stdout, 'write')
.mockImplementation(() => true);
@ -360,9 +358,6 @@ describe('runNonInteractive', () => {
.mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
.mockReturnValueOnce(createStreamFromEvents(finalResponse));
// Enable debug mode so handleToolError logs to console.error
(mockConfig.getDebugMode as Mock).mockReturnValue(true);
await runNonInteractive(
mockConfig,
mockSettings,
@ -371,9 +366,6 @@ describe('runNonInteractive', () => {
);
expect(mockCoreExecuteToolCall).toHaveBeenCalled();
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool errorTool: Execution failed',
);
expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(
2,
@ -443,9 +435,6 @@ describe('runNonInteractive', () => {
.mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
.mockReturnValueOnce(createStreamFromEvents(finalResponse));
// Enable debug mode so handleToolError logs to console.error
(mockConfig.getDebugMode as Mock).mockReturnValue(true);
await runNonInteractive(
mockConfig,
mockSettings,
@ -454,9 +443,6 @@ describe('runNonInteractive', () => {
);
expect(mockCoreExecuteToolCall).toHaveBeenCalled();
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool nonexistentTool: Tool "nonexistentTool" not found in registry.',
);
expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
expect(processStdoutSpy).toHaveBeenCalledWith(
"Sorry, I can't find that tool.",
@ -738,11 +724,6 @@ describe('runNonInteractive', () => {
throw testError;
});
// Mock console.error to capture JSON error output
const consoleErrorJsonSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
let thrownError: Error | null = null;
try {
await runNonInteractive(
@ -760,19 +741,18 @@ describe('runNonInteractive', () => {
// Should throw because of mocked process.exit
expect(thrownError?.message).toBe('process.exit(1) called');
expect(consoleErrorJsonSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'Error',
message: 'Invalid input provided',
code: 1,
},
const jsonError = JSON.stringify(
{
error: {
type: 'Error',
message: 'Invalid input provided',
code: 1,
},
null,
2,
),
},
null,
2,
);
expect(processStderrSpy).toHaveBeenCalledWith(`${jsonError}\n`);
});
it('should handle API errors in text mode and exit with error code', async () => {
@ -830,11 +810,6 @@ describe('runNonInteractive', () => {
throw fatalError;
});
// Mock console.error to capture JSON error output
const consoleErrorJsonSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
let thrownError: Error | null = null;
try {
await runNonInteractive(
@ -852,19 +827,18 @@ describe('runNonInteractive', () => {
// Should throw because of mocked process.exit with custom exit code
expect(thrownError?.message).toBe('process.exit(42) called');
expect(consoleErrorJsonSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalInputError',
message: 'Invalid command syntax provided',
code: 42,
},
const jsonError = JSON.stringify(
{
error: {
type: 'FatalInputError',
message: 'Invalid command syntax provided',
code: 42,
},
null,
2,
),
},
null,
2,
);
expect(processStderrSpy).toHaveBeenCalledWith(`${jsonError}\n`);
});
it('should execute a slash command that returns a prompt', async () => {