feat(FN-3815): quiet stderr logging for clean-exit MCP processes in pi-clau
Adds a changeset for FN-3815. The core change reclassifies clean-exit stderr output in the pi-claude-cli provider to reduce noisy logging, with corresponding test coverage distinguishing between clean and non-zero exit stderr handling. Fusion-Task-Id: FN-3815
This commit is contained in:
7
.changeset/fn-3815-quiet-pi-mcp-loading.md
Normal file
7
.changeset/fn-3815-quiet-pi-mcp-loading.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Quiet benign Claude Code CLI stderr on clean shutdown by routing it to debug-only logs in `pi-claude-cli`.
|
||||
|
||||
This prevents MCP loading/initialization lines from surfacing as warning/error-level entries in the TUI Logs tab when Claude exits cleanly, while preserving warning/error surfacing for non-zero Claude CLI exits and authentication-related failures.
|
||||
@@ -1188,28 +1188,42 @@ describe("streamViaCli", { timeout: 90_000 }, () => {
|
||||
expect(doneEvent.message.content).toBeDefined();
|
||||
});
|
||||
|
||||
it("logs stderr at warn level on close even with exit code 0", async () => {
|
||||
const model = mockModels[0] as any;
|
||||
const context = {
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
};
|
||||
it.each([
|
||||
{ code: 0, shouldWarn: false },
|
||||
{ code: 42, shouldWarn: true },
|
||||
])(
|
||||
"FN-3815: routes close stderr logging by exit code=$code",
|
||||
async ({ code, shouldWarn }) => {
|
||||
const model = mockModels[0] as any;
|
||||
const context = {
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
};
|
||||
|
||||
const warnSpy = vi.spyOn(console, "warn");
|
||||
const warnSpy = vi.spyOn(console, "warn");
|
||||
|
||||
streamViaCli(model, context);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
streamViaCli(model, context);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
const proc = (spawn as any).mock.results[0].value;
|
||||
const proc = (spawn as any).mock.results[0].value;
|
||||
|
||||
proc.stderr.emit("data", Buffer.from("minor warning from cli"));
|
||||
proc.emit("close", 0, null);
|
||||
proc.stdout.end();
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
proc.stderr.emit("data", Buffer.from("minor warning from cli"));
|
||||
proc.emit("close", code, null);
|
||||
proc.stdout.end();
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("minor warning from cli"),
|
||||
);
|
||||
});
|
||||
const stderrWarnCalls = warnSpy.mock.calls.filter(([message]) =>
|
||||
typeof message === "string" &&
|
||||
message.includes("[pi-claude-cli] Claude CLI stderr on close:"),
|
||||
);
|
||||
|
||||
if (shouldWarn) {
|
||||
expect(stderrWarnCalls).toHaveLength(1);
|
||||
expect(stderrWarnCalls[0]?.[0]).toContain("minor warning from cli");
|
||||
} else {
|
||||
expect(stderrWarnCalls).toHaveLength(0);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("warns when subprocess closes successfully with no content events", async () => {
|
||||
const model = mockModels[0] as any;
|
||||
|
||||
@@ -252,7 +252,13 @@ export function streamViaCli(
|
||||
if (broken) return; // Break-early kill, expected
|
||||
const stderr = getStderr().trim();
|
||||
if (stderr) {
|
||||
console.warn(`[pi-claude-cli] Claude CLI stderr on close: ${stderr}`);
|
||||
if (code === 0 || code === null) {
|
||||
// FN-3815: Claude CLI writes benign MCP bring-up diagnostics to stderr
|
||||
// on clean/abort shutdown; keep these debug-only to avoid false TUI warnings.
|
||||
debugLog(`Claude CLI stderr on close (clean exit): ${stderr}`);
|
||||
} else {
|
||||
console.warn(`[pi-claude-cli] Claude CLI stderr on close: ${stderr}`);
|
||||
}
|
||||
}
|
||||
if (code !== 0 && code !== null) {
|
||||
const message = stderr
|
||||
|
||||
Reference in New Issue
Block a user