@kaelio/ktx 0.1.0 → 0.2.0

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 (122) hide show
  1. package/assets/python/{kaelio_ktx-0.1.0-py3-none-any.whl → kaelio_ktx-0.2.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/admin-reindex.d.ts +15 -0
  4. package/dist/admin-reindex.js +168 -0
  5. package/dist/admin-reindex.test.js +116 -0
  6. package/dist/{dev.d.ts → admin.d.ts} +1 -1
  7. package/dist/{dev.js → admin.js} +14 -12
  8. package/dist/admin.test.d.ts +1 -0
  9. package/dist/{dev.test.js → admin.test.js} +36 -31
  10. package/dist/cli-program.js +7 -7
  11. package/dist/cli-program.test.js +1 -1
  12. package/dist/cli-runtime.d.ts +2 -0
  13. package/dist/commands/connection-commands.js +11 -10
  14. package/dist/commands/connection-selection.d.ts +11 -0
  15. package/dist/commands/connection-selection.js +9 -0
  16. package/dist/commands/ingest-commands.js +32 -26
  17. package/dist/commands/knowledge-commands.js +17 -28
  18. package/dist/commands/mcp-commands.js +17 -11
  19. package/dist/commands/setup-commands.js +14 -26
  20. package/dist/commands/sl-commands.js +27 -32
  21. package/dist/doctor.test.js +7 -8
  22. package/dist/example-smoke.test.js +3 -3
  23. package/dist/index.test.js +102 -70
  24. package/dist/ingest-depth.js +0 -1
  25. package/dist/ingest.test-utils.js +2 -2
  26. package/dist/ingest.test.js +4 -4
  27. package/dist/io/print-list.test.js +4 -4
  28. package/dist/knowledge.js +1 -1
  29. package/dist/managed-local-embeddings.d.ts +2 -0
  30. package/dist/managed-local-embeddings.js +2 -0
  31. package/dist/managed-local-embeddings.test.js +2 -0
  32. package/dist/managed-mcp-daemon.js +3 -2
  33. package/dist/managed-mcp-daemon.test.js +25 -0
  34. package/dist/managed-python-command.js +2 -2
  35. package/dist/managed-python-command.test.js +4 -3
  36. package/dist/managed-python-daemon.js +3 -2
  37. package/dist/managed-python-daemon.test.js +20 -0
  38. package/dist/managed-python-runtime.d.ts +5 -1
  39. package/dist/managed-python-runtime.js +50 -6
  40. package/dist/managed-python-runtime.test.js +53 -23
  41. package/dist/memory-flow-tui.test.js +2 -2
  42. package/dist/next-steps.d.ts +6 -6
  43. package/dist/next-steps.js +4 -4
  44. package/dist/next-steps.test.js +5 -5
  45. package/dist/print-command-tree.test.js +1 -1
  46. package/dist/proxy-env.d.ts +1 -0
  47. package/dist/proxy-env.js +23 -0
  48. package/dist/proxy-env.test.d.ts +1 -0
  49. package/dist/proxy-env.test.js +17 -0
  50. package/dist/public-ingest.js +3 -5
  51. package/dist/public-ingest.test.js +7 -3
  52. package/dist/runtime.test.js +2 -1
  53. package/dist/scan.test.js +2 -2
  54. package/dist/setup-agents.js +6 -4
  55. package/dist/setup-agents.test.js +35 -1
  56. package/dist/setup-embeddings.d.ts +1 -0
  57. package/dist/setup-embeddings.js +29 -7
  58. package/dist/setup-embeddings.test.js +49 -7
  59. package/dist/setup-models.d.ts +0 -1
  60. package/dist/setup-models.js +2 -3
  61. package/dist/setup-models.test.js +8 -10
  62. package/dist/setup-project.d.ts +9 -1
  63. package/dist/setup-project.js +52 -25
  64. package/dist/setup-project.test.js +8 -8
  65. package/dist/setup-runtime.test.js +4 -2
  66. package/dist/setup.d.ts +1 -2
  67. package/dist/setup.js +21 -5
  68. package/dist/setup.test.js +160 -43
  69. package/dist/sl.js +1 -1
  70. package/dist/sl.test.js +2 -1
  71. package/dist/standalone-smoke.test.js +8 -5
  72. package/dist/status-project.js +1 -10
  73. package/node_modules/@ktx/context/dist/index-sync/index.d.ts +2 -0
  74. package/node_modules/@ktx/context/dist/index-sync/index.js +1 -0
  75. package/node_modules/@ktx/context/dist/index-sync/reindex.d.ts +20 -0
  76. package/node_modules/@ktx/context/dist/index-sync/reindex.js +141 -0
  77. package/node_modules/@ktx/context/dist/index-sync/reindex.test.d.ts +1 -0
  78. package/node_modules/@ktx/context/dist/index-sync/reindex.test.js +139 -0
  79. package/node_modules/@ktx/context/dist/index-sync/types.d.ts +29 -0
  80. package/node_modules/@ktx/context/dist/index-sync/types.js +1 -0
  81. package/node_modules/@ktx/context/dist/index.d.ts +1 -0
  82. package/node_modules/@ktx/context/dist/index.js +1 -0
  83. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/local-ingest-acceptance.test.js +1 -1
  84. package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +8 -8
  85. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +4 -1
  86. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +3 -3
  87. package/node_modules/@ktx/context/dist/ingest/local-embedding-provider.integration.test.js +9 -10
  88. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +2 -2
  89. package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +2 -2
  90. package/node_modules/@ktx/context/dist/llm/local-config.js +2 -15
  91. package/node_modules/@ktx/context/dist/llm/local-config.test.js +3 -7
  92. package/node_modules/@ktx/context/dist/memory/local-memory.js +9 -3
  93. package/node_modules/@ktx/context/dist/project/config.d.ts +0 -5
  94. package/node_modules/@ktx/context/dist/project/config.js +5 -5
  95. package/node_modules/@ktx/context/dist/project/config.test.js +4 -7
  96. package/node_modules/@ktx/context/dist/scan/enrichment-state.test.js +4 -4
  97. package/node_modules/@ktx/context/dist/scan/index.d.ts +1 -1
  98. package/node_modules/@ktx/context/dist/scan/local-enrichment.d.ts +2 -6
  99. package/node_modules/@ktx/context/dist/scan/local-enrichment.js +31 -47
  100. package/node_modules/@ktx/context/dist/scan/local-enrichment.test.js +35 -18
  101. package/node_modules/@ktx/context/dist/scan/local-scan.test.js +2 -3
  102. package/node_modules/@ktx/context/dist/sl/ports.d.ts +3 -3
  103. package/node_modules/@ktx/context/dist/sl/sl-search.service.d.ts +3 -2
  104. package/node_modules/@ktx/context/dist/sl/sl-search.service.js +47 -45
  105. package/node_modules/@ktx/context/dist/sl/sl-search.service.test.js +61 -0
  106. package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.d.ts +4 -3
  107. package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.js +15 -5
  108. package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.test.js +24 -0
  109. package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.d.ts +3 -2
  110. package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.js +62 -51
  111. package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.test.js +59 -3
  112. package/node_modules/@ktx/context/dist/wiki/ports.d.ts +3 -3
  113. package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.d.ts +33 -0
  114. package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.js +155 -2
  115. package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.test.js +26 -0
  116. package/node_modules/@ktx/context/package.json +5 -0
  117. package/node_modules/@ktx/llm/dist/embedding-provider.d.ts +0 -7
  118. package/node_modules/@ktx/llm/dist/embedding-provider.js +12 -138
  119. package/node_modules/@ktx/llm/dist/embedding-provider.test.js +10 -25
  120. package/node_modules/@ktx/llm/dist/types.d.ts +1 -1
  121. package/package.json +1 -1
  122. /package/dist/{dev.test.d.ts → admin-reindex.test.d.ts} +0 -0
package/dist/setup.d.ts CHANGED
@@ -52,7 +52,7 @@ export interface KtxSetupStatus {
52
52
  export type KtxSetupArgs = {
53
53
  command: 'run';
54
54
  projectDir: string;
55
- mode: 'auto' | 'new' | 'existing';
55
+ mode: 'auto';
56
56
  agents: boolean;
57
57
  target?: KtxAgentTarget;
58
58
  agentScope?: KtxAgentScope;
@@ -64,7 +64,6 @@ export type KtxSetupArgs = {
64
64
  anthropicApiKeyEnv?: string;
65
65
  anthropicApiKeyFile?: string;
66
66
  llmModel?: string;
67
- anthropicModel?: string;
68
67
  vertexProject?: string;
69
68
  vertexLocation?: string;
70
69
  skipLlm: boolean;
package/dist/setup.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { existsSync } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
2
3
  import { basename, join, resolve } from 'node:path';
3
4
  import { getLatestLocalIngestStatus, savedMemoryCountsForReport } from '@ktx/context/ingest';
4
5
  import { ktxLocalStateDbPath, loadKtxProject, readKtxSetupState, } from '@ktx/context/project';
@@ -11,7 +12,7 @@ import { readKtxAgentInstallManifest, runKtxSetupAgentsStep, targetDisplayName,
11
12
  import { runKtxSetupDatabasesStep, } from './setup-databases.js';
12
13
  import { runKtxSetupEmbeddingsStep } from './setup-embeddings.js';
13
14
  import { isKtxSetupLlmConfigReady, runKtxSetupAnthropicModelStep, } from './setup-models.js';
14
- import { runKtxSetupProjectStep } from './setup-project.js';
15
+ import { runKtxSetupProjectStep, } from './setup-project.js';
15
16
  import { isKtxPreAgentSetupReady, isKtxSetupReady, runKtxSetupReadyChangeMenu, } from './setup-ready-menu.js';
16
17
  import { runKtxSetupSourcesStep } from './setup-sources.js';
17
18
  import { runKtxSetupRuntimeStep, } from './setup-runtime.js';
@@ -54,7 +55,6 @@ async function runKtxSetupDemoFromEntryMenu(args, io, deps) {
54
55
  function embeddingsReady(status) {
55
56
  return (status.backend !== undefined &&
56
57
  status.backend !== 'none' &&
57
- status.backend !== 'deterministic' &&
58
58
  typeof status.model === 'string' &&
59
59
  status.model.length > 0 &&
60
60
  typeof status.dimensions === 'number' &&
@@ -270,6 +270,17 @@ async function commitSetupConfigChanges(projectDir) {
270
270
  const project = await loadKtxProject({ projectDir });
271
271
  await project.git.commitFile('ktx.yaml', 'setup: update KTX project config', 'ktx setup', 'setup@ktx.local');
272
272
  }
273
+ const KTX_SETUP_SCAFFOLD_PATHS = ['ktx.yaml', '.ktx', 'wiki', 'semantic-layer', 'raw-sources', '.git'];
274
+ async function cleanupCreatedProjectScaffold(cleanup) {
275
+ if (!cleanup) {
276
+ return;
277
+ }
278
+ if (cleanup.kind === 'remove-project-dir') {
279
+ await rm(cleanup.projectDir, { recursive: true, force: true });
280
+ return;
281
+ }
282
+ await Promise.all(KTX_SETUP_SCAFFOLD_PATHS.map((relativePath) => rm(join(cleanup.projectDir, relativePath), { recursive: true, force: true })));
283
+ }
273
284
  export async function runKtxSetup(args, io, deps = {}) {
274
285
  try {
275
286
  return await runKtxSetupInner(args, io, deps);
@@ -390,7 +401,6 @@ async function runKtxSetupInner(args, io, deps = {}) {
390
401
  ...(args.anthropicApiKeyEnv ? { anthropicApiKeyEnv: args.anthropicApiKeyEnv } : {}),
391
402
  ...(args.anthropicApiKeyFile ? { anthropicApiKeyFile: args.anthropicApiKeyFile } : {}),
392
403
  ...(args.llmModel ? { llmModel: args.llmModel } : {}),
393
- ...(args.anthropicModel ? { anthropicModel: args.anthropicModel } : {}),
394
404
  ...(args.vertexProject ? { vertexProject: args.vertexProject } : {}),
395
405
  ...(args.vertexLocation ? { vertexLocation: args.vertexLocation } : {}),
396
406
  forcePrompt: forcePromptSteps.has('models') || runOnly === 'models',
@@ -493,7 +503,9 @@ async function runKtxSetupInner(args, io, deps = {}) {
493
503
  const agentsRunner = deps.agents ?? ((agentArgs, agentIo) => runKtxSetupAgentsStep(agentArgs, agentIo, deps.agentsDeps));
494
504
  const agentResult = await agentsRunner({
495
505
  projectDir: projectResult.projectDir,
496
- inputMode: args.inputMode,
506
+ inputMode: args.inputMode === 'auto' && io.stdout.isTTY !== true && deps.agentsDeps?.prompts === undefined
507
+ ? 'disabled'
508
+ : args.inputMode,
497
509
  yes: args.yes,
498
510
  agents: true,
499
511
  ...(args.target ? { target: args.target } : {}),
@@ -507,7 +519,11 @@ async function runKtxSetupInner(args, io, deps = {}) {
507
519
  agentNextActions = agentResult.nextActions;
508
520
  }
509
521
  }
510
- if (stepResult.status === 'failed' || stepResult.status === 'missing-input') {
522
+ if (stepResult.status === 'failed') {
523
+ await cleanupCreatedProjectScaffold(projectResult.createdProjectCleanup);
524
+ return 1;
525
+ }
526
+ if (stepResult.status === 'missing-input') {
511
527
  return 1;
512
528
  }
513
529
  if (stepResult.status === 'back') {
@@ -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';
@@ -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');
@@ -387,7 +385,7 @@ describe('setup status', () => {
387
385
  await expect(runKtxSetup({
388
386
  command: 'run',
389
387
  projectDir: tempDir,
390
- mode: 'new',
388
+ mode: 'auto',
391
389
  agents: false,
392
390
  target: 'claude-code',
393
391
  skipAgents: false,
@@ -456,6 +454,85 @@ describe('setup status', () => {
456
454
  expect(testIo.stdout()).not.toContain('Optional MCP:');
457
455
  expect(testIo.stderr()).toBe('');
458
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
+ });
459
536
  it('shows demo near the bottom of the first setup intent menu before project creation', async () => {
460
537
  const testIo = makeIo();
461
538
  const select = vi.fn(async (options) => {
@@ -750,16 +827,16 @@ describe('setup status', () => {
750
827
  }, testIo.io, { entryMenuDeps: { prompts: { select: vi.fn(async () => 'demo'), cancel: vi.fn() } } })).resolves.toBe(0);
751
828
  expect(runDemoTour).toHaveBeenCalledWith({ inputMode: 'auto' }, testIo.io, expect.objectContaining({}));
752
829
  });
753
- 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 () => {
754
831
  const testIo = makeIo();
755
832
  await expect(runKtxSetup({
756
833
  command: 'run',
757
834
  projectDir: tempDir,
758
- mode: 'new',
835
+ mode: 'auto',
759
836
  agents: false,
760
837
  skipAgents: true,
761
838
  inputMode: 'disabled',
762
- yes: false,
839
+ yes: true,
763
840
  cliVersion: '0.2.0',
764
841
  skipLlm: true,
765
842
  skipEmbeddings: true,
@@ -821,14 +898,14 @@ describe('setup status', () => {
821
898
  await expect(runKtxSetup({
822
899
  command: 'run',
823
900
  projectDir: tempDir,
824
- mode: 'new',
901
+ mode: 'auto',
825
902
  agents: false,
826
903
  skipAgents: true,
827
904
  inputMode: 'disabled',
828
- yes: false,
905
+ yes: true,
829
906
  cliVersion: '0.2.0',
830
907
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
831
- anthropicModel: 'claude-sonnet-4-6',
908
+ llmModel: 'claude-sonnet-4-6',
832
909
  skipLlm: false,
833
910
  skipEmbeddings: true,
834
911
  databaseSchemas: [],
@@ -839,7 +916,7 @@ describe('setup status', () => {
839
916
  projectDir: tempDir,
840
917
  inputMode: 'disabled',
841
918
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
842
- anthropicModel: 'claude-sonnet-4-6',
919
+ llmModel: 'claude-sonnet-4-6',
843
920
  skipLlm: false,
844
921
  }), testIo.io);
845
922
  });
@@ -849,16 +926,16 @@ describe('setup status', () => {
849
926
  await expect(runKtxSetup({
850
927
  command: 'run',
851
928
  projectDir: tempDir,
852
- mode: 'new',
929
+ mode: 'auto',
853
930
  agents: false,
854
931
  skipAgents: true,
855
932
  inputMode: 'disabled',
856
- yes: false,
933
+ yes: true,
857
934
  cliVersion: '0.2.0',
858
935
  llmBackend: 'vertex',
859
936
  vertexProject: 'local-gcp-project',
860
937
  vertexLocation: 'us-east5',
861
- anthropicModel: 'claude-sonnet-4-6',
938
+ llmModel: 'claude-sonnet-4-6',
862
939
  skipLlm: false,
863
940
  skipEmbeddings: true,
864
941
  databaseSchemas: [],
@@ -871,7 +948,7 @@ describe('setup status', () => {
871
948
  llmBackend: 'vertex',
872
949
  vertexProject: 'local-gcp-project',
873
950
  vertexLocation: 'us-east5',
874
- anthropicModel: 'claude-sonnet-4-6',
951
+ llmModel: 'claude-sonnet-4-6',
875
952
  skipLlm: false,
876
953
  }), testIo.io);
877
954
  });
@@ -882,14 +959,14 @@ describe('setup status', () => {
882
959
  await expect(runKtxSetup({
883
960
  command: 'run',
884
961
  projectDir: tempDir,
885
- mode: 'new',
962
+ mode: 'auto',
886
963
  agents: false,
887
964
  skipAgents: true,
888
965
  inputMode: 'disabled',
889
966
  yes: true,
890
967
  cliVersion: '0.2.0',
891
968
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
892
- anthropicModel: 'claude-sonnet-4-6',
969
+ llmModel: 'claude-sonnet-4-6',
893
970
  skipLlm: false,
894
971
  embeddingBackend: 'openai',
895
972
  embeddingApiKeyEnv: 'OPENAI_API_KEY', // pragma: allowlist secret
@@ -911,10 +988,11 @@ describe('setup status', () => {
911
988
  it('passes no-input runtime policy to the embeddings step', async () => {
912
989
  const io = makeIo();
913
990
  const embeddings = vi.fn(async () => ({ status: 'failed', projectDir: tempDir }));
991
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
914
992
  await expect(runKtxSetup({
915
993
  command: 'run',
916
994
  projectDir: tempDir,
917
- mode: 'new',
995
+ mode: 'auto',
918
996
  agents: false,
919
997
  agentScope: 'project',
920
998
  skipAgents: true,
@@ -936,10 +1014,11 @@ describe('setup status', () => {
936
1014
  const io = makeIo();
937
1015
  const embeddings = vi.fn(async () => ({ status: 'ready', projectDir: tempDir }));
938
1016
  const context = vi.fn(async () => ({ status: 'failed', projectDir: tempDir }));
1017
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
939
1018
  await expect(runKtxSetup({
940
1019
  command: 'run',
941
1020
  projectDir: tempDir,
942
- mode: 'new',
1021
+ mode: 'auto',
943
1022
  agents: false,
944
1023
  agentScope: 'project',
945
1024
  skipAgents: true,
@@ -966,6 +1045,7 @@ describe('setup status', () => {
966
1045
  });
967
1046
  it('lets Back from embedding setup return to the model step instead of exiting', async () => {
968
1047
  const testIo = makeIo();
1048
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
969
1049
  const modelResults = [
970
1050
  { status: 'ready', projectDir: tempDir },
971
1051
  { status: 'back', projectDir: tempDir },
@@ -975,7 +1055,7 @@ describe('setup status', () => {
975
1055
  await expect(runKtxSetup({
976
1056
  command: 'run',
977
1057
  projectDir: tempDir,
978
- mode: 'new',
1058
+ mode: 'auto',
979
1059
  agents: false,
980
1060
  skipAgents: true,
981
1061
  inputMode: 'auto',
@@ -993,6 +1073,7 @@ describe('setup status', () => {
993
1073
  });
994
1074
  it('lets Back from database selection return to embedding setup', async () => {
995
1075
  const testIo = makeIo();
1076
+ await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
996
1077
  const modelResults = [
997
1078
  { status: 'ready', projectDir: tempDir },
998
1079
  { status: 'back', projectDir: tempDir },
@@ -1013,11 +1094,11 @@ describe('setup status', () => {
1013
1094
  await expect(runKtxSetup({
1014
1095
  command: 'run',
1015
1096
  projectDir: tempDir,
1016
- mode: 'new',
1097
+ mode: 'auto',
1017
1098
  agents: false,
1018
1099
  skipAgents: true,
1019
1100
  inputMode: 'auto',
1020
- yes: false,
1101
+ yes: true,
1021
1102
  cliVersion: '0.2.0',
1022
1103
  skipLlm: false,
1023
1104
  skipEmbeddings: false,
@@ -1050,7 +1131,7 @@ describe('setup status', () => {
1050
1131
  agents: false,
1051
1132
  skipAgents: true,
1052
1133
  inputMode: 'auto',
1053
- yes: false,
1134
+ yes: true,
1054
1135
  cliVersion: '0.2.0',
1055
1136
  skipLlm: false,
1056
1137
  skipEmbeddings: true,
@@ -1078,14 +1159,14 @@ describe('setup status', () => {
1078
1159
  await expect(runKtxSetup({
1079
1160
  command: 'run',
1080
1161
  projectDir: tempDir,
1081
- mode: 'new',
1162
+ mode: 'auto',
1082
1163
  agents: false,
1083
1164
  skipAgents: true,
1084
1165
  inputMode: 'disabled',
1085
- yes: false,
1166
+ yes: true,
1086
1167
  cliVersion: '0.2.0',
1087
1168
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
1088
- anthropicModel: 'claude-sonnet-4-6',
1169
+ llmModel: 'claude-sonnet-4-6',
1089
1170
  skipLlm: false,
1090
1171
  embeddingBackend: 'openai',
1091
1172
  embeddingApiKeyEnv: 'OPENAI_API_KEY', // pragma: allowlist secret
@@ -1124,7 +1205,7 @@ describe('setup status', () => {
1124
1205
  await expect(runKtxSetup({
1125
1206
  command: 'run',
1126
1207
  projectDir: tempDir,
1127
- mode: 'existing',
1208
+ mode: 'auto',
1128
1209
  agents: false,
1129
1210
  skipAgents: true,
1130
1211
  inputMode: 'disabled',
@@ -1187,7 +1268,7 @@ describe('setup status', () => {
1187
1268
  await expect(runKtxSetup({
1188
1269
  command: 'run',
1189
1270
  projectDir: tempDir,
1190
- mode: 'existing',
1271
+ mode: 'auto',
1191
1272
  agents: false,
1192
1273
  skipAgents: true,
1193
1274
  inputMode: 'disabled',
@@ -1224,7 +1305,7 @@ describe('setup status', () => {
1224
1305
  await expect(runKtxSetup({
1225
1306
  command: 'run',
1226
1307
  projectDir: tempDir,
1227
- mode: 'existing',
1308
+ mode: 'auto',
1228
1309
  agents: false,
1229
1310
  skipAgents: true,
1230
1311
  inputMode: 'disabled',
@@ -1263,7 +1344,7 @@ describe('setup status', () => {
1263
1344
  await expect(runKtxSetup({
1264
1345
  command: 'run',
1265
1346
  projectDir: tempDir,
1266
- mode: 'existing',
1347
+ mode: 'auto',
1267
1348
  agents: false,
1268
1349
  inputMode: 'disabled',
1269
1350
  yes: true,
@@ -1315,7 +1396,7 @@ describe('setup status', () => {
1315
1396
  await expect(runKtxSetup({
1316
1397
  command: 'run',
1317
1398
  projectDir: tempDir,
1318
- mode: 'new',
1399
+ mode: 'auto',
1319
1400
  agents: false,
1320
1401
  inputMode: 'disabled',
1321
1402
  yes: true,
@@ -1356,7 +1437,7 @@ describe('setup status', () => {
1356
1437
  await expect(runKtxSetup({
1357
1438
  command: 'run',
1358
1439
  projectDir: tempDir,
1359
- mode: 'existing',
1440
+ mode: 'auto',
1360
1441
  agents: true,
1361
1442
  target: 'codex',
1362
1443
  agentScope: 'project',
@@ -1406,7 +1487,7 @@ describe('setup status', () => {
1406
1487
  await expect(runKtxSetup({
1407
1488
  command: 'run',
1408
1489
  projectDir: tempDir,
1409
- mode: 'existing',
1490
+ mode: 'auto',
1410
1491
  agents: true,
1411
1492
  target: 'codex',
1412
1493
  agentScope: 'project',
@@ -1429,6 +1510,41 @@ describe('setup status', () => {
1429
1510
  expect(agents).toHaveBeenCalledTimes(1);
1430
1511
  expect(io.stderr()).not.toContain('KTX context is not ready for agents.');
1431
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');
1547
+ });
1432
1548
  it('routes a ready project menu selection to agent setup', async () => {
1433
1549
  const calls = [];
1434
1550
  const io = makeIo();
@@ -1478,7 +1594,7 @@ describe('setup status', () => {
1478
1594
  await expect(runKtxSetup({
1479
1595
  command: 'run',
1480
1596
  projectDir: tempDir,
1481
- mode: 'existing',
1597
+ mode: 'auto',
1482
1598
  agents: false,
1483
1599
  inputMode: 'auto',
1484
1600
  yes: false,
@@ -1570,7 +1686,7 @@ describe('setup status', () => {
1570
1686
  await expect(runKtxSetup({
1571
1687
  command: 'run',
1572
1688
  projectDir: tempDir,
1573
- mode: 'existing',
1689
+ mode: 'auto',
1574
1690
  agents: false,
1575
1691
  inputMode: 'auto',
1576
1692
  yes: false,
@@ -1627,7 +1743,7 @@ describe('setup status', () => {
1627
1743
  await expect(runKtxSetup({
1628
1744
  command: 'run',
1629
1745
  projectDir: tempDir,
1630
- mode: 'new',
1746
+ mode: 'auto',
1631
1747
  agents: true,
1632
1748
  target: 'universal',
1633
1749
  agentScope: 'project',
@@ -1659,19 +1775,20 @@ describe('setup status', () => {
1659
1775
  await expect(runKtxSetup({
1660
1776
  command: 'run',
1661
1777
  projectDir: tempDir,
1662
- mode: 'new',
1778
+ mode: 'auto',
1663
1779
  agents: false,
1664
1780
  skipAgents: true,
1665
1781
  inputMode: 'disabled',
1666
- yes: false,
1782
+ yes: true,
1667
1783
  cliVersion: '0.2.0',
1668
1784
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
1669
- anthropicModel: 'claude-sonnet-4-6',
1785
+ llmModel: 'claude-sonnet-4-6',
1670
1786
  skipLlm: false,
1671
1787
  skipEmbeddings: false,
1672
1788
  databaseSchemas: [],
1673
1789
  skipDatabases: true,
1674
1790
  }, testIo.io, { model, embeddings })).resolves.toBe(1);
1791
+ expect(model).toHaveBeenCalledTimes(1);
1675
1792
  expect(embeddings).not.toHaveBeenCalled();
1676
1793
  });
1677
1794
  });
package/dist/sl.js CHANGED
@@ -99,7 +99,7 @@ export async function runKtxSl(args, io = process, deps = {}) {
99
99
  await printSlSources({
100
100
  rows: sources,
101
101
  emptyMessage: `No semantic-layer sources matched "${args.query}" in ${project.projectDir}`,
102
- emptyHint: 'Run `ktx sl list` to inspect available sources.',
102
+ emptyHint: 'Run `ktx sl` to inspect available sources.',
103
103
  command: 'sl search',
104
104
  output: args.output,
105
105
  json: args.json,
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',
@@ -76,7 +76,7 @@ async function writeSqliteScanConfig(projectDir, dbPath, enrich = false) {
76
76
  ' enrichment:',
77
77
  ' mode: deterministic',
78
78
  ' embeddings:',
79
- ' backend: deterministic',
79
+ ' backend: none',
80
80
  ' dimensions: 6',
81
81
  ]
82
82
  : []),
@@ -94,7 +94,6 @@ async function runSetupNewProject(projectDir) {
94
94
  'setup',
95
95
  '--project-dir',
96
96
  projectDir,
97
- '--new',
98
97
  '--no-input',
99
98
  '--yes',
100
99
  '--skip-llm',
@@ -117,6 +116,10 @@ describe('standalone built ktx CLI smoke', () => {
117
116
  const init = await runSetupNewProject(projectDir);
118
117
  expectSetupStderr(init);
119
118
  expect(init.stdout).toContain(`Project: ${projectDir}`);
119
+ const reindex = await runBuiltCli(['--project-dir', projectDir, 'admin', 'reindex', '--output', 'plain']);
120
+ expect(reindex.code).toBe(0);
121
+ expect(reindex.stdout).toContain('reindex\t');
122
+ expect(reindex.stderr).toContain('wiki/global');
120
123
  const run = await runBuiltCli([
121
124
  'ingest',
122
125
  'run',
@@ -126,7 +129,7 @@ describe('standalone built ktx CLI smoke', () => {
126
129
  'fake',
127
130
  ]);
128
131
  expect(run).toMatchObject({ code: 1, stdout: '' });
129
- expect(run.stderr).toContain("unknown option '--connection-id'");
132
+ expect(run.stderr).toContain("unknown option '--adapter'");
130
133
  });
131
134
  it('rejects the removed agent command through the built binary', async () => {
132
135
  const result = await runBuiltCli(['agent']);
@@ -135,7 +138,7 @@ describe('standalone built ktx CLI smoke', () => {
135
138
  expect(result.stderr).toContain("unknown command 'agent'");
136
139
  });
137
140
  it('runs status setup checks through the built binary', async () => {
138
- const result = await runBuiltCli(['status', '--verbose', '--no-input']);
141
+ const result = await runBuiltCli(['status', '--verbose', '--no-input'], { cwd: tempDir });
139
142
  expect(result.stdout).toMatch(/KTX status/);
140
143
  if (result.stdout.includes('No project here yet.')) {
141
144
  expect(result.stdout).toContain('ktx setup');
@@ -236,7 +239,7 @@ describe('standalone built ktx CLI smoke', () => {
236
239
  ]);
237
240
  expect(add.code).toBe(1);
238
241
  expect(add.stdout).toBe('');
239
- expect(add.stderr).toContain("unknown command 'add'");
242
+ expect(add.stderr).toMatch(/unknown (command|option)|too many arguments/);
240
243
  const yaml = await readFile(join(projectDir, 'ktx.yaml'), 'utf-8');
241
244
  expect(yaml).not.toContain('driver: notion');
242
245
  expect(yaml).not.toContain('auth_token_ref: env:NOTION_TOKEN');
@@ -120,15 +120,6 @@ function buildEmbeddingsStatus(config, env) {
120
120
  detail: 'disabled — semantic search will be skipped',
121
121
  };
122
122
  }
123
- if (backend === 'deterministic') {
124
- return {
125
- backend,
126
- model,
127
- dimensions,
128
- status: 'warn',
129
- detail: 'deterministic — semantic search degraded (lexical/dictionary lanes still work)',
130
- };
131
- }
132
123
  if (backend === 'openai') {
133
124
  const ref = config.openai?.api_key;
134
125
  const resolved = resolveRef(ref, env);
@@ -469,7 +460,7 @@ function buildVerdict(llm, embeddings, connections, queryHistory, warnings) {
469
460
  if (llm.status === 'warn')
470
461
  reasons.push('LLM credentials missing');
471
462
  if (embeddings.status === 'warn') {
472
- if (embeddings.backend === 'deterministic' || embeddings.backend === 'none') {
463
+ if (embeddings.backend === 'none') {
473
464
  reasons.push('semantic search disabled');
474
465
  }
475
466
  else {
@@ -0,0 +1,2 @@
1
+ export type { ReindexOptions, ReindexScopeResult, ReindexSummary, ReindexWorkResult } from './types.js';
2
+ export { discoverReindexScopes, reindexLocalIndexes } from './reindex.js';
@@ -0,0 +1 @@
1
+ export { discoverReindexScopes, reindexLocalIndexes } from './reindex.js';