@kaelio/ktx 0.1.0 → 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 (63) hide show
  1. package/assets/python/{kaelio_ktx-0.1.0-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/setup-commands.js +14 -26
  4. package/dist/doctor.test.js +3 -4
  5. package/dist/index.test.js +26 -10
  6. package/dist/ingest-depth.js +0 -1
  7. package/dist/ingest.test-utils.js +2 -2
  8. package/dist/ingest.test.js +4 -4
  9. package/dist/managed-local-embeddings.d.ts +2 -0
  10. package/dist/managed-local-embeddings.js +2 -0
  11. package/dist/managed-local-embeddings.test.js +2 -0
  12. package/dist/managed-mcp-daemon.js +3 -2
  13. package/dist/managed-mcp-daemon.test.js +25 -0
  14. package/dist/managed-python-command.test.js +1 -0
  15. package/dist/managed-python-daemon.js +3 -2
  16. package/dist/managed-python-daemon.test.js +20 -0
  17. package/dist/managed-python-runtime.d.ts +4 -0
  18. package/dist/managed-python-runtime.js +47 -3
  19. package/dist/managed-python-runtime.test.js +51 -21
  20. package/dist/proxy-env.d.ts +1 -0
  21. package/dist/proxy-env.js +23 -0
  22. package/dist/proxy-env.test.d.ts +1 -0
  23. package/dist/proxy-env.test.js +17 -0
  24. package/dist/runtime.test.js +1 -0
  25. package/dist/setup-agents.js +3 -1
  26. package/dist/setup-agents.test.js +34 -0
  27. package/dist/setup-embeddings.d.ts +1 -0
  28. package/dist/setup-embeddings.js +28 -6
  29. package/dist/setup-embeddings.test.js +46 -4
  30. package/dist/setup-models.d.ts +0 -1
  31. package/dist/setup-models.js +2 -3
  32. package/dist/setup-models.test.js +8 -10
  33. package/dist/setup-project.d.ts +9 -1
  34. package/dist/setup-project.js +52 -25
  35. package/dist/setup-project.test.js +8 -8
  36. package/dist/setup-runtime.test.js +2 -0
  37. package/dist/setup.d.ts +1 -2
  38. package/dist/setup.js +21 -5
  39. package/dist/setup.test.js +160 -43
  40. package/dist/sl.test.js +2 -1
  41. package/dist/standalone-smoke.test.js +2 -3
  42. package/dist/status-project.js +1 -10
  43. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/local-ingest-acceptance.test.js +1 -1
  44. package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +8 -8
  45. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +1 -1
  46. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +3 -3
  47. package/node_modules/@ktx/context/dist/ingest/local-embedding-provider.integration.test.js +9 -10
  48. package/node_modules/@ktx/context/dist/llm/local-config.js +2 -15
  49. package/node_modules/@ktx/context/dist/llm/local-config.test.js +3 -7
  50. package/node_modules/@ktx/context/dist/project/config.d.ts +0 -5
  51. package/node_modules/@ktx/context/dist/project/config.js +5 -5
  52. package/node_modules/@ktx/context/dist/project/config.test.js +4 -7
  53. package/node_modules/@ktx/context/dist/scan/enrichment-state.test.js +4 -4
  54. package/node_modules/@ktx/context/dist/scan/index.d.ts +1 -1
  55. package/node_modules/@ktx/context/dist/scan/local-enrichment.d.ts +2 -6
  56. package/node_modules/@ktx/context/dist/scan/local-enrichment.js +31 -47
  57. package/node_modules/@ktx/context/dist/scan/local-enrichment.test.js +35 -18
  58. package/node_modules/@ktx/context/dist/scan/local-scan.test.js +2 -3
  59. package/node_modules/@ktx/llm/dist/embedding-provider.d.ts +0 -7
  60. package/node_modules/@ktx/llm/dist/embedding-provider.js +12 -138
  61. package/node_modules/@ktx/llm/dist/embedding-provider.test.js +10 -25
  62. package/node_modules/@ktx/llm/dist/types.d.ts +1 -1
  63. package/package.json +1 -1
@@ -37,6 +37,15 @@ async function existingFolderState(projectDir) {
37
37
  throw error;
38
38
  }
39
39
  }
40
+ function cleanupForFolderState(projectDir, state) {
41
+ if (state === 'missing') {
42
+ return { kind: 'remove-project-dir', projectDir };
43
+ }
44
+ if (state === 'empty-directory') {
45
+ return { kind: 'remove-ktx-scaffold', projectDir };
46
+ }
47
+ return undefined;
48
+ }
40
49
  async function confirmProjectDir(selectedDir, io, prompts) {
41
50
  const state = await existingFolderState(selectedDir);
42
51
  if (state === 'not-directory') {
@@ -75,7 +84,7 @@ async function confirmProjectDir(selectedDir, io, prompts) {
75
84
  return { status: 'back' };
76
85
  if (action !== 'create')
77
86
  return { status: 'cancelled' };
78
- return { status: 'confirmed', confirmedCreation: true };
87
+ return { status: 'confirmed', confirmedCreation: true, createdProjectCleanup: cleanupForFolderState(selectedDir, state) };
79
88
  }
80
89
  async function normalizeSetupGitignore(projectDir) {
81
90
  const gitignorePath = join(projectDir, '.ktx/.gitignore');
@@ -147,34 +156,34 @@ async function promptForNewProjectDir(projectDir, homeDir, io, prompts) {
147
156
  return { status: 'back', projectDir };
148
157
  if (confirmed.status === 'cancelled')
149
158
  return { status: 'cancelled', projectDir };
150
- return { status: 'selected', projectDir: selectedDir, confirmedCreation: confirmed.confirmedCreation };
159
+ return {
160
+ status: 'selected',
161
+ projectDir: selectedDir,
162
+ confirmedCreation: confirmed.confirmedCreation,
163
+ ...(confirmed.createdProjectCleanup ? { createdProjectCleanup: confirmed.createdProjectCleanup } : {}),
164
+ };
151
165
  }
152
166
  }
167
+ async function createProjectWithCleanup(projectDir, deps) {
168
+ const state = await existingFolderState(projectDir);
169
+ const project = await createProject(projectDir, deps);
170
+ const createdProjectCleanup = cleanupForFolderState(projectDir, state);
171
+ return {
172
+ project,
173
+ ...(createdProjectCleanup ? { createdProjectCleanup } : {}),
174
+ };
175
+ }
153
176
  export async function runKtxSetupProjectStep(args, io, deps = {}) {
154
177
  const projectDir = resolve(args.projectDir);
155
178
  const homeDir = deps.homeDir ?? homedir();
156
179
  const exists = hasProjectConfig(projectDir);
157
- if (args.mode === 'existing') {
158
- if (!exists) {
159
- io.stderr.write(`No existing KTX project found at ${projectDir}. Pass --new to create it.\n`);
160
- return { status: 'missing-input', projectDir };
161
- }
162
- const project = await loadExistingProject(projectDir, deps);
163
- printProjectSummary(io, projectDir);
164
- return { status: 'ready', projectDir, project };
165
- }
166
- if (args.mode === 'new') {
167
- const project = await createProject(projectDir, deps);
168
- printProjectSummary(io, projectDir);
169
- return { status: 'ready', projectDir, project };
170
- }
171
180
  if (args.mode === 'prompt-new') {
172
181
  if (args.inputMode === 'disabled') {
173
- io.stderr.write('Missing new project folder: pass --new --project-dir to create a project without prompts.\n');
182
+ io.stderr.write('Missing new project folder: pass --project-dir and --yes to create a project without prompts.\n');
174
183
  return { status: 'missing-input', projectDir };
175
184
  }
176
185
  if (!io.stdout.isTTY && !deps.prompts) {
177
- io.stderr.write('Missing new project folder: pass --new --project-dir to create a project outside an interactive terminal.\n');
186
+ io.stderr.write('Missing new project folder: pass --project-dir and --yes to create a project outside an interactive terminal.\n');
178
187
  return { status: 'missing-input', projectDir };
179
188
  }
180
189
  const prompts = deps.prompts ?? createClackSetupProjectPromptAdapter();
@@ -192,6 +201,7 @@ export async function runKtxSetupProjectStep(args, io, deps = {}) {
192
201
  projectDir: selected.projectDir,
193
202
  project,
194
203
  confirmedCreation: selected.confirmedCreation,
204
+ ...(selected.createdProjectCleanup ? { createdProjectCleanup: selected.createdProjectCleanup } : {}),
195
205
  };
196
206
  }
197
207
  if (exists) {
@@ -201,15 +211,20 @@ export async function runKtxSetupProjectStep(args, io, deps = {}) {
201
211
  }
202
212
  if (args.inputMode === 'disabled') {
203
213
  if (!args.yes) {
204
- io.stderr.write('Missing setup choice: pass --new or --yes to create a project in non-interactive setup.\n');
214
+ io.stderr.write('Missing setup choice: pass --yes to create a project in non-interactive setup.\n');
205
215
  return { status: 'missing-input', projectDir };
206
216
  }
207
- const project = await createProject(projectDir, deps);
217
+ const { project, createdProjectCleanup } = await createProjectWithCleanup(projectDir, deps);
208
218
  printProjectSummary(io, projectDir);
209
- return { status: 'ready', projectDir, project };
219
+ return {
220
+ status: 'ready',
221
+ projectDir,
222
+ project,
223
+ ...(createdProjectCleanup ? { createdProjectCleanup } : {}),
224
+ };
210
225
  }
211
226
  if (!io.stdout.isTTY && !deps.prompts) {
212
- io.stderr.write('Missing setup choice: pass --new or --yes to create a project outside an interactive terminal.\n');
227
+ io.stderr.write('Missing setup choice: pass --yes to create a project outside an interactive terminal.\n');
213
228
  return { status: 'missing-input', projectDir };
214
229
  }
215
230
  const prompts = deps.prompts ?? createClackSetupProjectPromptAdapter();
@@ -238,9 +253,14 @@ export async function runKtxSetupProjectStep(args, io, deps = {}) {
238
253
  return { status: 'cancelled', projectDir };
239
254
  }
240
255
  if (choice === 'current') {
241
- const project = await createProject(projectDir, deps);
256
+ const { project, createdProjectCleanup } = await createProjectWithCleanup(projectDir, deps);
242
257
  printProjectSummary(io, projectDir);
243
- return { status: 'ready', projectDir, project };
258
+ return {
259
+ status: 'ready',
260
+ projectDir,
261
+ project,
262
+ ...(createdProjectCleanup ? { createdProjectCleanup } : {}),
263
+ };
244
264
  }
245
265
  if (choice === 'new-default') {
246
266
  const confirmed = await confirmProjectDir(defaultProjectDir, io, prompts);
@@ -257,6 +277,7 @@ export async function runKtxSetupProjectStep(args, io, deps = {}) {
257
277
  projectDir: defaultProjectDir,
258
278
  project,
259
279
  confirmedCreation: confirmed.confirmedCreation,
280
+ ...(confirmed.createdProjectCleanup ? { createdProjectCleanup: confirmed.createdProjectCleanup } : {}),
260
281
  };
261
282
  }
262
283
  if (choice === 'new-custom') {
@@ -281,7 +302,13 @@ export async function runKtxSetupProjectStep(args, io, deps = {}) {
281
302
  return { status: 'cancelled', projectDir };
282
303
  const project = await createProject(customDir, deps);
283
304
  printProjectSummary(io, customDir);
284
- return { status: 'ready', projectDir: customDir, project, confirmedCreation: confirmed.confirmedCreation };
305
+ return {
306
+ status: 'ready',
307
+ projectDir: customDir,
308
+ project,
309
+ confirmedCreation: confirmed.confirmedCreation,
310
+ ...(confirmed.createdProjectCleanup ? { createdProjectCleanup: confirmed.createdProjectCleanup } : {}),
311
+ };
285
312
  }
286
313
  prompts.cancel('Setup cancelled.');
287
314
  return { status: 'cancelled', projectDir };
@@ -48,10 +48,10 @@ describe('setup project step', () => {
48
48
  afterEach(async () => {
49
49
  await rm(tempDir, { recursive: true, force: true });
50
50
  });
51
- it('creates a new project with --new and marks the project step complete', async () => {
51
+ it('creates a new project in non-interactive auto mode with --yes and marks the project step complete', async () => {
52
52
  const projectDir = join(tempDir, 'warehouse');
53
53
  const testIo = makeIo();
54
- const result = await runKtxSetupProjectStep({ projectDir, mode: 'new', inputMode: 'disabled', yes: false }, testIo.io);
54
+ const result = await runKtxSetupProjectStep({ projectDir, mode: 'auto', inputMode: 'disabled', yes: true }, testIo.io);
55
55
  expect(result.status).toBe('ready');
56
56
  expect(result.projectDir).toBe(projectDir);
57
57
  expect(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
@@ -61,7 +61,7 @@ describe('setup project step', () => {
61
61
  expect(testIo.stdout()).toContain(`Project: ${projectDir}`);
62
62
  expect(testIo.stderr()).toBe('');
63
63
  });
64
- it('loads an existing project with --existing and drops config setup progress', async () => {
64
+ it('loads an existing project in auto mode and drops config setup progress', async () => {
65
65
  const projectDir = join(tempDir, 'warehouse');
66
66
  await initKtxProject({ projectDir });
67
67
  await writeFile(join(projectDir, 'ktx.yaml'), [
@@ -72,7 +72,7 @@ describe('setup project step', () => {
72
72
  ' - llm',
73
73
  'connections: {}',
74
74
  ].join('\n'), 'utf-8');
75
- const result = await runKtxSetupProjectStep({ projectDir, mode: 'existing', inputMode: 'disabled', yes: false }, makeIo().io);
75
+ const result = await runKtxSetupProjectStep({ projectDir, mode: 'auto', inputMode: 'disabled', yes: false }, makeIo().io);
76
76
  expect(result.status).toBe('ready');
77
77
  const config = parseKtxProjectConfig(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'));
78
78
  expect(config.setup).toEqual({
@@ -86,16 +86,16 @@ describe('setup project step', () => {
86
86
  const rejectedIo = makeIo();
87
87
  const acceptedIo = makeIo();
88
88
  await expect(runKtxSetupProjectStep({ projectDir, mode: 'auto', inputMode: 'disabled', yes: false }, rejectedIo.io)).resolves.toMatchObject({ status: 'missing-input' });
89
- expect(rejectedIo.stderr()).toContain('Missing setup choice: pass --new or --yes');
89
+ expect(rejectedIo.stderr()).toContain('Missing setup choice: pass --yes');
90
90
  await expect(stat(join(projectDir, 'ktx.yaml'))).rejects.toThrow();
91
91
  await expect(runKtxSetupProjectStep({ projectDir, mode: 'auto', inputMode: 'disabled', yes: true }, acceptedIo.io)).resolves.toMatchObject({ status: 'ready', projectDir });
92
92
  await expect(stat(join(projectDir, 'ktx.yaml'))).resolves.toBeDefined();
93
93
  });
94
- it('fails --existing clearly when ktx.yaml is missing', async () => {
94
+ it('fails clearly in no-input auto mode when ktx.yaml is missing and --yes is absent', async () => {
95
95
  const projectDir = join(tempDir, 'warehouse');
96
96
  const testIo = makeIo();
97
- await expect(runKtxSetupProjectStep({ projectDir, mode: 'existing', inputMode: 'disabled', yes: false }, testIo.io)).resolves.toMatchObject({ status: 'missing-input' });
98
- expect(testIo.stderr()).toContain(`No existing KTX project found at ${projectDir}`);
97
+ await expect(runKtxSetupProjectStep({ projectDir, mode: 'auto', inputMode: 'disabled', yes: false }, testIo.io)).resolves.toMatchObject({ status: 'missing-input' });
98
+ expect(testIo.stderr()).toContain('Missing setup choice: pass --yes');
99
99
  });
100
100
  it('prompts to use the current directory and creates a project in interactive auto mode', async () => {
101
101
  const projectDir = join(tempDir, 'warehouse');
@@ -77,6 +77,8 @@ describe('runKtxSetupRuntimeStep', () => {
77
77
  const io = makeIo();
78
78
  const ensureLocalEmbeddings = vi.fn(async () => ({
79
79
  baseUrl: 'http://127.0.0.1:61234',
80
+ stdoutLog: join(tempDir, '.ktx', 'runtime', 'daemon.stdout.log'),
81
+ stderrLog: join(tempDir, '.ktx', 'runtime', 'daemon.stderr.log'),
80
82
  env: { KTX_MANAGED_SENTENCE_TRANSFORMERS_BASE_URL: 'http://127.0.0.1:61234' },
81
83
  }));
82
84
  const config = {
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') {