From 80c2930ad8b57fced5827afe244dc22d789e27d0 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Tue, 12 May 2026 14:56:09 +0200 Subject: [PATCH 1/2] fix(cli): replace KLO logo with KTX --- packages/cli/src/memory-flow-hud.tsx | 12 ++++++------ packages/cli/src/memory-flow-tui.test.tsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/memory-flow-hud.tsx b/packages/cli/src/memory-flow-hud.tsx index 68f50bae..fbeaf219 100644 --- a/packages/cli/src/memory-flow-hud.tsx +++ b/packages/cli/src/memory-flow-hud.tsx @@ -316,12 +316,12 @@ function pad(str: string, width: number): string { } const KTX_LOGO_SMALL = [ - '██╗ ██╗██╗ ██████╗ ', - '██║ ██╔╝██║ ██╔═══██╗', - '█████╔╝ ██║ ██║ ██║', - '██╔═██╗ ██║ ██║ ██║', - '██║ ██╗███████╗╚██████╔╝', - '╚═╝ ╚═╝╚══════╝ ╚═════╝ ', + '██╗ ██╗████████╗██╗ ██╗', + '██║ ██╔╝╚══██╔══╝╚██╗██╔╝', + '█████╔╝ ██║ ╚███╔╝ ', + '██╔═██╗ ██║ ██╔██╗ ', + '██║ ██╗ ██║ ██╔╝ ██╗', + '╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝', ] as const; export function Logo(props: { theme: HudTheme; done: boolean }): ReactNode { diff --git a/packages/cli/src/memory-flow-tui.test.tsx b/packages/cli/src/memory-flow-tui.test.tsx index 9bf5425e..4326132a 100644 --- a/packages/cli/src/memory-flow-tui.test.tsx +++ b/packages/cli/src/memory-flow-tui.test.tsx @@ -105,7 +105,7 @@ describe('sanitizeMemoryFlowTuiError', () => { describe('MemoryFlowTuiApp', () => { it('always shows the KTX logo', () => { const { lastFrame } = renderInkTest(); - expect(lastFrame()).toContain('█████╔╝'); + expect(lastFrame()).toContain('╚███╔╝'); }); it('shows persistent HUD with source and status terminology', () => { @@ -229,7 +229,7 @@ describe('MemoryFlowTuiApp', () => { const frame = lastFrame() ?? ''; expect(frame).toContain('Ingesting — 0/1 business area done'); expect(frame).toContain('Reading table schemas, understanding relationships, creating query definitions'); - expect(frame).toContain('█████╔╝'); + expect(frame).toContain('╚███╔╝'); }); it('describes multi-source ingestion as building the context layer', () => { From dc0e33ddda3e111c10dd44e90e13a3fe7050bbe2 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Tue, 12 May 2026 15:15:28 +0200 Subject: [PATCH 2/2] fix(cli): route plain ingest progress to stderr --- packages/cli/src/ingest.test.ts | 37 +++++++++++++++++---------------- packages/cli/src/ingest.ts | 6 +++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/cli/src/ingest.test.ts b/packages/cli/src/ingest.test.ts index b547d185..4f49216c 100644 --- a/packages/cli/src/ingest.test.ts +++ b/packages/cli/src/ingest.test.ts @@ -1077,16 +1077,17 @@ describe('runKtxIngest', () => { ).resolves.toBe(0); const stdout = io.stdout(); - expect(stdout).toContain('[5%] Fetching source files for warehouse/historic-sql'); - expect(stdout).toContain('[15%] Fetched 3 source files from historic-sql'); - expect(stdout).toContain('[45%] Planned 1 work unit'); - expect(stdout).toContain('[80%] Processed 1/1 work units'); - expect(stdout).toContain('[100%] Ingest completed'); + const stderr = io.stderr(); + expect(stderr).toContain('[5%] Fetching source files for warehouse/historic-sql'); + expect(stderr).toContain('[15%] Fetched 3 source files from historic-sql'); + expect(stderr).toContain('[45%] Planned 1 work unit'); + expect(stderr).toContain('[80%] Processed 1/1 work units'); + expect(stderr).toContain('[100%] Ingest completed'); expect(stdout).toContain('Report: report-live-1'); - expect(io.stderr()).toBe(''); + expect(stdout).not.toContain('[5%]'); }); - it('writes plain TTY ingest progress and final report to stdout', async () => { + it('writes plain TTY ingest progress to stderr and final report to stdout', async () => { const projectDir = join(tempDir, 'project'); await writeWarehouseConfig(projectDir); const sourceDir = join(tempDir, 'source'); @@ -1113,9 +1114,9 @@ describe('runKtxIngest', () => { ), ).resolves.toBe(0); - expect(io.stdout()).toContain('[5%] Fetching source files for warehouse/fake'); + expect(io.stderr()).toContain('[5%] Fetching source files for warehouse/fake'); expect(io.stdout()).toContain('Report: report-live-1'); - expect(io.stderr()).toBe(''); + expect(io.stdout()).not.toContain('[5%]'); }); it('prints plain WorkUnit step progress during long-running local ingest', async () => { @@ -1202,13 +1203,13 @@ describe('runKtxIngest', () => { ), ).resolves.toBe(0); - const stdout = io.stdout(); - expect(stdout).toContain('[45%] Planned 2 work units'); - expect(stdout).toContain('[55%] Processing 1/2 work units: historic-sql-table-public-orders'); - expect(stdout).toContain( + const stderr = io.stderr(); + expect(stderr).toContain('[45%] Planned 2 work units'); + expect(stderr).toContain('[55%] Processing 1/2 work units: historic-sql-table-public-orders'); + expect(stderr).toContain( '\r[58%] Processing work units: 0/2 complete, 1 active; latest historic-sql-table-public-orders step 7/40\u001b[K', ); - expect(stdout).toContain('[68%] Processed 1/2 work units'); + expect(stderr).toContain('[68%] Processed 1/2 work units'); }); it('renders concurrent WorkUnit step progress as transient aggregate status', async () => { @@ -1294,14 +1295,14 @@ describe('runKtxIngest', () => { ), ).resolves.toBe(0); - const stdout = io.stdout(); - expect(stdout).toContain( + const stderr = io.stderr(); + expect(stderr).toContain( '\r[56%] Processing work units: 0/6 complete, 6 active; latest historic-sql-table-public-suppliers step 1/40\u001b[K', ); - expect(stdout).not.toContain( + expect(stderr).not.toContain( '\n[56%] Processing 6/6 work units: historic-sql-table-public-suppliers step 1/40\n', ); - expect(stdout).toContain('\n[100%] Ingest completed\n'); + expect(stderr).toContain('\n[100%] Ingest completed\n'); }); it('passes local Looker pull-config options and agent runner into scheduled ingest for Looker scheduled ingest', async () => { diff --git a/packages/cli/src/ingest.ts b/packages/cli/src/ingest.ts index 571bc1ef..3b5e2aa7 100644 --- a/packages/cli/src/ingest.ts +++ b/packages/cli/src/ingest.ts @@ -306,7 +306,7 @@ function createPlainIngestProgressRenderer( if (!hasPendingTransient) { return; } - io.stdout.write('\n'); + io.stderr.write('\n'); hasPendingTransient = false; }; @@ -315,12 +315,12 @@ function createPlainIngestProgressRenderer( lastPercent = nextPercent; const line = `[${nextPercent}%] ${message}`; if (options?.transient === true) { - io.stdout.write(`\r${line}\u001b[K`); + io.stderr.write(`\r${line}\u001b[K`); hasPendingTransient = true; return; } flush(); - io.stdout.write(`${line}\n`); + io.stderr.write(`${line}\n`); }; return {