11import { Command } from "commander" ;
22import { afterAll , afterEach , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
3+ import { loggingState } from "../../logging/state.js" ;
34import { setCommandJsonMode } from "./json-mode.js" ;
45
56const setVerboseMock = vi . fn ( ) ;
@@ -58,6 +59,7 @@ let originalProcessArgv: string[];
5859let originalProcessTitle : string ;
5960let originalNodeNoWarnings : string | undefined ;
6061let originalHideBanner : string | undefined ;
62+ let originalForceStderr : boolean ;
6163
6264beforeAll ( async ( ) => {
6365 ( { registerPreActionHooks } = await import ( "./preaction.js" ) ) ;
@@ -76,13 +78,16 @@ beforeEach(() => {
7678 originalProcessTitle = process . title ;
7779 originalNodeNoWarnings = process . env . NODE_NO_WARNINGS ;
7880 originalHideBanner = process . env . OPENCLAW_HIDE_BANNER ;
81+ originalForceStderr = loggingState . forceConsoleToStderr ;
82+ loggingState . forceConsoleToStderr = false ;
7983 delete process . env . NODE_NO_WARNINGS ;
8084 delete process . env . OPENCLAW_HIDE_BANNER ;
8185} ) ;
8286
8387afterEach ( ( ) => {
8488 process . argv = originalProcessArgv ;
8589 process . title = originalProcessTitle ;
90+ loggingState . forceConsoleToStderr = originalForceStderr ;
8691 if ( originalNodeNoWarnings === undefined ) {
8792 delete process . env . NODE_NO_WARNINGS ;
8893 } else {
@@ -340,6 +345,39 @@ describe("registerPreActionHooks", () => {
340345 expect ( ensureConfigReadyMock ) . not . toHaveBeenCalled ( ) ;
341346 } ) ;
342347
348+ it ( "routes logs to stderr during plugin loading in --json mode and restores after" , async ( ) => {
349+ let stderrDuringPluginLoad = false ;
350+ ensurePluginRegistryLoadedMock . mockImplementation ( ( ) => {
351+ stderrDuringPluginLoad = loggingState . forceConsoleToStderr ;
352+ } ) ;
353+
354+ await runPreAction ( {
355+ parseArgv : [ "agents" ] ,
356+ processArgv : [ "node" , "openclaw" , "agents" , "--json" ] ,
357+ } ) ;
358+
359+ expect ( ensurePluginRegistryLoadedMock ) . toHaveBeenCalled ( ) ;
360+ expect ( stderrDuringPluginLoad ) . toBe ( true ) ;
361+ // Flag must be restored after plugin loading completes
362+ expect ( loggingState . forceConsoleToStderr ) . toBe ( false ) ;
363+ } ) ;
364+
365+ it ( "does not route logs to stderr during plugin loading without --json" , async ( ) => {
366+ let stderrDuringPluginLoad = false ;
367+ ensurePluginRegistryLoadedMock . mockImplementation ( ( ) => {
368+ stderrDuringPluginLoad = loggingState . forceConsoleToStderr ;
369+ } ) ;
370+
371+ await runPreAction ( {
372+ parseArgv : [ "agents" ] ,
373+ processArgv : [ "node" , "openclaw" , "agents" ] ,
374+ } ) ;
375+
376+ expect ( ensurePluginRegistryLoadedMock ) . toHaveBeenCalled ( ) ;
377+ expect ( stderrDuringPluginLoad ) . toBe ( false ) ;
378+ expect ( loggingState . forceConsoleToStderr ) . toBe ( false ) ;
379+ } ) ;
380+
343381 beforeAll ( ( ) => {
344382 program = buildProgram ( ) ;
345383 const hooks = (
0 commit comments