diff --git a/docs-site/content/docs/cli-reference/ktx-setup.mdx b/docs-site/content/docs/cli-reference/ktx-setup.mdx index a52a3eba..b94d65db 100644 --- a/docs-site/content/docs/cli-reference/ktx-setup.mdx +++ b/docs-site/content/docs/cli-reference/ktx-setup.mdx @@ -136,6 +136,10 @@ Enabling query history makes deep ingest readiness matter for later ### Context Sources +In interactive setup, after you configure a database, choose +**Skip context sources** to leave optional context-source setup complete with no +sources. This is equivalent to passing `--skip-sources` in scripted setup. + | Flag | Description | |------|-------------| | `--source ` | Context-source connector type: `dbt`, `metricflow`, `metabase`, `looker`, `lookml`, or `notion` | diff --git a/packages/cli/src/setup-databases.test.ts b/packages/cli/src/setup-databases.test.ts index 50a1c6ed..09a24fdb 100644 --- a/packages/cli/src/setup-databases.test.ts +++ b/packages/cli/src/setup-databases.test.ts @@ -695,6 +695,7 @@ describe('setup databases step', () => { message: 'Databases configured: warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -703,6 +704,48 @@ describe('setup databases step', () => { expect(scanConnection).not.toHaveBeenCalled(); }); + it('can skip context sources from the configured database menu', async () => { + await writeFile( + join(tempDir, 'ktx.yaml'), + [ + 'connections:', + ' warehouse:', + ' driver: postgres', + ' url: env:DATABASE_URL', + 'setup:', + ' database_connection_ids:', + ' - warehouse', + '', + ].join('\n'), + 'utf-8', + ); + await writeKtxSetupState(tempDir, { completed_steps: ['databases'] }); + const prompts = makePromptAdapter({ selectValues: ['skip-sources'] }); + const testConnection = vi.fn(async () => 0); + const scanConnection = vi.fn(async () => 0); + + const result = await runKtxSetupDatabasesStep( + { + projectDir: tempDir, + inputMode: 'auto', + skipDatabases: false, + databaseSchemas: [], + disableQueryHistory: true, + }, + makeIo().io, + { prompts, testConnection, scanConnection }, + ); + + expect(result).toEqual({ + status: 'ready', + projectDir: tempDir, + connectionIds: ['warehouse'], + skipSources: true, + }); + expect(testConnection).not.toHaveBeenCalled(); + expect(scanConnection).not.toHaveBeenCalled(); + }); + it('preserves existing database ids when adding another database from the configured menu', async () => { await writeFile( join(tempDir, 'ktx.yaml'), @@ -753,6 +796,7 @@ describe('setup databases step', () => { message: 'Databases configured: warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -801,6 +845,7 @@ describe('setup databases step', () => { message: 'Databases configured: postgres-warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -846,6 +891,7 @@ describe('setup databases step', () => { message: 'Databases configured: postgres-warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -890,6 +936,7 @@ describe('setup databases step', () => { message: 'Databases configured: warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -936,6 +983,7 @@ describe('setup databases step', () => { message: 'Databases configured: warehouse\nWhat would you like to do?', options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], diff --git a/packages/cli/src/setup-databases.ts b/packages/cli/src/setup-databases.ts index 30d4fa20..5d22d6b4 100644 --- a/packages/cli/src/setup-databases.ts +++ b/packages/cli/src/setup-databases.ts @@ -56,7 +56,12 @@ export interface KtxSetupDatabasesArgs { } export type KtxSetupDatabasesResult = - | { status: 'ready'; projectDir: string; connectionIds: string[] } + | { + status: 'ready'; + projectDir: string; + connectionIds: string[]; + skipSources?: boolean; + } | { status: 'skipped'; projectDir: string } | { status: 'back'; projectDir: string } | { status: 'missing-input'; projectDir: string } @@ -659,6 +664,7 @@ function configuredPrimarySourcesPrompt(connectionIds: string[]): { message: `Databases configured: ${connectionIds.join(', ')}\nWhat would you like to do?`, options: [ { value: 'continue', label: 'Continue to context sources' }, + { value: 'skip-sources', label: 'Skip context sources' }, { value: 'edit', label: 'Edit an existing database' }, { value: 'add', label: 'Add another database' }, ], @@ -2217,6 +2223,15 @@ export async function runKtxSetupDatabasesStep( await markDatabasesComplete(args.projectDir, selectedConnectionIds); return { status: 'ready', projectDir: args.projectDir, connectionIds: selectedConnectionIds }; } + if (action === 'skip-sources') { + await markDatabasesComplete(args.projectDir, selectedConnectionIds); + return { + status: 'ready', + projectDir: args.projectDir, + connectionIds: selectedConnectionIds, + skipSources: true, + }; + } if (action === 'edit') { const connectionId = await choosePrimarySourceToEdit({ projectDir: args.projectDir, diff --git a/packages/cli/src/setup.test.ts b/packages/cli/src/setup.test.ts index ff8513b7..26dc0324 100644 --- a/packages/cli/src/setup.test.ts +++ b/packages/cli/src/setup.test.ts @@ -1641,6 +1641,67 @@ describe('setup status', () => { expect(calls).toEqual(['model', 'embeddings', 'databases', 'sources']); }); + it('passes context-source skip selection from database setup into the sources step', async () => { + const calls: string[] = []; + const io = makeIo(); + await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8'); + + await expect( + runKtxSetup( + { + command: 'run', + projectDir: tempDir, + mode: 'auto', + agents: false, + skipAgents: true, + inputMode: 'disabled', + yes: true, + cliVersion: '0.2.0', + skipLlm: true, + skipEmbeddings: true, + skipDatabases: false, + skipSources: false, + databaseSchemas: [], + }, + io.io, + { + model: async () => { + calls.push('model'); + return { status: 'skipped', projectDir: tempDir }; + }, + embeddings: async () => { + calls.push('embeddings'); + return { status: 'skipped', projectDir: tempDir }; + }, + databases: async () => { + calls.push('databases'); + return { + status: 'ready', + projectDir: tempDir, + connectionIds: ['warehouse'], + skipSources: true, + }; + }, + sources: async (args) => { + expect(args.skipSources).toBe(true); + calls.push('sources'); + return { status: 'skipped', projectDir: tempDir }; + }, + runtime: async () => { + calls.push('runtime'); + return runtimeReady(tempDir); + }, + context: async () => { + calls.push('context'); + return { status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' }; + }, + }, + ), + ).resolves.toBe(0); + + expect(calls).toEqual(['model', 'embeddings', 'databases', 'sources', 'runtime', 'context']); + }); + it.each([ { backend: 'vertex', diff --git a/packages/cli/src/setup.ts b/packages/cli/src/setup.ts index 825170c0..422f95c5 100644 --- a/packages/cli/src/setup.ts +++ b/packages/cli/src/setup.ts @@ -667,6 +667,7 @@ async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetup const shouldRunContext = !agentOnlySetup && (!runOnly || runOnly === 'context'); const shouldRunAgents = agentsRequested || !runOnly || runOnly === 'agents'; const showPromptInstructions = projectResult.confirmedCreation !== true; + let skipSourcesFromDatabaseMenu = false; const setupSteps: KtxSetupFlowStep[] = agentOnlySetup ? [] @@ -680,7 +681,9 @@ async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetup if (step === 'models') return !args.skipLlm && shouldRunModels; if (step === 'embeddings') return !args.skipEmbeddings && shouldRunEmbeddings; if (step === 'databases') return !args.skipDatabases && shouldRunDatabases; - if (step === 'sources') return args.skipSources !== true && shouldRunSources; + if (step === 'sources') { + return args.skipSources !== true && !skipSourcesFromDatabaseMenu && shouldRunSources; + } if (step === 'runtime') return shouldRunRuntime; if (step === 'context') return shouldRunContext; return shouldRunAgents && args.skipAgents !== true; @@ -743,7 +746,7 @@ async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetup const databasesRunner = deps.databases ?? ((databaseArgs, databaseIo) => runKtxSetupDatabasesStep(databaseArgs, databaseIo, deps.databasesDeps)); - stepResult = await databasesRunner( + const databaseResult = await databasesRunner( { projectDir: projectResult.projectDir, inputMode: args.inputMode, @@ -768,6 +771,8 @@ async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetup }, io, ); + skipSourcesFromDatabaseMenu = databaseResult.status === 'ready' && databaseResult.skipSources === true; + stepResult = databaseResult; } else if (step === 'sources') { const sourcesRunner = deps.sources ?? ((sourceArgs, sourceIo) => runKtxSetupSourcesStep(sourceArgs, sourceIo, deps.sourcesDeps)); @@ -794,7 +799,7 @@ async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetup ...(args.notionCrawlMode ? { notionCrawlMode: args.notionCrawlMode } : {}), ...(args.notionRootPageIds ? { notionRootPageIds: args.notionRootPageIds } : {}), runInitialSourceIngest: args.runInitialSourceIngest ?? false, - skipSources: args.skipSources === true || !shouldRunSources, + skipSources: args.skipSources === true || !shouldRunSources || skipSourcesFromDatabaseMenu, }, io, );