@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
@@ -98,9 +98,10 @@ describe('runKtxCli', () => {
98
98
  await expect(runKtxCli(['--help'], testIo.io)).resolves.toBe(0);
99
99
  expect(testIo.stdout()).toContain('Usage: ktx [options] [command]');
100
100
  expect(testIo.stdout()).toContain('KTX data agent context layer CLI');
101
- for (const command of ['setup', 'connection', 'ingest', 'wiki', 'sl', 'status', 'dev']) {
101
+ for (const command of ['setup', 'connection', 'ingest', 'wiki', 'sl', 'status', 'admin']) {
102
102
  expect(testIo.stdout()).toContain(`${command}`);
103
103
  }
104
+ expect(testIo.stdout()).not.toMatch(/^ dev\s/m);
104
105
  expect(testIo.stdout()).not.toMatch(/^ scan\s/m);
105
106
  for (const removed of ['demo', 'init', 'connect', 'ask', 'knowledge', 'agent', 'completion', 'serve']) {
106
107
  expect(testIo.stdout()).not.toMatch(new RegExp(`^\\s+${removed}(?:\\s|\\[|$)`, 'm'));
@@ -115,7 +116,7 @@ describe('runKtxCli', () => {
115
116
  it('routes supported public wiki commands', async () => {
116
117
  const knowledge = vi.fn(async () => 0);
117
118
  const listIo = makeIo();
118
- await expect(runKtxCli(['--project-dir', tempDir, 'wiki', 'list', '--json'], listIo.io, { knowledge }))
119
+ await expect(runKtxCli(['--project-dir', tempDir, 'wiki', '--json'], listIo.io, { knowledge }))
119
120
  .resolves.toBe(0);
120
121
  expect(knowledge).toHaveBeenCalledWith({
121
122
  command: 'list',
@@ -124,7 +125,7 @@ describe('runKtxCli', () => {
124
125
  json: true,
125
126
  }, listIo.io);
126
127
  const searchIo = makeIo();
127
- await expect(runKtxCli(['--project-dir', tempDir, 'wiki', 'search', 'revenue', '--limit', '5'], searchIo.io, { knowledge })).resolves.toBe(0);
128
+ await expect(runKtxCli(['--project-dir', tempDir, 'wiki', 'revenue', '--limit', '5'], searchIo.io, { knowledge })).resolves.toBe(0);
128
129
  expect(knowledge).toHaveBeenLastCalledWith({
129
130
  command: 'search',
130
131
  projectDir: tempDir,
@@ -134,7 +135,7 @@ describe('runKtxCli', () => {
134
135
  limit: 5,
135
136
  }, searchIo.io);
136
137
  const debugSearchIo = makeIo();
137
- await expect(runKtxCli(['--project-dir', tempDir, '--debug', 'wiki', 'search', 'revenue'], debugSearchIo.io, { knowledge })).resolves.toBe(0);
138
+ await expect(runKtxCli(['--project-dir', tempDir, '--debug', 'wiki', 'revenue'], debugSearchIo.io, { knowledge })).resolves.toBe(0);
138
139
  expect(knowledge).toHaveBeenLastCalledWith({
139
140
  command: 'search',
140
141
  projectDir: tempDir,
@@ -143,35 +144,32 @@ describe('runKtxCli', () => {
143
144
  json: false,
144
145
  debug: true,
145
146
  }, debugSearchIo.io);
147
+ const multiWordIo = makeIo();
148
+ await expect(runKtxCli(['--project-dir', tempDir, 'wiki', 'revenue', 'policy'], multiWordIo.io, { knowledge })).resolves.toBe(0);
149
+ expect(knowledge).toHaveBeenLastCalledWith({
150
+ command: 'search',
151
+ projectDir: tempDir,
152
+ query: 'revenue policy',
153
+ userId: 'local',
154
+ json: false,
155
+ }, multiWordIo.io);
146
156
  });
147
- it('rejects removed public wiki read and write commands', async () => {
157
+ it('rejects unknown write-style flags on the flattened wiki and sl commands', async () => {
148
158
  const knowledge = vi.fn(async () => 0);
149
- for (const argv of [
150
- ['--project-dir', tempDir, 'wiki', 'read', 'revenue', '--json'],
151
- ['--project-dir', tempDir, 'wiki', 'write', 'revenue', '--summary', 'Revenue', '--content', 'Revenue.'],
152
- ]) {
153
- const io = makeIo();
154
- await expect(runKtxCli(argv, io.io, { knowledge })).resolves.toBe(1);
155
- expect(io.stderr()).toMatch(/unknown command|error:/);
156
- }
157
- expect(knowledge).not.toHaveBeenCalled();
158
- });
159
- it('rejects removed public sl read/write commands', async () => {
160
159
  const sl = vi.fn(async () => 0);
161
- for (const argv of [
162
- ['--project-dir', tempDir, 'sl', 'read', 'orders', '--connection-id', 'warehouse'],
163
- ['--project-dir', tempDir, 'sl', 'write', 'orders', '--connection-id', 'warehouse', '--yaml', 'name: orders'],
164
- ]) {
165
- const io = makeIo();
166
- await expect(runKtxCli(argv, io.io, { sl })).resolves.toBe(1);
167
- expect(io.stderr()).toMatch(/unknown command|error:/);
168
- }
160
+ const wikiIo = makeIo();
161
+ await expect(runKtxCli(['--project-dir', tempDir, 'wiki', 'revenue', '--summary', 'Revenue', '--content', 'Revenue.'], wikiIo.io, { knowledge })).resolves.toBe(1);
162
+ expect(wikiIo.stderr()).toMatch(/unknown option|error:/);
163
+ expect(knowledge).not.toHaveBeenCalled();
164
+ const slIo = makeIo();
165
+ await expect(runKtxCli(['--project-dir', tempDir, 'sl', 'orders', '--yaml', 'name: orders'], slIo.io, { sl })).resolves.toBe(1);
166
+ expect(slIo.stderr()).toMatch(/unknown option|error:/);
169
167
  expect(sl).not.toHaveBeenCalled();
170
168
  });
171
- it('routes sl search and rejects the old sl list --query flag', async () => {
169
+ it('routes sl search via the flattened query positional and rejects unknown flags', async () => {
172
170
  const sl = vi.fn(async () => 0);
173
171
  const searchIo = makeIo();
174
- await expect(runKtxCli(['--project-dir', tempDir, 'sl', 'search', 'revenue', '--connection-id', 'warehouse', '--limit', '5', '--json'], searchIo.io, { sl })).resolves.toBe(0);
172
+ await expect(runKtxCli(['--project-dir', tempDir, 'sl', 'revenue', '--connection-id', 'warehouse', '--limit', '5', '--json'], searchIo.io, { sl })).resolves.toBe(0);
175
173
  expect(sl).toHaveBeenCalledWith({
176
174
  command: 'search',
177
175
  projectDir: tempDir,
@@ -181,9 +179,18 @@ describe('runKtxCli', () => {
181
179
  json: true,
182
180
  output: undefined,
183
181
  }, searchIo.io);
184
- const listIo = makeIo();
185
- await expect(runKtxCli(['--project-dir', tempDir, 'sl', 'list', '--query', 'revenue'], listIo.io, { sl })).resolves.toBe(1);
186
- expect(listIo.stderr()).toContain("unknown option '--query'");
182
+ const bareIo = makeIo();
183
+ await expect(runKtxCli(['--project-dir', tempDir, 'sl', '--connection-id', 'warehouse', '--json'], bareIo.io, { sl })).resolves.toBe(0);
184
+ expect(sl).toHaveBeenLastCalledWith({
185
+ command: 'list',
186
+ projectDir: tempDir,
187
+ connectionId: 'warehouse',
188
+ json: true,
189
+ output: undefined,
190
+ }, bareIo.io);
191
+ const unknownIo = makeIo();
192
+ await expect(runKtxCli(['--project-dir', tempDir, 'sl', '--query', 'revenue'], unknownIo.io, { sl })).resolves.toBe(1);
193
+ expect(unknownIo.stderr()).toContain("unknown option '--query'");
187
194
  });
188
195
  it('routes runtime management commands with the release runtime version', async () => {
189
196
  const runtime = vi.fn(async () => 0);
@@ -193,14 +200,14 @@ describe('runKtxCli', () => {
193
200
  const stopAllIo = makeIo();
194
201
  const statusIo = makeIo();
195
202
  const pruneIo = makeIo();
196
- await expect(runKtxCli(['dev', 'runtime', 'install', '--feature', 'local-embeddings', '--force', '--yes'], installIo.io, {
203
+ await expect(runKtxCli(['admin', 'runtime', 'install', '--feature', 'local-embeddings', '--force', '--yes'], installIo.io, {
197
204
  runtime,
198
205
  })).resolves.toBe(0);
199
- await expect(runKtxCli(['dev', 'runtime', 'start', '--feature', 'local-embeddings', '--force'], startIo.io, { runtime })).resolves.toBe(0);
200
- await expect(runKtxCli(['dev', 'runtime', 'stop'], stopIo.io, { runtime })).resolves.toBe(0);
201
- await expect(runKtxCli(['dev', 'runtime', 'stop', '--all'], stopAllIo.io, { runtime })).resolves.toBe(0);
202
- await expect(runKtxCli(['dev', 'runtime', 'status', '--json'], statusIo.io, { runtime })).resolves.toBe(0);
203
- await expect(runKtxCli(['dev', 'runtime', 'prune', '--dry-run'], pruneIo.io, { runtime })).resolves.toBe(1);
206
+ await expect(runKtxCli(['admin', 'runtime', 'start', '--feature', 'local-embeddings', '--force'], startIo.io, { runtime })).resolves.toBe(0);
207
+ await expect(runKtxCli(['admin', 'runtime', 'stop'], stopIo.io, { runtime })).resolves.toBe(0);
208
+ await expect(runKtxCli(['admin', 'runtime', 'stop', '--all'], stopAllIo.io, { runtime })).resolves.toBe(0);
209
+ await expect(runKtxCli(['admin', 'runtime', 'status', '--json'], statusIo.io, { runtime })).resolves.toBe(0);
210
+ await expect(runKtxCli(['admin', 'runtime', 'prune', '--dry-run'], pruneIo.io, { runtime })).resolves.toBe(1);
204
211
  expect(runtime).toHaveBeenNthCalledWith(1, {
205
212
  command: 'install',
206
213
  cliVersion: '0.1.0-rc.1',
@@ -262,7 +269,7 @@ describe('runKtxCli', () => {
262
269
  });
263
270
  it('documents runtime stop all in command help', async () => {
264
271
  const testIo = makeIo();
265
- await expect(runKtxCli(['dev', 'runtime', 'stop', '--help'], testIo.io)).resolves.toBe(0);
272
+ await expect(runKtxCli(['admin', 'runtime', 'stop', '--help'], testIo.io)).resolves.toBe(0);
266
273
  expect(testIo.stdout()).toContain('--all');
267
274
  expect(testIo.stdout()).toContain('Stop all KTX daemon processes recorded or discoverable');
268
275
  expect(testIo.stdout()).toContain('on this machine');
@@ -318,8 +325,6 @@ describe('runKtxCli', () => {
318
325
  expect(stdout).not.toContain('setup demo');
319
326
  expect(stdout).not.toContain('setup context');
320
327
  for (const hiddenFlag of [
321
- '--new',
322
- '--existing',
323
328
  '--agent-scope',
324
329
  '--skip-agents',
325
330
  '--llm-backend',
@@ -328,7 +333,6 @@ describe('runKtxCli', () => {
328
333
  '--embedding-backend',
329
334
  '--database ',
330
335
  '--database-connection-id',
331
- '--new-database-connection-id',
332
336
  '--enable-historic-sql',
333
337
  '--historic-sql-min-executions',
334
338
  '--enable-query-history',
@@ -370,7 +374,7 @@ describe('runKtxCli', () => {
370
374
  await initKtxProject({ projectDir });
371
375
  const commands = [
372
376
  ['--project-dir', projectDir, 'status', '--json'],
373
- ['--project-dir', projectDir, 'sl', 'list', '--json'],
377
+ ['--project-dir', projectDir, 'sl', '--json'],
374
378
  ];
375
379
  for (const argv of commands) {
376
380
  const testIo = makeIo();
@@ -486,8 +490,8 @@ describe('runKtxCli', () => {
486
490
  it('rejects removed shell completion commands', async () => {
487
491
  const completionIo = makeIo();
488
492
  const hiddenIo = makeIo();
489
- await expect(runKtxCli(['dev', 'completion', 'zsh'], completionIo.io)).resolves.toBe(1);
490
- await expect(runKtxCli(['dev', '__complete', '--shell', 'zsh', '--position', '2', '--', 'ktx', 'co'], hiddenIo.io)).resolves.toBe(1);
493
+ await expect(runKtxCli(['admin', 'completion', 'zsh'], completionIo.io)).resolves.toBe(1);
494
+ await expect(runKtxCli(['admin', '__complete', '--shell', 'zsh', '--position', '2', '--', 'ktx', 'co'], hiddenIo.io)).resolves.toBe(1);
491
495
  expect(completionIo.stderr()).toMatch(/unknown command|error:/);
492
496
  expect(hiddenIo.stderr()).toMatch(/unknown command|error:/);
493
497
  });
@@ -612,8 +616,12 @@ describe('runKtxCli', () => {
612
616
  it('rejects removed setup options', async () => {
613
617
  const setup = vi.fn(async () => 0);
614
618
  const cases = [
619
+ ['setup', '--new'],
620
+ ['setup', '--existing'],
615
621
  ['setup', '--project'],
616
622
  ['setup', '--agent-scope', 'global'],
623
+ ['setup', '--anthropic-model', 'claude-sonnet-4-6'],
624
+ ['setup', '--new-database-connection-id', 'warehouse'],
617
625
  ['setup', '--skip-initial-source-ingest'],
618
626
  ];
619
627
  for (const args of cases) {
@@ -635,7 +643,8 @@ describe('runKtxCli', () => {
635
643
  expect(testIo.stdout()).toContain('--query-history');
636
644
  expect(testIo.stdout()).toContain('--no-query-history');
637
645
  expect(testIo.stdout()).toContain('--query-history-window-days <days>');
638
- expect(testIo.stdout()).toContain('text');
646
+ expect(testIo.stdout()).toContain('--text');
647
+ expect(testIo.stdout()).toContain('--file');
639
648
  expect(testIo.stdout()).not.toMatch(/^ status\s/m);
640
649
  expect(testIo.stdout()).not.toMatch(/^ replay\s/m);
641
650
  expect(testIo.stdout()).not.toMatch(/^ run\s/m);
@@ -651,7 +660,6 @@ describe('runKtxCli', () => {
651
660
  '--project-dir',
652
661
  tempDir,
653
662
  'ingest',
654
- 'text',
655
663
  '--text',
656
664
  'Revenue means gross receipts.',
657
665
  '--text',
@@ -674,37 +682,46 @@ describe('runKtxCli', () => {
674
682
  }, testIo.io);
675
683
  expect(testIo.stderr()).toBe('');
676
684
  });
677
- it('documents text ingest inputs without a manifest option', async () => {
685
+ it('rejects a positional connection id when --text is supplied', async () => {
678
686
  const textIngest = vi.fn(async () => 0);
687
+ const publicIngest = vi.fn(async () => 0);
679
688
  const testIo = makeIo();
680
- await expect(runKtxCli(['ingest', 'text', '--help'], testIo.io, { textIngest })).resolves.toBe(0);
681
- expect(testIo.stdout()).toContain('Usage: ktx ingest text [options] [files...]');
682
- expect(testIo.stdout()).toContain('--text <content>');
683
- expect(testIo.stdout()).toContain('--connection-id <connectionId>');
684
- expect(testIo.stdout()).toContain('--user-id <id>');
685
- expect(testIo.stdout()).toContain('--fail-fast');
686
- expect(testIo.stdout()).not.toContain('--manifest');
689
+ await expect(runKtxCli(['--project-dir', tempDir, 'ingest', 'warehouse', '--text', 'hello'], testIo.io, { textIngest, publicIngest })).resolves.toBe(1);
687
690
  expect(textIngest).not.toHaveBeenCalled();
691
+ expect(publicIngest).not.toHaveBeenCalled();
692
+ expect(testIo.stderr()).toMatch(/--text\/--file does not accept a positional connection id/);
688
693
  });
689
- it('rejects old adapter-backed ingest flags at the top level and under dev', async () => {
694
+ it('treats bare ingest as ingest --all', async () => {
695
+ const publicIngest = vi.fn().mockResolvedValue(0);
696
+ const testIo = makeIo();
697
+ await expect(runKtxCli(['--project-dir', tempDir, 'ingest', '--no-input'], testIo.io, { publicIngest })).resolves.toBe(0);
698
+ expect(publicIngest).toHaveBeenCalledWith(expect.objectContaining({
699
+ command: 'run',
700
+ projectDir: tempDir,
701
+ all: true,
702
+ }), testIo.io);
703
+ const args = publicIngest.mock.calls[0]?.[0];
704
+ expect(args.targetConnectionId).toBeUndefined();
705
+ });
706
+ it('rejects old adapter-backed ingest flags at the top level and under admin', async () => {
690
707
  const rootRunIo = makeIo();
691
708
  const devRunIo = makeIo();
692
709
  const publicIngest = vi.fn(async () => 0);
693
710
  await expect(runKtxCli(['ingest', 'run', '--connection-id', 'warehouse', '--adapter', 'metabase'], rootRunIo.io, {
694
711
  publicIngest,
695
712
  })).resolves.toBe(1);
696
- await expect(runKtxCli(['dev', 'ingest', 'run', '--connection-id', 'warehouse', '--adapter', 'metabase'], devRunIo.io, {
713
+ await expect(runKtxCli(['admin', 'ingest', 'run', '--connection-id', 'warehouse', '--adapter', 'metabase'], devRunIo.io, {
697
714
  publicIngest,
698
715
  })).resolves.toBe(1);
699
716
  expect(publicIngest).not.toHaveBeenCalled();
700
717
  expect(rootRunIo.stderr()).toMatch(/unknown option '--connection-id'|error:/);
701
718
  expect(devRunIo.stderr()).toMatch(/unknown command|error:/);
702
719
  });
703
- it('rejects removed dev doctor and removed ingest parser cases', async () => {
720
+ it('rejects removed admin doctor and removed ingest parser cases', async () => {
704
721
  const doctor = vi.fn(async () => 0);
705
722
  const doctorIo = makeIo();
706
723
  const ingestRunIo = makeIo();
707
- await expect(runKtxCli(['dev', 'doctor', 'setup', '--json', '--no-input'], doctorIo.io, { doctor })).resolves.toBe(1);
724
+ await expect(runKtxCli(['admin', 'doctor', 'setup', '--json', '--no-input'], doctorIo.io, { doctor })).resolves.toBe(1);
708
725
  await expect(runKtxCli([
709
726
  'ingest',
710
727
  'run',
@@ -778,7 +795,7 @@ describe('runKtxCli', () => {
778
795
  '--no-input',
779
796
  '--anthropic-api-key-env',
780
797
  'ANTHROPIC_API_KEY',
781
- '--anthropic-model',
798
+ '--llm-model',
782
799
  'claude-sonnet-4-6',
783
800
  ], setupIo.io, { setup })).resolves.toBe(0);
784
801
  expect(setup).toHaveBeenCalledWith(expect.objectContaining({
@@ -787,7 +804,7 @@ describe('runKtxCli', () => {
787
804
  inputMode: 'disabled',
788
805
  cliVersion: '0.1.0-rc.1',
789
806
  anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret
790
- anthropicModel: 'claude-sonnet-4-6',
807
+ llmModel: 'claude-sonnet-4-6',
791
808
  skipLlm: false,
792
809
  }), setupIo.io);
793
810
  });
@@ -805,7 +822,7 @@ describe('runKtxCli', () => {
805
822
  'local-gcp-project',
806
823
  '--vertex-location',
807
824
  'us-east5',
808
- '--anthropic-model',
825
+ '--llm-model',
809
826
  'claude-sonnet-4-6',
810
827
  ], setupIo.io, { setup })).resolves.toBe(0);
811
828
  expect(setup).toHaveBeenCalledWith(expect.objectContaining({
@@ -816,7 +833,7 @@ describe('runKtxCli', () => {
816
833
  llmBackend: 'vertex',
817
834
  vertexProject: 'local-gcp-project',
818
835
  vertexLocation: 'us-east5',
819
- anthropicModel: 'claude-sonnet-4-6',
836
+ llmModel: 'claude-sonnet-4-6',
820
837
  skipLlm: false,
821
838
  }), setupIo.io);
822
839
  });
@@ -895,7 +912,7 @@ describe('runKtxCli', () => {
895
912
  '--skip-embeddings',
896
913
  '--database',
897
914
  'postgres',
898
- '--new-database-connection-id',
915
+ '--database-connection-id',
899
916
  'warehouse',
900
917
  '--database-url',
901
918
  'env:DATABASE_URL',
@@ -928,12 +945,27 @@ describe('runKtxCli', () => {
928
945
  it('dispatches setup database connection ids that match former ingest subcommand names', async () => {
929
946
  const testIo = makeIo();
930
947
  const setup = vi.fn(async () => 0);
931
- await expect(runKtxCli(['setup', '--new-database-connection-id', 'status', '--no-input'], testIo.io, { setup })).resolves.toBe(0);
948
+ await expect(runKtxCli(['setup', '--database-connection-id', 'status', '--no-input'], testIo.io, { setup })).resolves.toBe(0);
932
949
  expect(setup).toHaveBeenCalledWith(expect.objectContaining({
933
950
  command: 'run',
934
- databaseConnectionId: 'status',
951
+ databaseConnectionIds: ['status'],
935
952
  }), testIo.io);
936
953
  });
954
+ it('dispatches non-TTY agents setup with target without requiring --no-input or --yes', async () => {
955
+ const testIo = makeIo({ stdoutIsTty: false });
956
+ const setup = vi.fn(async () => 0);
957
+ await expect(runKtxCli(['--project-dir', tempDir, 'setup', '--agents', '--target', 'claude-code'], testIo.io, { setup })).resolves.toBe(0);
958
+ expect(setup).toHaveBeenCalledWith(expect.objectContaining({
959
+ command: 'run',
960
+ projectDir: tempDir,
961
+ agents: true,
962
+ target: 'claude-code',
963
+ agentScope: 'project',
964
+ inputMode: 'auto',
965
+ yes: false,
966
+ }), testIo.io);
967
+ expect(testIo.stderr()).toBe('');
968
+ });
937
969
  it('dispatches setup source flags', async () => {
938
970
  const setup = vi.fn(async () => 0);
939
971
  const testIo = makeIo();
@@ -1221,10 +1253,10 @@ describe('runKtxCli', () => {
1221
1253
  ], serveIo.io)).resolves.toBe(1);
1222
1254
  expect(serveIo.stderr()).toMatch(/unknown command|error:/);
1223
1255
  });
1224
- it('prints dev help for bare dev commands', async () => {
1256
+ it('prints admin help for bare admin commands', async () => {
1225
1257
  const testIo = makeIo();
1226
- await expect(runKtxCli(['dev'], testIo.io)).resolves.toBe(0);
1227
- expect(testIo.stdout()).toContain('Usage: ktx dev [options] [command]');
1258
+ await expect(runKtxCli(['admin'], testIo.io)).resolves.toBe(0);
1259
+ expect(testIo.stdout()).toContain('Usage: ktx admin [options] [command]');
1228
1260
  expect(testIo.stdout()).toContain('Low-level project initialization');
1229
1261
  expect(testIo.stdout()).toContain('init');
1230
1262
  expect(testIo.stdout()).toContain('runtime');
@@ -1235,20 +1267,20 @@ describe('runKtxCli', () => {
1235
1267
  expect(testIo.stdout()).not.toContain('knowledge');
1236
1268
  expect(testIo.stderr()).toBe('');
1237
1269
  });
1238
- it('rejects removed dev command groups without invoking execution', async () => {
1270
+ it('rejects removed admin command groups without invoking execution', async () => {
1239
1271
  for (const command of ['scan', 'ingest', 'mapping']) {
1240
1272
  const testIo = makeIo();
1241
1273
  const publicIngest = vi.fn().mockResolvedValue(0);
1242
1274
  const sl = vi.fn().mockResolvedValue(0);
1243
- await expect(runKtxCli(['dev', command], testIo.io, { publicIngest, sl })).resolves.toBe(1);
1275
+ await expect(runKtxCli(['admin', command], testIo.io, { publicIngest, sl })).resolves.toBe(1);
1244
1276
  expect(testIo.stderr()).toMatch(/unknown command|error:/);
1245
1277
  expect(publicIngest).not.toHaveBeenCalled();
1246
1278
  expect(sl).not.toHaveBeenCalled();
1247
1279
  }
1248
1280
  });
1249
- it('rejects removed reserved dev subcommands', async () => {
1281
+ it('rejects removed reserved admin subcommands', async () => {
1250
1282
  const testIo = makeIo();
1251
- await expect(runKtxCli(['dev', 'artifacts'], testIo.io)).resolves.toBe(1);
1283
+ await expect(runKtxCli(['admin', 'artifacts'], testIo.io)).resolves.toBe(1);
1252
1284
  expect(testIo.stderr()).toMatch(/unknown command|error:/);
1253
1285
  });
1254
1286
  it('rejects mutually exclusive public ingest output modes before invoking runners', async () => {
@@ -46,7 +46,6 @@ export function deepReadinessGaps(config) {
46
46
  const embeddings = config.scan.enrichment.embeddings;
47
47
  if (!embeddings ||
48
48
  embeddings.backend === 'none' ||
49
- embeddings.backend === 'deterministic' ||
50
49
  !embeddings.model ||
51
50
  embeddings.dimensions <= 0) {
52
51
  gaps.push('scan embeddings');
@@ -84,7 +84,7 @@ export async function writeMetabaseConfig(projectDir) {
84
84
  ' adapters:',
85
85
  ' - metabase',
86
86
  ' embeddings:',
87
- ' backend: deterministic',
87
+ ' backend: none',
88
88
  '',
89
89
  ].join('\n'), 'utf-8');
90
90
  }
@@ -418,7 +418,7 @@ export async function runPublicMetabaseSyncModeCase(tempDir, input) {
418
418
  ' adapters:',
419
419
  ' - metabase',
420
420
  ' embeddings:',
421
- ' backend: deterministic',
421
+ ' backend: none',
422
422
  '',
423
423
  ].join('\n'), 'utf-8');
424
424
  const project = await loadKtxProject({ projectDir });
@@ -182,7 +182,7 @@ describe('runKtxIngest', () => {
182
182
  await expect(runKtxSetup({
183
183
  command: 'run',
184
184
  projectDir,
185
- mode: 'new',
185
+ mode: 'auto',
186
186
  agents: false,
187
187
  agentScope: 'project',
188
188
  skipAgents: true,
@@ -227,7 +227,7 @@ describe('runKtxIngest', () => {
227
227
  expect(runIo.stderr()).toContain('ktx ingest requires llm.provider.backend: anthropic, vertex, gateway, or claude-code, or an injected agentRunner.');
228
228
  expect(runIo.stderr()).toContain('Configure a local Claude Code session or API-backed LLM, then rerun ingest:');
229
229
  expect(runIo.stderr()).toContain(`ktx setup --project-dir ${projectDir} --llm-backend claude-code --no-input`);
230
- expect(runIo.stderr()).toContain(`ktx setup --project-dir ${projectDir} --llm-backend anthropic --anthropic-api-key-env ANTHROPIC_API_KEY --anthropic-model claude-sonnet-4-6 --no-input`);
230
+ expect(runIo.stderr()).toContain(`ktx setup --project-dir ${projectDir} --llm-backend anthropic --anthropic-api-key-env ANTHROPIC_API_KEY --llm-model claude-sonnet-4-6 --no-input`);
231
231
  });
232
232
  it('routes metabase scheduled pulls to the fan-out runner and prints child summaries', async () => {
233
233
  const projectDir = join(tempDir, 'project');
@@ -620,7 +620,7 @@ describe('runKtxIngest', () => {
620
620
  ' adapters:',
621
621
  ' - metabase',
622
622
  ' embeddings:',
623
- ' backend: deterministic',
623
+ ' backend: none',
624
624
  '',
625
625
  ].join('\n'), 'utf-8');
626
626
  const project = await loadKtxProject({ projectDir });
@@ -1449,7 +1449,7 @@ describe('runKtxIngest', () => {
1449
1449
  ' adapters:',
1450
1450
  ' - looker',
1451
1451
  ' embeddings:',
1452
- ' backend: deterministic',
1452
+ ' backend: none',
1453
1453
  '',
1454
1454
  ].join('\n'), 'utf-8');
1455
1455
  const project = await loadKtxProject({ projectDir });
@@ -60,13 +60,13 @@ describe('printList — plain mode', () => {
60
60
  mode: 'plain',
61
61
  command: 'sl search',
62
62
  emptyMessage: 'No sources matched "foo"',
63
- emptyHint: 'Run `ktx sl list` to see available sources.',
63
+ emptyHint: 'Run `ktx sl` to see available sources.',
64
64
  unit: 'source',
65
65
  io: r.io,
66
66
  });
67
67
  expect(r.out()).toBe('');
68
68
  expect(r.err()).toBe('No sources matched "foo"\n' +
69
- 'Run `ktx sl list` to see available sources.\n');
69
+ 'Run `ktx sl` to see available sources.\n');
70
70
  });
71
71
  });
72
72
  describe('printList — json mode', () => {
@@ -162,13 +162,13 @@ describe('printList — pretty mode', () => {
162
162
  mode: 'pretty',
163
163
  command: 'sl search',
164
164
  emptyMessage: 'No sources matched "foo"',
165
- emptyHint: 'Run `ktx sl list` to see available sources.',
165
+ emptyHint: 'Run `ktx sl` to see available sources.',
166
166
  unit: 'source',
167
167
  io: r.io,
168
168
  });
169
169
  const out = stripAnsi(r.out());
170
170
  expect(out).toContain('No sources matched "foo"');
171
- expect(out).toContain('Run `ktx sl list` to see available sources.');
171
+ expect(out).toContain('Run `ktx sl` to see available sources.');
172
172
  });
173
173
  it('singularizes the footer when there is one row', () => {
174
174
  const r = recorder();
package/dist/knowledge.js CHANGED
@@ -74,7 +74,7 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
74
74
  }
75
75
  const mode = resolveOutputMode({ explicit: args.output, json: args.json, io });
76
76
  let emptyMessage = `No local wiki pages matched "${args.query}"`;
77
- let emptyHint = 'Run `ktx wiki list` to inspect available pages.';
77
+ let emptyHint = 'Run `ktx wiki` to inspect available pages.';
78
78
  if (results.length === 0 && mode !== 'json') {
79
79
  const pages = await listLocalKnowledgePages(project, { userId: args.userId });
80
80
  if (pages.length === 0) {
@@ -6,6 +6,8 @@ import { type KtxManagedPythonInstallPolicy, type ManagedPythonCommandRuntime }
6
6
  import { type ManagedPythonDaemonStartResult } from './managed-python-daemon.js';
7
7
  export interface ManagedLocalEmbeddingsDaemon {
8
8
  baseUrl: string;
9
+ stdoutLog: string;
10
+ stderrLog: string;
9
11
  env: Record<typeof MANAGED_SENTENCE_TRANSFORMERS_BASE_URL_ENV, string>;
10
12
  }
11
13
  export interface ManagedLocalEmbeddingsOptions {
@@ -42,6 +42,8 @@ export async function ensureManagedLocalEmbeddingsDaemon(options) {
42
42
  options.io.stderr.write(`${verb} KTX local embeddings daemon: ${daemon.baseUrl}\n`);
43
43
  return {
44
44
  baseUrl: daemon.baseUrl,
45
+ stdoutLog: daemon.state.stdoutLog,
46
+ stderrLog: daemon.state.stderrLog,
45
47
  env: {
46
48
  [MANAGED_SENTENCE_TRANSFORMERS_BASE_URL_ENV]: daemon.baseUrl,
47
49
  },
@@ -128,6 +128,8 @@ describe('ensureManagedLocalEmbeddingsDaemon', () => {
128
128
  startDaemon,
129
129
  })).resolves.toEqual({
130
130
  baseUrl: 'http://127.0.0.1:61234',
131
+ stdoutLog: '/work/proj/.ktx/runtime/daemon.stdout.log',
132
+ stderrLog: '/work/proj/.ktx/runtime/daemon.stderr.log',
131
133
  env: {
132
134
  [MANAGED_SENTENCE_TRANSFORMERS_BASE_URL_ENV]: 'http://127.0.0.1:61234',
133
135
  },
@@ -4,6 +4,7 @@ import { createServer } from 'node:net';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { setTimeout as delay } from 'node:timers/promises';
6
6
  import { z } from 'zod';
7
+ import { sanitizeChildProxyEnv } from './proxy-env.js';
7
8
  const stateSchema = z.object({
8
9
  schemaVersion: z.literal(1),
9
10
  pid: z.number().int().positive(),
@@ -118,11 +119,11 @@ export async function startKtxMcpDaemon(options) {
118
119
  const child = (options.spawnDaemon ?? defaultSpawnDaemon)(process.execPath, args, {
119
120
  detached: true,
120
121
  stdio: ['ignore', log.fd, log.fd],
121
- env: {
122
+ env: sanitizeChildProxyEnv({
122
123
  ...process.env,
123
124
  KTX_CLI_VERSION: options.cliVersion,
124
125
  ...(options.token ? { KTX_MCP_TOKEN: options.token } : {}),
125
- },
126
+ }),
126
127
  });
127
128
  if (!child.pid) {
128
129
  throw new Error('Failed to start KTX MCP daemon: child process pid was not available.');
@@ -28,6 +28,7 @@ describe('managed MCP daemon lifecycle', () => {
28
28
  await mkdir(projectDir, { recursive: true });
29
29
  });
30
30
  afterEach(async () => {
31
+ vi.unstubAllEnvs();
31
32
  await rm(tempDir, { recursive: true, force: true });
32
33
  });
33
34
  it('uses the spec state and log paths', () => {
@@ -72,6 +73,30 @@ describe('managed MCP daemon lifecycle', () => {
72
73
  }));
73
74
  expect(JSON.stringify(JSON.parse(await readFile(join(projectDir, '.ktx/mcp.json'), 'utf8')))).not.toContain('secret-token');
74
75
  });
76
+ it('sanitizes IPv6 CIDR entries from child NO_PROXY env', async () => {
77
+ vi.stubEnv('NO_PROXY', 'localhost,fd07:b51a:cc66:f0::/64');
78
+ vi.stubEnv('no_proxy', '::1,fd00::/8,*.orb.local');
79
+ const spawnDaemon = vi.fn(() => child(5555));
80
+ await startKtxMcpDaemon({
81
+ projectDir,
82
+ cliVersion: '0.0.0-test',
83
+ host: '127.0.0.1',
84
+ port: 7879,
85
+ allowedHosts: [],
86
+ allowedOrigins: [],
87
+ binPath: '/repo/packages/cli/dist/bin.js',
88
+ spawnDaemon,
89
+ processAlive: vi.fn(() => false),
90
+ portAvailable: vi.fn(async () => true),
91
+ now: () => new Date('2026-05-14T00:00:00.000Z'),
92
+ });
93
+ const env = spawnDaemon.mock.calls[0]?.[2].env;
94
+ if (!env) {
95
+ throw new Error('Expected MCP daemon spawn env');
96
+ }
97
+ expect(env.NO_PROXY).toBe('localhost,::1,*.orb.local');
98
+ expect(env.no_proxy).toBe(env.NO_PROXY);
99
+ });
75
100
  it('returns already-running without spawning when the daemon is alive at the same host/port', async () => {
76
101
  await mkdir(join(projectDir, '.ktx'), { recursive: true });
77
102
  await writeFile(join(projectDir, '.ktx/mcp.json'), `${JSON.stringify(state(projectDir), null, 2)}\n`);
@@ -12,8 +12,8 @@ export function runtimeInstallPolicyFromFlags(options) {
12
12
  }
13
13
  export function managedRuntimeInstallCommand(feature) {
14
14
  return feature === 'local-embeddings'
15
- ? 'ktx dev runtime install --feature local-embeddings --yes'
16
- : 'ktx dev runtime install --yes';
15
+ ? 'ktx admin runtime install --feature local-embeddings --yes'
16
+ : 'ktx admin runtime install --yes';
17
17
  }
18
18
  function installPrompt(feature) {
19
19
  const label = feature === 'local-embeddings' ? 'local embeddings Python runtime' : 'core Python runtime';
@@ -81,6 +81,7 @@ function installResult(features = ['core']) {
81
81
  asset: {
82
82
  manifest: installedManifest.asset,
83
83
  wheelPath: '/assets/python/kaelio_ktx-0.2.0-py3-none-any.whl',
84
+ requiresPython: { specifier: '>=3.13', minimumVersion: '3.13' },
84
85
  },
85
86
  manifest: installedManifest,
86
87
  };
@@ -97,8 +98,8 @@ function makeSpinnerEvents() {
97
98
  }
98
99
  describe('managedRuntimeInstallCommand', () => {
99
100
  it('prints the exact command for each managed runtime feature', () => {
100
- expect(managedRuntimeInstallCommand('core')).toBe('ktx dev runtime install --yes');
101
- expect(managedRuntimeInstallCommand('local-embeddings')).toBe('ktx dev runtime install --feature local-embeddings --yes');
101
+ expect(managedRuntimeInstallCommand('core')).toBe('ktx admin runtime install --yes');
102
+ expect(managedRuntimeInstallCommand('local-embeddings')).toBe('ktx admin runtime install --feature local-embeddings --yes');
102
103
  });
103
104
  });
104
105
  describe('runtimeInstallPolicyFromFlags', () => {
@@ -176,7 +177,7 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
176
177
  io: io.io,
177
178
  readStatus: vi.fn(async () => missingStatus()),
178
179
  installRuntime,
179
- })).rejects.toThrow('KTX Python runtime is required for this command. Run: ktx dev runtime install --yes');
180
+ })).rejects.toThrow('KTX Python runtime is required for this command. Run: ktx admin runtime install --yes');
180
181
  expect(installRuntime).not.toHaveBeenCalled();
181
182
  });
182
183
  it('installs the core runtime without prompting when policy is auto', async () => {