@kaelio/ktx 0.1.0-rc.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/assets/python/{kaelio_ktx-0.1.0rc6-py3-none-any.whl → kaelio_ktx-0.1.1-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/commands/mcp-commands.js +11 -3
  4. package/dist/commands/mcp-commands.test.js +30 -1
  5. package/dist/commands/setup-commands.js +14 -26
  6. package/dist/doctor.test.js +3 -4
  7. package/dist/index.test.js +26 -10
  8. package/dist/ingest-depth.js +0 -1
  9. package/dist/ingest.test-utils.js +2 -2
  10. package/dist/ingest.test.js +6 -30
  11. package/dist/managed-local-embeddings.d.ts +2 -0
  12. package/dist/managed-local-embeddings.js +2 -0
  13. package/dist/managed-local-embeddings.test.js +2 -0
  14. package/dist/managed-mcp-daemon.js +3 -2
  15. package/dist/managed-mcp-daemon.test.js +25 -0
  16. package/dist/managed-python-command.test.js +1 -0
  17. package/dist/managed-python-daemon.js +3 -2
  18. package/dist/managed-python-daemon.test.js +20 -0
  19. package/dist/managed-python-runtime.d.ts +4 -0
  20. package/dist/managed-python-runtime.js +47 -3
  21. package/dist/managed-python-runtime.test.js +51 -21
  22. package/dist/next-steps.js +1 -1
  23. package/dist/next-steps.test.js +2 -0
  24. package/dist/proxy-env.d.ts +1 -0
  25. package/dist/proxy-env.js +23 -0
  26. package/dist/proxy-env.test.js +17 -0
  27. package/dist/runtime-requirements.d.ts +1 -2
  28. package/dist/runtime-requirements.js +0 -7
  29. package/dist/runtime-requirements.test.js +2 -2
  30. package/dist/runtime.test.js +1 -0
  31. package/dist/setup-agents.d.ts +11 -3
  32. package/dist/setup-agents.js +400 -135
  33. package/dist/setup-agents.test.js +394 -62
  34. package/dist/setup-embeddings.d.ts +1 -0
  35. package/dist/setup-embeddings.js +28 -6
  36. package/dist/setup-embeddings.test.js +46 -4
  37. package/dist/setup-models.d.ts +0 -1
  38. package/dist/setup-models.js +2 -3
  39. package/dist/setup-models.test.js +8 -10
  40. package/dist/setup-project.d.ts +9 -1
  41. package/dist/setup-project.js +52 -25
  42. package/dist/setup-project.test.js +8 -8
  43. package/dist/setup-runtime.d.ts +0 -1
  44. package/dist/setup-runtime.js +0 -1
  45. package/dist/setup-runtime.test.js +9 -13
  46. package/dist/setup.d.ts +4 -2
  47. package/dist/setup.js +72 -30
  48. package/dist/setup.test.js +271 -58
  49. package/dist/sl.test.js +2 -1
  50. package/dist/standalone-smoke.test.js +2 -3
  51. package/dist/status-project.js +1 -10
  52. package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
  53. package/node_modules/@ktx/context/dist/core/git.service.d.ts +0 -1
  54. package/node_modules/@ktx/context/dist/core/git.service.js +0 -12
  55. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/historic-sql.adapter.d.ts +1 -2
  56. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/historic-sql.adapter.js +0 -18
  57. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/local-ingest-acceptance.test.js +7 -7
  58. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/post-processor.d.ts +4 -0
  59. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/post-processor.js +38 -0
  60. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/post-processor.test.d.ts +1 -0
  61. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/post-processor.test.js +63 -0
  62. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/projection.d.ts +0 -5
  63. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/projection.js +0 -48
  64. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/projection.test.js +0 -83
  65. package/node_modules/@ktx/context/dist/ingest/index.d.ts +2 -1
  66. package/node_modules/@ktx/context/dist/ingest/index.js +1 -0
  67. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +0 -2
  68. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +0 -166
  69. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +45 -235
  70. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +38 -193
  71. package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +11 -30
  72. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +5 -1
  73. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +3 -3
  74. package/node_modules/@ktx/context/dist/ingest/local-embedding-provider.integration.test.js +9 -10
  75. package/node_modules/@ktx/context/dist/ingest/local-ingest.js +7 -0
  76. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +4 -4
  77. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +1 -1
  78. package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +1 -1
  79. package/node_modules/@ktx/context/dist/ingest/ports.d.ts +20 -1
  80. package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +2 -73
  81. package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +0 -27
  82. package/node_modules/@ktx/context/dist/ingest/reports.d.ts +5 -23
  83. package/node_modules/@ktx/context/dist/ingest/reports.js +24 -7
  84. package/node_modules/@ktx/context/dist/ingest/types.d.ts +0 -33
  85. package/node_modules/@ktx/context/dist/llm/local-config.js +2 -15
  86. package/node_modules/@ktx/context/dist/llm/local-config.test.js +3 -7
  87. package/node_modules/@ktx/context/dist/package-exports.test.js +1 -2
  88. package/node_modules/@ktx/context/dist/project/config.d.ts +0 -5
  89. package/node_modules/@ktx/context/dist/project/config.js +5 -5
  90. package/node_modules/@ktx/context/dist/project/config.test.js +4 -7
  91. package/node_modules/@ktx/context/dist/scan/enrichment-state.test.js +4 -4
  92. package/node_modules/@ktx/context/dist/scan/index.d.ts +1 -1
  93. package/node_modules/@ktx/context/dist/scan/local-enrichment.d.ts +2 -6
  94. package/node_modules/@ktx/context/dist/scan/local-enrichment.js +31 -47
  95. package/node_modules/@ktx/context/dist/scan/local-enrichment.test.js +35 -18
  96. package/node_modules/@ktx/context/dist/scan/local-scan.test.js +2 -3
  97. package/node_modules/@ktx/llm/dist/embedding-provider.d.ts +0 -7
  98. package/node_modules/@ktx/llm/dist/embedding-provider.js +12 -138
  99. package/node_modules/@ktx/llm/dist/embedding-provider.test.js +10 -25
  100. package/node_modules/@ktx/llm/dist/types.d.ts +1 -1
  101. package/package.json +4 -4
  102. package/node_modules/@ktx/context/dist/ingest/finalization-scope.d.ts +0 -22
  103. package/node_modules/@ktx/context/dist/ingest/finalization-scope.js +0 -95
  104. package/node_modules/@ktx/context/dist/ingest/finalization-scope.test.js +0 -114
  105. /package/{node_modules/@ktx/context/dist/ingest/finalization-scope.test.d.ts → dist/proxy-env.test.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { execFile } from 'node:child_process';
2
- import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
2
+ import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { promisify } from 'node:util';
@@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
8
  import { localFakeBundleReport, persistLocalBundleReport } from './ingest.test-utils.js';
9
9
  import { contextBuildCommands, writeKtxSetupContextState } from './setup-context.js';
10
10
  import { runDemoTour } from './setup-demo-tour.js';
11
- import { formatKtxSetupStatus, readKtxSetupStatus, runKtxSetup } from './setup.js';
11
+ import { formatKtxSetupCompletionSummary, formatKtxSetupStatus, readKtxSetupStatus, runKtxSetup } from './setup.js';
12
12
  vi.mock('./setup-demo-tour.js', () => ({
13
13
  runDemoTour: vi.fn(async () => 0),
14
14
  }));
@@ -88,7 +88,7 @@ describe('setup status', () => {
88
88
  agents: [],
89
89
  });
90
90
  });
91
- it('reports deterministic default embeddings as not setup-ready', async () => {
91
+ it('reports disabled default embeddings as not setup-ready', async () => {
92
92
  await mkdir(tempDir, { recursive: true });
93
93
  await writeFile(join(tempDir, 'ktx.yaml'), [
94
94
  'llm:',
@@ -100,15 +100,14 @@ describe('setup status', () => {
100
100
  ' default: claude-sonnet-4-6',
101
101
  'ingest:',
102
102
  ' embeddings:',
103
- ' backend: deterministic',
104
- ' model: deterministic',
103
+ ' backend: none',
105
104
  ' dimensions: 8',
106
105
  'connections: {}',
107
106
  ].join('\n'), 'utf-8');
108
107
  await expect(readKtxSetupStatus(tempDir)).resolves.toMatchObject({
109
108
  project: { path: tempDir, ready: true },
110
109
  llm: { backend: 'anthropic', ready: true, model: 'claude-sonnet-4-6' },
111
- embeddings: { backend: 'deterministic', ready: false, model: 'deterministic', dimensions: 8 },
110
+ embeddings: { backend: 'none', ready: false, dimensions: 8 },
112
111
  });
113
112
  });
114
113
  it.each([
@@ -299,8 +298,7 @@ describe('setup status', () => {
299
298
  ' default: claude-sonnet-4-6',
300
299
  'ingest:',
301
300
  ' embeddings:',
302
- ' backend: deterministic',
303
- ' model: deterministic',
301
+ ' backend: none',
304
302
  ' dimensions: 8',
305
303
  '',
306
304
  ].join('\n'), 'utf-8');
@@ -337,6 +335,98 @@ describe('setup status', () => {
337
335
  expect(rendered).toContain('KTX context built: no');
338
336
  expect(rendered).not.toContain('No KTX project found.');
339
337
  });
338
+ it('formats a concise ready summary for completed agent setup', () => {
339
+ const rendered = formatKtxSetupCompletionSummary({
340
+ project: { path: tempDir, ready: true },
341
+ llm: { ready: true, model: 'sonnet' },
342
+ embeddings: { ready: true, model: 'text-embedding-3-small' },
343
+ databases: [{ connectionId: 'postgres-warehouse', ready: true }],
344
+ sources: [{ connectionId: 'dbt-main', type: 'dbt', ready: true }],
345
+ runtime: { required: true, ready: true, features: ['core'] },
346
+ context: { ready: true, status: 'completed' },
347
+ agents: [
348
+ { target: 'claude-code', scope: 'project', ready: true },
349
+ { target: 'claude-desktop', scope: 'global', ready: true },
350
+ ],
351
+ }, {
352
+ agentNextActions: [
353
+ '1. Start MCP',
354
+ ' Run this command before using Claude Code:',
355
+ '',
356
+ ' RUN:',
357
+ ` ktx mcp start --project-dir ${tempDir}`,
358
+ '',
359
+ ' If you need to stop MCP later:',
360
+ ` ktx mcp stop --project-dir ${tempDir}`,
361
+ '',
362
+ '2. Open Claude Code',
363
+ ' Open Claude Code from the KTX project directory:',
364
+ '',
365
+ ' RUN:',
366
+ ` cd '${tempDir}'`,
367
+ ' claude',
368
+ ].join('\n'),
369
+ });
370
+ expect(rendered).toContain(`Project\n ${tempDir}`);
371
+ expect(rendered).toContain('Context\n built');
372
+ expect(rendered).toContain('Agents configured\n Claude Code, Claude Desktop');
373
+ expect(rendered).toContain('REQUIRED BEFORE USING AGENTS\n\n 1. Start MCP');
374
+ expect(rendered).toContain(' Run this command before using Claude Code:');
375
+ expect(rendered).toContain(' RUN:');
376
+ expect(rendered).toContain(' If you need to stop MCP later:');
377
+ expect(rendered).toContain(`ktx mcp stop --project-dir ${tempDir}`);
378
+ expect(rendered).toContain('After that, try\n Ask your agent: "Use KTX to show me the available tables."');
379
+ expect(rendered).not.toContain('Verify');
380
+ expect(rendered).not.toContain('Project ready: yes');
381
+ expect(rendered).not.toContain('What you can do next');
382
+ });
383
+ it('prints agent next actions inside the final ready summary during full setup', async () => {
384
+ const testIo = makeIo();
385
+ await expect(runKtxSetup({
386
+ command: 'run',
387
+ projectDir: tempDir,
388
+ mode: 'auto',
389
+ agents: false,
390
+ target: 'claude-code',
391
+ skipAgents: false,
392
+ inputMode: 'disabled',
393
+ yes: true,
394
+ cliVersion: '0.2.0',
395
+ skipLlm: true,
396
+ skipEmbeddings: true,
397
+ skipDatabases: true,
398
+ skipSources: true,
399
+ databaseSchemas: [],
400
+ }, testIo.io, {
401
+ runtime: async () => runtimeReady(tempDir),
402
+ context: async () => {
403
+ await writeKtxSetupContextState(tempDir, {
404
+ runId: 'setup-context-local-test',
405
+ status: 'completed',
406
+ primarySourceConnectionIds: [],
407
+ contextSourceConnectionIds: [],
408
+ reportIds: [],
409
+ artifactPaths: [],
410
+ retryableFailedTargets: [],
411
+ commands: contextBuildCommands(tempDir, 'setup-context-local-test'),
412
+ });
413
+ await writeKtxSetupState(tempDir, { completed_steps: ['project', 'context'] });
414
+ return { status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' };
415
+ },
416
+ })).resolves.toBe(0);
417
+ const output = testIo.stdout();
418
+ expect(output).toContain('Claude Code · Project scope');
419
+ expect(output).toContain(join(tempDir, '.mcp.json'));
420
+ expect(output).toContain('Requires MCP to be started.');
421
+ expect(output).toContain('Analytics skill installed.');
422
+ expect(output).not.toContain('Agent integration complete');
423
+ expect(output).toContain('Finish KTX agent setup');
424
+ expect(output).not.toContain('KTX project ready');
425
+ expect(output).toContain('REQUIRED BEFORE USING AGENTS');
426
+ expect(output).toContain('Run this command before using Claude Code:');
427
+ expect(output).toContain(`ktx mcp start --project-dir ${tempDir}`);
428
+ expect(output).not.toContain('Finish agent setup');
429
+ });
340
430
  it('prints the setup shell intro for auto-created run mode', async () => {
341
431
  const testIo = makeIo();
342
432
  await expect(runKtxSetup({
@@ -364,6 +454,85 @@ describe('setup status', () => {
364
454
  expect(testIo.stdout()).not.toContain('Optional MCP:');
365
455
  expect(testIo.stderr()).toBe('');
366
456
  });
457
+ it('removes a newly created missing project directory when a later runtime step fails', async () => {
458
+ const projectDir = join(tempDir, 'missing-project');
459
+ const testIo = makeIo();
460
+ await expect(runKtxSetup({
461
+ command: 'run',
462
+ projectDir,
463
+ mode: 'auto',
464
+ agents: false,
465
+ skipAgents: true,
466
+ inputMode: 'disabled',
467
+ yes: true,
468
+ cliVersion: '0.2.0',
469
+ skipLlm: true,
470
+ skipEmbeddings: true,
471
+ databaseSchemas: [],
472
+ skipDatabases: true,
473
+ skipSources: true,
474
+ }, testIo.io, {
475
+ model: async () => ({ status: 'skipped', projectDir }),
476
+ embeddings: async () => ({ status: 'skipped', projectDir }),
477
+ databases: async () => ({ status: 'skipped', projectDir }),
478
+ sources: async () => ({ status: 'skipped', projectDir }),
479
+ runtime: async () => ({ status: 'failed', projectDir, requirements: { features: ['core'], requirements: [] } }),
480
+ })).resolves.toBe(1);
481
+ await expect(stat(projectDir)).rejects.toThrow();
482
+ });
483
+ it('removes KTX scaffold files from an initially empty project directory when runtime setup fails', async () => {
484
+ const testIo = makeIo();
485
+ await expect(runKtxSetup({
486
+ command: 'run',
487
+ projectDir: tempDir,
488
+ mode: 'auto',
489
+ agents: false,
490
+ skipAgents: true,
491
+ inputMode: 'disabled',
492
+ yes: true,
493
+ cliVersion: '0.2.0',
494
+ skipLlm: true,
495
+ skipEmbeddings: true,
496
+ databaseSchemas: [],
497
+ skipDatabases: true,
498
+ skipSources: true,
499
+ }, testIo.io, {
500
+ model: async () => ({ status: 'skipped', projectDir: tempDir }),
501
+ embeddings: async () => ({ status: 'skipped', projectDir: tempDir }),
502
+ databases: async () => ({ status: 'skipped', projectDir: tempDir }),
503
+ sources: async () => ({ status: 'skipped', projectDir: tempDir }),
504
+ runtime: async () => ({ status: 'failed', projectDir: tempDir, requirements: { features: ['core'], requirements: [] } }),
505
+ })).resolves.toBe(1);
506
+ await expect(stat(tempDir)).resolves.toBeDefined();
507
+ expect(await readdir(tempDir)).toEqual([]);
508
+ });
509
+ it('preserves a pre-existing non-empty project directory when runtime setup fails', async () => {
510
+ await writeFile(join(tempDir, 'notes.txt'), 'keep me\n', 'utf-8');
511
+ const testIo = makeIo();
512
+ await expect(runKtxSetup({
513
+ command: 'run',
514
+ projectDir: tempDir,
515
+ mode: 'auto',
516
+ agents: false,
517
+ skipAgents: true,
518
+ inputMode: 'disabled',
519
+ yes: true,
520
+ cliVersion: '0.2.0',
521
+ skipLlm: true,
522
+ skipEmbeddings: true,
523
+ databaseSchemas: [],
524
+ skipDatabases: true,
525
+ skipSources: true,
526
+ }, testIo.io, {
527
+ model: async () => ({ status: 'skipped', projectDir: tempDir }),
528
+ embeddings: async () => ({ status: 'skipped', projectDir: tempDir }),
529
+ databases: async () => ({ status: 'skipped', projectDir: tempDir }),
530
+ sources: async () => ({ status: 'skipped', projectDir: tempDir }),
531
+ runtime: async () => ({ status: 'failed', projectDir: tempDir, requirements: { features: ['core'], requirements: [] } }),
532
+ })).resolves.toBe(1);
533
+ await expect(readFile(join(tempDir, 'notes.txt'), 'utf-8')).resolves.toBe('keep me\n');
534
+ await expect(stat(join(tempDir, 'ktx.yaml'))).resolves.toBeDefined();
535
+ });
367
536
  it('shows demo near the bottom of the first setup intent menu before project creation', async () => {
368
537
  const testIo = makeIo();
369
538
  const select = vi.fn(async (options) => {
@@ -658,16 +827,16 @@ describe('setup status', () => {
658
827
  }, testIo.io, { entryMenuDeps: { prompts: { select: vi.fn(async () => 'demo'), cancel: vi.fn() } } })).resolves.toBe(0);
659
828
  expect(runDemoTour).toHaveBeenCalledWith({ inputMode: 'auto' }, testIo.io, expect.objectContaining({}));
660
829
  });
661
- it('creates a project through run mode when --new is selected', async () => {
830
+ it('creates a project through run mode when --yes is selected', async () => {
662
831
  const testIo = makeIo();
663
832
  await expect(runKtxSetup({
664
833
  command: 'run',
665
834
  projectDir: tempDir,
666
- mode: 'new',
835
+ mode: 'auto',
667
836
  agents: false,
668
837
  skipAgents: true,
669
838
  inputMode: 'disabled',
670
- yes: false,
839
+ yes: true,
671
840
  cliVersion: '0.2.0',
672
841
  skipLlm: true,
673
842
  skipEmbeddings: true,
@@ -729,14 +898,14 @@ describe('setup status', () => {
729
898
  await expect(runKtxSetup({
730
899
  command: 'run',
731
900
  projectDir: tempDir,
732
- mode: 'new',
901
+ mode: 'auto',
733
902
  agents: false,
734
903
  skipAgents: true,
735
904
  inputMode: 'disabled',
736
- yes: false,
905
+ yes: true,
737
906
  cliVersion: '0.2.0',
738
907
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
739
- anthropicModel: 'claude-sonnet-4-6',
908
+ llmModel: 'claude-sonnet-4-6',
740
909
  skipLlm: false,
741
910
  skipEmbeddings: true,
742
911
  databaseSchemas: [],
@@ -747,7 +916,7 @@ describe('setup status', () => {
747
916
  projectDir: tempDir,
748
917
  inputMode: 'disabled',
749
918
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
750
- anthropicModel: 'claude-sonnet-4-6',
919
+ llmModel: 'claude-sonnet-4-6',
751
920
  skipLlm: false,
752
921
  }), testIo.io);
753
922
  });
@@ -757,16 +926,16 @@ describe('setup status', () => {
757
926
  await expect(runKtxSetup({
758
927
  command: 'run',
759
928
  projectDir: tempDir,
760
- mode: 'new',
929
+ mode: 'auto',
761
930
  agents: false,
762
931
  skipAgents: true,
763
932
  inputMode: 'disabled',
764
- yes: false,
933
+ yes: true,
765
934
  cliVersion: '0.2.0',
766
935
  llmBackend: 'vertex',
767
936
  vertexProject: 'local-gcp-project',
768
937
  vertexLocation: 'us-east5',
769
- anthropicModel: 'claude-sonnet-4-6',
938
+ llmModel: 'claude-sonnet-4-6',
770
939
  skipLlm: false,
771
940
  skipEmbeddings: true,
772
941
  databaseSchemas: [],
@@ -779,7 +948,7 @@ describe('setup status', () => {
779
948
  llmBackend: 'vertex',
780
949
  vertexProject: 'local-gcp-project',
781
950
  vertexLocation: 'us-east5',
782
- anthropicModel: 'claude-sonnet-4-6',
951
+ llmModel: 'claude-sonnet-4-6',
783
952
  skipLlm: false,
784
953
  }), testIo.io);
785
954
  });
@@ -790,14 +959,14 @@ describe('setup status', () => {
790
959
  await expect(runKtxSetup({
791
960
  command: 'run',
792
961
  projectDir: tempDir,
793
- mode: 'new',
962
+ mode: 'auto',
794
963
  agents: false,
795
964
  skipAgents: true,
796
965
  inputMode: 'disabled',
797
966
  yes: true,
798
967
  cliVersion: '0.2.0',
799
968
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
800
- anthropicModel: 'claude-sonnet-4-6',
969
+ llmModel: 'claude-sonnet-4-6',
801
970
  skipLlm: false,
802
971
  embeddingBackend: 'openai',
803
972
  embeddingApiKeyEnv: 'OPENAI_API_KEY', // pragma: allowlist secret
@@ -819,10 +988,11 @@ describe('setup status', () => {
819
988
  it('passes no-input runtime policy to the embeddings step', async () => {
820
989
  const io = makeIo();
821
990
  const embeddings = vi.fn(async () => ({ status: 'failed', projectDir: tempDir }));
991
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
822
992
  await expect(runKtxSetup({
823
993
  command: 'run',
824
994
  projectDir: tempDir,
825
- mode: 'new',
995
+ mode: 'auto',
826
996
  agents: false,
827
997
  agentScope: 'project',
828
998
  skipAgents: true,
@@ -844,10 +1014,11 @@ describe('setup status', () => {
844
1014
  const io = makeIo();
845
1015
  const embeddings = vi.fn(async () => ({ status: 'ready', projectDir: tempDir }));
846
1016
  const context = vi.fn(async () => ({ status: 'failed', projectDir: tempDir }));
1017
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
847
1018
  await expect(runKtxSetup({
848
1019
  command: 'run',
849
1020
  projectDir: tempDir,
850
- mode: 'new',
1021
+ mode: 'auto',
851
1022
  agents: false,
852
1023
  agentScope: 'project',
853
1024
  skipAgents: true,
@@ -874,6 +1045,7 @@ describe('setup status', () => {
874
1045
  });
875
1046
  it('lets Back from embedding setup return to the model step instead of exiting', async () => {
876
1047
  const testIo = makeIo();
1048
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
877
1049
  const modelResults = [
878
1050
  { status: 'ready', projectDir: tempDir },
879
1051
  { status: 'back', projectDir: tempDir },
@@ -883,7 +1055,7 @@ describe('setup status', () => {
883
1055
  await expect(runKtxSetup({
884
1056
  command: 'run',
885
1057
  projectDir: tempDir,
886
- mode: 'new',
1058
+ mode: 'auto',
887
1059
  agents: false,
888
1060
  skipAgents: true,
889
1061
  inputMode: 'auto',
@@ -901,6 +1073,7 @@ describe('setup status', () => {
901
1073
  });
902
1074
  it('lets Back from database selection return to embedding setup', async () => {
903
1075
  const testIo = makeIo();
1076
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
904
1077
  const modelResults = [
905
1078
  { status: 'ready', projectDir: tempDir },
906
1079
  { status: 'back', projectDir: tempDir },
@@ -921,11 +1094,11 @@ describe('setup status', () => {
921
1094
  await expect(runKtxSetup({
922
1095
  command: 'run',
923
1096
  projectDir: tempDir,
924
- mode: 'new',
1097
+ mode: 'auto',
925
1098
  agents: false,
926
1099
  skipAgents: true,
927
1100
  inputMode: 'auto',
928
- yes: false,
1101
+ yes: true,
929
1102
  cliVersion: '0.2.0',
930
1103
  skipLlm: false,
931
1104
  skipEmbeddings: false,
@@ -958,7 +1131,7 @@ describe('setup status', () => {
958
1131
  agents: false,
959
1132
  skipAgents: true,
960
1133
  inputMode: 'auto',
961
- yes: false,
1134
+ yes: true,
962
1135
  cliVersion: '0.2.0',
963
1136
  skipLlm: false,
964
1137
  skipEmbeddings: true,
@@ -986,14 +1159,14 @@ describe('setup status', () => {
986
1159
  await expect(runKtxSetup({
987
1160
  command: 'run',
988
1161
  projectDir: tempDir,
989
- mode: 'new',
1162
+ mode: 'auto',
990
1163
  agents: false,
991
1164
  skipAgents: true,
992
1165
  inputMode: 'disabled',
993
- yes: false,
1166
+ yes: true,
994
1167
  cliVersion: '0.2.0',
995
1168
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
996
- anthropicModel: 'claude-sonnet-4-6',
1169
+ llmModel: 'claude-sonnet-4-6',
997
1170
  skipLlm: false,
998
1171
  embeddingBackend: 'openai',
999
1172
  embeddingApiKeyEnv: 'OPENAI_API_KEY', // pragma: allowlist secret
@@ -1032,7 +1205,7 @@ describe('setup status', () => {
1032
1205
  await expect(runKtxSetup({
1033
1206
  command: 'run',
1034
1207
  projectDir: tempDir,
1035
- mode: 'existing',
1208
+ mode: 'auto',
1036
1209
  agents: false,
1037
1210
  skipAgents: true,
1038
1211
  inputMode: 'disabled',
@@ -1095,7 +1268,7 @@ describe('setup status', () => {
1095
1268
  await expect(runKtxSetup({
1096
1269
  command: 'run',
1097
1270
  projectDir: tempDir,
1098
- mode: 'existing',
1271
+ mode: 'auto',
1099
1272
  agents: false,
1100
1273
  skipAgents: true,
1101
1274
  inputMode: 'disabled',
@@ -1132,7 +1305,7 @@ describe('setup status', () => {
1132
1305
  await expect(runKtxSetup({
1133
1306
  command: 'run',
1134
1307
  projectDir: tempDir,
1135
- mode: 'existing',
1308
+ mode: 'auto',
1136
1309
  agents: false,
1137
1310
  skipAgents: true,
1138
1311
  inputMode: 'disabled',
@@ -1171,7 +1344,7 @@ describe('setup status', () => {
1171
1344
  await expect(runKtxSetup({
1172
1345
  command: 'run',
1173
1346
  projectDir: tempDir,
1174
- mode: 'existing',
1347
+ mode: 'auto',
1175
1348
  agents: false,
1176
1349
  inputMode: 'disabled',
1177
1350
  yes: true,
@@ -1223,7 +1396,7 @@ describe('setup status', () => {
1223
1396
  await expect(runKtxSetup({
1224
1397
  command: 'run',
1225
1398
  projectDir: tempDir,
1226
- mode: 'new',
1399
+ mode: 'auto',
1227
1400
  agents: false,
1228
1401
  inputMode: 'disabled',
1229
1402
  yes: true,
@@ -1257,14 +1430,14 @@ describe('setup status', () => {
1257
1430
  const committedConfig = await execFileAsync('git', ['-C', tempDir, 'show', 'HEAD:ktx.yaml']);
1258
1431
  expect(committedConfig.stdout).toContain('warehouse:');
1259
1432
  });
1260
- it('runs agent setup after context succeeds in --agents mode', async () => {
1433
+ it('runs agent setup without runtime or context in --agents mode', async () => {
1261
1434
  const calls = [];
1262
1435
  const io = makeIo();
1263
1436
  await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
1264
1437
  await expect(runKtxSetup({
1265
1438
  command: 'run',
1266
1439
  projectDir: tempDir,
1267
- mode: 'existing',
1440
+ mode: 'auto',
1268
1441
  agents: true,
1269
1442
  target: 'codex',
1270
1443
  agentScope: 'project',
@@ -1284,11 +1457,11 @@ describe('setup status', () => {
1284
1457
  sources: async () => ({ status: 'skipped', projectDir: tempDir }),
1285
1458
  runtime: async () => {
1286
1459
  calls.push('runtime');
1287
- return runtimeReady(tempDir);
1460
+ throw new Error('runtime should not run');
1288
1461
  },
1289
1462
  context: async () => {
1290
1463
  calls.push('context');
1291
- return { status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' };
1464
+ throw new Error('context should not run');
1292
1465
  },
1293
1466
  agents: async () => {
1294
1467
  calls.push('agents');
@@ -1299,10 +1472,12 @@ describe('setup status', () => {
1299
1472
  };
1300
1473
  },
1301
1474
  })).resolves.toBe(0);
1302
- expect(calls).toEqual(['runtime', 'context', 'agents']);
1475
+ expect(calls).toEqual(['agents']);
1303
1476
  });
1304
- it('does not install agents when non-interactive --agents finds context incomplete', async () => {
1477
+ it('installs agents when non-interactive --agents finds context incomplete', async () => {
1305
1478
  const io = makeIo();
1479
+ const runtime = vi.fn(async () => runtimeReady(tempDir));
1480
+ const context = vi.fn(async () => ({ status: 'skipped', projectDir: tempDir }));
1306
1481
  const agents = vi.fn(async () => ({
1307
1482
  status: 'ready',
1308
1483
  projectDir: tempDir,
@@ -1312,7 +1487,7 @@ describe('setup status', () => {
1312
1487
  await expect(runKtxSetup({
1313
1488
  command: 'run',
1314
1489
  projectDir: tempDir,
1315
- mode: 'existing',
1490
+ mode: 'auto',
1316
1491
  agents: true,
1317
1492
  target: 'codex',
1318
1493
  agentScope: 'project',
@@ -1326,12 +1501,49 @@ describe('setup status', () => {
1326
1501
  skipAgents: false,
1327
1502
  databaseSchemas: [],
1328
1503
  }, io.io, {
1329
- runtime: async () => runtimeReady(tempDir),
1330
- context: async () => ({ status: 'skipped', projectDir: tempDir }),
1504
+ runtime,
1505
+ context,
1331
1506
  agents,
1332
- })).resolves.toBe(1);
1333
- expect(agents).not.toHaveBeenCalled();
1334
- expect(io.stderr()).toContain('KTX context is not ready for agents.');
1507
+ })).resolves.toBe(0);
1508
+ expect(runtime).not.toHaveBeenCalled();
1509
+ expect(context).not.toHaveBeenCalled();
1510
+ expect(agents).toHaveBeenCalledTimes(1);
1511
+ expect(io.stderr()).not.toContain('KTX context is not ready for agents.');
1512
+ });
1513
+ it('runs non-TTY --agents with a target without requiring --no-input or --yes', async () => {
1514
+ const io = makeIo();
1515
+ const agents = vi.fn(async () => ({
1516
+ status: 'ready',
1517
+ projectDir: tempDir,
1518
+ installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp' }],
1519
+ }));
1520
+ await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
1521
+ await expect(runKtxSetup({
1522
+ command: 'run',
1523
+ projectDir: tempDir,
1524
+ mode: 'auto',
1525
+ agents: true,
1526
+ target: 'claude-code',
1527
+ agentScope: 'project',
1528
+ inputMode: 'auto',
1529
+ yes: false,
1530
+ cliVersion: '0.2.0',
1531
+ skipLlm: false,
1532
+ skipEmbeddings: false,
1533
+ skipDatabases: false,
1534
+ skipSources: false,
1535
+ skipAgents: false,
1536
+ databaseSchemas: [],
1537
+ }, io.io, { agents })).resolves.toBe(0);
1538
+ expect(agents).toHaveBeenCalledWith(expect.objectContaining({
1539
+ inputMode: 'disabled',
1540
+ yes: false,
1541
+ agents: true,
1542
+ target: 'claude-code',
1543
+ scope: 'project',
1544
+ mode: 'mcp',
1545
+ }), io.io);
1546
+ expect(io.stderr()).not.toContain('Interactive setup requires a terminal');
1335
1547
  });
1336
1548
  it('routes a ready project menu selection to agent setup', async () => {
1337
1549
  const calls = [];
@@ -1382,7 +1594,7 @@ describe('setup status', () => {
1382
1594
  await expect(runKtxSetup({
1383
1595
  command: 'run',
1384
1596
  projectDir: tempDir,
1385
- mode: 'existing',
1597
+ mode: 'auto',
1386
1598
  agents: false,
1387
1599
  inputMode: 'auto',
1388
1600
  yes: false,
@@ -1433,7 +1645,7 @@ describe('setup status', () => {
1433
1645
  process.env.KTX_RUNTIME_ROOT = previousRuntimeRoot;
1434
1646
  }
1435
1647
  }
1436
- expect(calls).toEqual(['runtime', 'agents']);
1648
+ expect(calls).toEqual(['agents']);
1437
1649
  });
1438
1650
  it('skips to agent setup when context is ready but agents are not configured', async () => {
1439
1651
  const calls = [];
@@ -1474,7 +1686,7 @@ describe('setup status', () => {
1474
1686
  await expect(runKtxSetup({
1475
1687
  command: 'run',
1476
1688
  projectDir: tempDir,
1477
- mode: 'existing',
1689
+ mode: 'auto',
1478
1690
  agents: false,
1479
1691
  inputMode: 'auto',
1480
1692
  yes: false,
@@ -1517,9 +1729,9 @@ describe('setup status', () => {
1517
1729
  },
1518
1730
  })).resolves.toBe(0);
1519
1731
  expect(readyMenuSelect).not.toHaveBeenCalled();
1520
- expect(calls).toEqual(['runtime', 'agents']);
1732
+ expect(calls).toEqual(['agents']);
1521
1733
  });
1522
- it('runs only project resolution, runtime, context gate, and agent setup in --agents mode', async () => {
1734
+ it('runs only project resolution and agent setup in --agents mode', async () => {
1523
1735
  const io = makeIo();
1524
1736
  const runtime = vi.fn(async () => runtimeReady(tempDir));
1525
1737
  const context = vi.fn(async () => ({ status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' }));
@@ -1531,7 +1743,7 @@ describe('setup status', () => {
1531
1743
  await expect(runKtxSetup({
1532
1744
  command: 'run',
1533
1745
  projectDir: tempDir,
1534
- mode: 'new',
1746
+ mode: 'auto',
1535
1747
  agents: true,
1536
1748
  target: 'universal',
1537
1749
  agentScope: 'project',
@@ -1552,8 +1764,8 @@ describe('setup status', () => {
1552
1764
  context,
1553
1765
  agents,
1554
1766
  })).resolves.toBe(0);
1555
- expect(runtime).toHaveBeenCalledTimes(1);
1556
- expect(context).toHaveBeenCalledTimes(1);
1767
+ expect(runtime).not.toHaveBeenCalled();
1768
+ expect(context).not.toHaveBeenCalled();
1557
1769
  expect(agents).toHaveBeenCalledTimes(1);
1558
1770
  });
1559
1771
  it('does not run embedding setup when the model step fails', async () => {
@@ -1563,19 +1775,20 @@ describe('setup status', () => {
1563
1775
  await expect(runKtxSetup({
1564
1776
  command: 'run',
1565
1777
  projectDir: tempDir,
1566
- mode: 'new',
1778
+ mode: 'auto',
1567
1779
  agents: false,
1568
1780
  skipAgents: true,
1569
1781
  inputMode: 'disabled',
1570
- yes: false,
1782
+ yes: true,
1571
1783
  cliVersion: '0.2.0',
1572
1784
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
1573
- anthropicModel: 'claude-sonnet-4-6',
1785
+ llmModel: 'claude-sonnet-4-6',
1574
1786
  skipLlm: false,
1575
1787
  skipEmbeddings: false,
1576
1788
  databaseSchemas: [],
1577
1789
  skipDatabases: true,
1578
1790
  }, testIo.io, { model, embeddings })).resolves.toBe(1);
1791
+ expect(model).toHaveBeenCalledTimes(1);
1579
1792
  expect(embeddings).not.toHaveBeenCalled();
1580
1793
  });
1581
1794
  });
package/dist/sl.test.js CHANGED
@@ -89,7 +89,8 @@ describe('runKtxSl', () => {
89
89
  yaml: [
90
90
  'name: orders',
91
91
  'table: public.orders',
92
- 'description: Paid order facts',
92
+ 'descriptions:',
93
+ ' user: Paid order facts',
93
94
  'grain: [order_id]',
94
95
  'columns:',
95
96
  ' - name: order_id',