diff --git a/setup/lib/skill-driver.test.ts b/setup/lib/skill-driver.test.ts index 224536e22..9652f3918 100644 --- a/setup/lib/skill-driver.test.ts +++ b/setup/lib/skill-driver.test.ts @@ -148,6 +148,20 @@ describe('thin skill driver', () => { expect(out.fields.PLATFORM_ID).toBe('telegram:42'); }); + it('hostExecStream children run with LOG_LEVEL=warn — host logger info noise stays off the wizard', async () => { + const root = mkdtempSync(join(tmpdir(), 'driver-loglevel-')); + const prev = process.env.LOG_LEVEL; + delete process.env.LOG_LEVEL; // simulate an operator who didn't set one + try { + const out = await hostExecStream(root)( + 'echo "=== NANOCLAW SETUP: ENV ==="; echo "STATUS: success"; echo "LVL: $LOG_LEVEL"; echo "=== END ==="', + ); + expect(out.fields.LVL).toBe('warn'); + } finally { + if (prev !== undefined) process.env.LOG_LEVEL = prev; + } + }); + function reuseScratch(): { root: string; skill: string } { const root = mkdtempSync(join(tmpdir(), 'reuse-')); const skill = mkdtempSync(join(tmpdir(), 'reuse-skill-')); diff --git a/setup/lib/skill-driver.ts b/setup/lib/skill-driver.ts index b881f1849..6b8baeb17 100644 --- a/setup/lib/skill-driver.ts +++ b/setup/lib/skill-driver.ts @@ -282,7 +282,20 @@ export function hostExecStream(projectRoot: string): (cmd: string) => Promise { const child = spawn('bash', ['-c', cmd], { cwd: projectRoot, - env: { ...process.env, PATH: `${join(projectRoot, 'bin')}:${process.env.PATH ?? ''}` }, + env: { + ...process.env, + PATH: `${join(projectRoot, 'bin')}:${process.env.PATH ?? ''}`, + // A step renders curated operator UI (a code card, a QR) — the host + // logger's info noise doesn't belong on the wizard screen, and it + // always emits ANSI so it can't be filtered by stream. Warnings and + // errors still pass. An operator-set LOG_LEVEL wins (debugging). + LOG_LEVEL: process.env.LOG_LEVEL ?? 'warn', + // The child's stdout is a pipe, so picocolors would strip its clack + // rendering to bare box chars that clash with the wizard theme. + // When the OPERATOR's terminal is a real TTY, the teed lines land + // there — force color so the child's card matches the parent. + ...(process.stdout.isTTY ? { FORCE_COLOR: '1' } : {}), + }, stdio: ['inherit', 'pipe', 'pipe'], }); const blocks: Array<{ fields: Record }> = [];