@kaelio/ktx 0.11.0 → 0.13.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 (212) hide show
  1. package/assets/python/kaelio_ktx-0.13.0-py3-none-any.whl +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/admin.js +1 -1
  5. package/dist/clack.d.ts +16 -0
  6. package/dist/clack.js +37 -6
  7. package/dist/claude-code-prompt-caching.js +1 -1
  8. package/dist/cli-program.js +3 -3
  9. package/dist/cli-runtime.js +2 -2
  10. package/dist/commands/connection-commands.js +1 -1
  11. package/dist/commands/ingest-commands.js +4 -4
  12. package/dist/commands/mcp-commands.js +12 -12
  13. package/dist/commands/runtime-commands.js +4 -4
  14. package/dist/commands/setup-commands.js +19 -5
  15. package/dist/commands/sl-commands.js +1 -1
  16. package/dist/commands/sql-commands.js +1 -1
  17. package/dist/commands/status-commands.js +1 -1
  18. package/dist/connection.js +15 -3
  19. package/dist/connectors/bigquery/connector.js +1 -14
  20. package/dist/connectors/clickhouse/connector.js +2 -16
  21. package/dist/connectors/duckdb/federated-attach.d.ts +7 -0
  22. package/dist/connectors/duckdb/federated-attach.js +86 -0
  23. package/dist/connectors/duckdb/federated-executor.d.ts +5 -0
  24. package/dist/connectors/duckdb/federated-executor.js +59 -0
  25. package/dist/connectors/mysql/connector.js +2 -16
  26. package/dist/connectors/postgres/connector.js +1 -14
  27. package/dist/connectors/shared/string-reference.d.ts +6 -0
  28. package/dist/connectors/shared/string-reference.js +19 -0
  29. package/dist/connectors/snowflake/connector.d.ts +1 -1
  30. package/dist/connectors/snowflake/connector.js +1 -14
  31. package/dist/connectors/sqlite/connector.js +2 -25
  32. package/dist/connectors/sqlserver/connector.js +4 -17
  33. package/dist/context/connections/connection-type.d.ts +1 -1
  34. package/dist/context/connections/federation.d.ts +33 -0
  35. package/dist/context/connections/federation.js +51 -0
  36. package/dist/context/connections/local-warehouse-descriptor.d.ts +2 -0
  37. package/dist/context/connections/project-sql-executor.d.ts +18 -0
  38. package/dist/context/connections/project-sql-executor.js +39 -0
  39. package/dist/context/connections/query-executor.d.ts +2 -2
  40. package/dist/context/connections/read-only-sql.d.ts +1 -0
  41. package/dist/context/connections/read-only-sql.js +119 -4
  42. package/dist/context/connections/resolve-connection.d.ts +12 -0
  43. package/dist/context/connections/resolve-connection.js +37 -0
  44. package/dist/context/core/git-env.d.ts +4 -0
  45. package/dist/context/core/git-env.js +5 -1
  46. package/dist/context/core/git.service.d.ts +23 -0
  47. package/dist/context/core/git.service.js +71 -8
  48. package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
  49. package/dist/context/ingest/adapters/live-database/manifest.d.ts +3 -0
  50. package/dist/context/ingest/adapters/live-database/manifest.js +19 -11
  51. package/dist/context/ingest/adapters/looker/client.js +7 -2
  52. package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
  53. package/dist/context/ingest/adapters/looker/factory.js +9 -0
  54. package/dist/context/ingest/adapters/looker/mapping.js +1 -1
  55. package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
  56. package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
  57. package/dist/context/ingest/adapters/metabase/client.js +1 -1
  58. package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
  59. package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
  60. package/dist/context/ingest/artifact-gates.d.ts +2 -6
  61. package/dist/context/ingest/artifact-gates.js +5 -47
  62. package/dist/context/ingest/constrained-repair.d.ts +55 -0
  63. package/dist/context/ingest/constrained-repair.js +167 -0
  64. package/dist/context/ingest/final-gate-repair.d.ts +9 -11
  65. package/dist/context/ingest/final-gate-repair.js +40 -128
  66. package/dist/context/ingest/finalization-scope.d.ts +1 -1
  67. package/dist/context/ingest/finalization-scope.js +15 -15
  68. package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
  69. package/dist/context/ingest/ingest-bundle.runner.js +101 -67
  70. package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
  71. package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
  72. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
  73. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
  74. package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
  75. package/dist/context/ingest/local-bundle-runtime.js +9 -10
  76. package/dist/context/ingest/local-ingest.d.ts +2 -0
  77. package/dist/context/ingest/local-ingest.js +2 -0
  78. package/dist/context/ingest/memory-flow/view-model.js +1 -1
  79. package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
  80. package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
  81. package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
  82. package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
  83. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
  84. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
  85. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
  86. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
  87. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
  88. package/dist/context/llm/ai-sdk-runtime.js +2 -2
  89. package/dist/context/llm/claude-code-runtime.js +19 -3
  90. package/dist/context/llm/local-config.js +1 -1
  91. package/dist/context/llm/runtime-tools.js +2 -2
  92. package/dist/context/mcp/context-tools.js +33 -8
  93. package/dist/context/mcp/local-project-ports.js +63 -89
  94. package/dist/context/mcp/types.d.ts +2 -0
  95. package/dist/context/memory/local-memory.js +4 -1
  96. package/dist/context/memory/memory-agent.service.js +1 -1
  97. package/dist/context/project/config.d.ts +11 -4
  98. package/dist/context/project/config.js +85 -30
  99. package/dist/context/project/driver-schemas.js +1 -1
  100. package/dist/context/project/mappings-yaml-schema.js +2 -2
  101. package/dist/context/project/project.js +12 -4
  102. package/dist/context/scan/description-generation.js +4 -4
  103. package/dist/context/scan/local-enrichment-artifacts.js +33 -4
  104. package/dist/context/scan/local-scan.js +2 -2
  105. package/dist/context/scan/local-structural-artifacts.js +5 -5
  106. package/dist/context/scan/relationship-benchmark-report.js +1 -1
  107. package/dist/context/scan/relationship-discovery.js +3 -3
  108. package/dist/context/scan/relationship-llm-proposal.js +3 -3
  109. package/dist/context/sl/local-query.js +31 -44
  110. package/dist/context/sl/local-sl.d.ts +0 -8
  111. package/dist/context/sl/local-sl.js +71 -70
  112. package/dist/context/sl/semantic-layer.service.d.ts +25 -8
  113. package/dist/context/sl/semantic-layer.service.js +109 -56
  114. package/dist/context/sl/source-files.d.ts +48 -0
  115. package/dist/context/sl/source-files.js +138 -0
  116. package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
  117. package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
  118. package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
  119. package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
  120. package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
  121. package/dist/context/sql-analysis/dialect.d.ts +2 -0
  122. package/dist/context/sql-analysis/dialect.js +20 -0
  123. package/dist/context/tools/base-tool.d.ts +6 -19
  124. package/dist/context/tools/base-tool.js +0 -14
  125. package/dist/context-build-view.js +5 -5
  126. package/dist/database-tree-picker.js +18 -3
  127. package/dist/demo-assets.js +0 -1
  128. package/dist/doctor.d.ts +1 -1
  129. package/dist/doctor.js +31 -23
  130. package/dist/errors.d.ts +31 -0
  131. package/dist/errors.js +44 -0
  132. package/dist/ingest-query-executor.d.ts +2 -0
  133. package/dist/ingest-query-executor.js +8 -22
  134. package/dist/ingest.d.ts +1 -1
  135. package/dist/ingest.js +8 -2
  136. package/dist/io/symbols.d.ts +2 -0
  137. package/dist/io/symbols.js +2 -0
  138. package/dist/io/tty.d.ts +8 -0
  139. package/dist/io/tty.js +16 -0
  140. package/dist/llm/embedding-health.js +1 -1
  141. package/dist/llm/embedding-provider.js +3 -3
  142. package/dist/llm/model-provider.js +1 -1
  143. package/dist/local-adapters.d.ts +1 -0
  144. package/dist/local-adapters.js +2 -2
  145. package/dist/local-scan-connectors.js +1 -1
  146. package/dist/managed-local-embeddings.js +17 -8
  147. package/dist/managed-mcp-daemon.js +3 -3
  148. package/dist/managed-python-command.d.ts +7 -0
  149. package/dist/managed-python-command.js +34 -8
  150. package/dist/managed-python-daemon.js +2 -2
  151. package/dist/managed-python-http.js +3 -3
  152. package/dist/managed-python-runtime.d.ts +30 -1
  153. package/dist/managed-python-runtime.js +134 -18
  154. package/dist/managed-uv-release.d.ts +7 -0
  155. package/dist/managed-uv-release.js +11 -0
  156. package/dist/mcp-http-server.js +4 -4
  157. package/dist/mcp-server-factory.js +3 -3
  158. package/dist/mcp-stdio-server.js +1 -1
  159. package/dist/memory-flow-hud.js +2 -2
  160. package/dist/next-steps.js +2 -2
  161. package/dist/prompt-navigation.d.ts +17 -0
  162. package/dist/prompt-navigation.js +49 -3
  163. package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
  164. package/dist/prompts/memory_agent_external_ingest.md +2 -2
  165. package/dist/public-ingest-copy.js +1 -1
  166. package/dist/public-ingest.js +3 -3
  167. package/dist/release-version.js +1 -1
  168. package/dist/runtime-requirements.js +1 -1
  169. package/dist/runtime.js +9 -9
  170. package/dist/scan.js +1 -1
  171. package/dist/setup-agents.d.ts +21 -15
  172. package/dist/setup-agents.js +143 -66
  173. package/dist/setup-banner.d.ts +20 -0
  174. package/dist/setup-banner.js +39 -0
  175. package/dist/setup-context.js +24 -15
  176. package/dist/setup-databases.d.ts +3 -0
  177. package/dist/setup-databases.js +47 -59
  178. package/dist/setup-demo-tour.js +12 -8
  179. package/dist/setup-embeddings.js +9 -9
  180. package/dist/setup-interrupt.js +1 -1
  181. package/dist/setup-models.d.ts +4 -1
  182. package/dist/setup-models.js +54 -28
  183. package/dist/setup-project.js +29 -5
  184. package/dist/setup-prompts.js +16 -1
  185. package/dist/setup-ready-menu.js +1 -1
  186. package/dist/setup-sources.js +28 -12
  187. package/dist/setup.d.ts +1 -0
  188. package/dist/setup.js +14 -13
  189. package/dist/skills/analytics/SKILL.md +3 -3
  190. package/dist/skills/dbt_ingest/SKILL.md +3 -3
  191. package/dist/skills/looker_ingest/SKILL.md +3 -3
  192. package/dist/skills/lookml_ingest/SKILL.md +7 -7
  193. package/dist/skills/metabase_ingest/SKILL.md +4 -4
  194. package/dist/skills/metricflow_ingest/SKILL.md +15 -15
  195. package/dist/skills/notion_synthesize/SKILL.md +1 -1
  196. package/dist/skills/sl/SKILL.md +3 -3
  197. package/dist/skills/sl_capture/SKILL.md +1 -1
  198. package/dist/skills/wiki_capture/SKILL.md +1 -1
  199. package/dist/source-mapping.js +1 -1
  200. package/dist/sql.d.ts +2 -0
  201. package/dist/sql.js +35 -53
  202. package/dist/startup-profile.js +1 -1
  203. package/dist/status-project.d.ts +0 -2
  204. package/dist/status-project.js +4 -6
  205. package/dist/telemetry/events.d.ts +3 -2
  206. package/dist/telemetry/events.js +11 -1
  207. package/dist/telemetry/exception.js +14 -0
  208. package/dist/text-ingest.js +1 -1
  209. package/dist/tree-picker-tui.d.ts +0 -1
  210. package/dist/tree-picker-tui.js +2 -3
  211. package/package.json +2 -1
  212. package/assets/python/kaelio_ktx-0.11.0-py3-none-any.whl +0 -0
@@ -12,7 +12,7 @@ function normalizedBaseUrl(baseUrl) {
12
12
  function parseJsonObject(raw, path) {
13
13
  const parsed = JSON.parse(raw);
14
14
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
15
- throw new Error(`KTX daemon HTTP ${path} returned non-object JSON`);
15
+ throw new Error(`ktx daemon HTTP ${path} returned non-object JSON`);
16
16
  }
17
17
  return parsed;
18
18
  }
@@ -35,7 +35,7 @@ async function postManagedDaemonJson(baseUrl, path, payload) {
35
35
  const text = Buffer.concat(chunks).toString('utf8');
36
36
  const statusCode = response.statusCode ?? 0;
37
37
  if (statusCode < 200 || statusCode >= 300) {
38
- reject(new Error(`KTX daemon HTTP ${path} failed with ${statusCode}: ${text}`));
38
+ reject(new Error(`ktx daemon HTTP ${path} failed with ${statusCode}: ${text}`));
39
39
  return;
40
40
  }
41
41
  try {
@@ -72,7 +72,7 @@ export function createManagedPythonDaemonBaseUrlResolver(options) {
72
72
  force: false,
73
73
  });
74
74
  const verb = daemon.status === 'started' ? 'Started' : 'Using existing';
75
- writePrefixedLines((chunk) => options.io.stderr.write(chunk), `${verb} KTX daemon: ${daemon.baseUrl}`);
75
+ writePrefixedLines((chunk) => options.io.stderr.write(chunk), `${verb} ktx daemon: ${daemon.baseUrl}`);
76
76
  cachedBaseUrl = daemon.baseUrl;
77
77
  return cachedBaseUrl;
78
78
  };
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type ManagedUvArtifact, type ManagedUvPlatformKey } from './managed-uv-release.js';
2
3
  export declare const runtimeFeatureSchema: z.ZodEnum<{
3
4
  core: "core";
4
5
  "local-embeddings": "local-embeddings";
@@ -92,6 +93,7 @@ export interface ManagedPythonRuntimeInstallOptions extends ManagedPythonRuntime
92
93
  features: KtxRuntimeFeature[];
93
94
  force?: boolean;
94
95
  exec?: ManagedPythonRuntimeExec;
96
+ fetchUvArtifact?: ManagedUvFetchArtifact;
95
97
  }
96
98
  export interface ManagedPythonRuntimeInstallResult {
97
99
  status: 'ready' | 'installed';
@@ -113,8 +115,22 @@ export interface ManagedPythonRuntimeDoctorCheck {
113
115
  detail: string;
114
116
  fix?: string;
115
117
  }
118
+ export type ManagedUvFetchArtifact = (url: string) => Promise<Uint8Array>;
116
119
  /** @internal */
117
- export declare const MISSING_UV_RUNTIME_INSTALL_MESSAGE = "uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx admin runtime install --yes";
120
+ export interface ManagedUvRelease {
121
+ version: string;
122
+ artifacts: Partial<Record<ManagedUvPlatformKey, ManagedUvArtifact>>;
123
+ }
124
+ /** @internal */
125
+ export interface EnsureManagedUvOptions {
126
+ platform?: NodeJS.Platform;
127
+ arch?: string;
128
+ env?: NodeJS.ProcessEnv;
129
+ homeDir?: string;
130
+ runtimeRoot?: string;
131
+ fetchArtifact?: ManagedUvFetchArtifact;
132
+ release?: ManagedUvRelease;
133
+ }
118
134
  /** @internal */
119
135
  export declare function managedPythonRuntimeLayout(options: ManagedPythonRuntimeLayoutOptions): ManagedPythonRuntimeLayout;
120
136
  export declare function managedPythonDaemonLayout(options: ManagedPythonDaemonLayoutOptions): ManagedPythonDaemonLayout;
@@ -122,9 +138,22 @@ export declare function managedPythonDaemonLayout(options: ManagedPythonDaemonLa
122
138
  export declare function verifyRuntimeAsset(input: {
123
139
  assetDir: string;
124
140
  }): Promise<ManagedRuntimeAsset>;
141
+ /** @internal */
142
+ export declare function managedUvPath(options?: EnsureManagedUvOptions): string;
143
+ /**
144
+ * ktx provisions its own pinned uv under the runtime root; uv on PATH is never
145
+ * consulted, so runtime installs behave identically on every machine. All
146
+ * failures here are environment outcomes (offline host, intercepting proxy,
147
+ * unsupported platform) and stay out of Error Tracking via KtxExpectedError —
148
+ * except a pin/layout mismatch inside a checksum-verified archive, which is a
149
+ * ktx release fault and must reach Error Tracking.
150
+ * @internal
151
+ */
152
+ export declare function ensureManagedUv(options?: EnsureManagedUvOptions): Promise<string>;
125
153
  export declare function installManagedPythonRuntime(options: ManagedPythonRuntimeInstallOptions): Promise<ManagedPythonRuntimeInstallResult>;
126
154
  export declare function readManagedPythonRuntimeStatus(options: ManagedPythonRuntimeLayoutOptions): Promise<ManagedPythonRuntimeStatus>;
127
155
  export declare function doctorManagedPythonRuntime(options: ManagedPythonRuntimeLayoutOptions & {
128
156
  exec?: ManagedPythonRuntimeExec;
157
+ fetchUvArtifact?: ManagedUvFetchArtifact;
129
158
  }): Promise<ManagedPythonRuntimeDoctorCheck[]>;
130
159
  export {};
@@ -1,12 +1,14 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
- import { access, appendFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
+ import { access, appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
4
  import { homedir } from 'node:os';
5
- import { basename, join } from 'node:path';
5
+ import { basename, dirname, join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { promisify } from 'node:util';
8
- import { strFromU8, unzipSync } from 'fflate';
8
+ import { gunzipSync, strFromU8, unzipSync } from 'fflate';
9
9
  import { z } from 'zod';
10
+ import { KtxExpectedError } from './errors.js';
11
+ import { MANAGED_UV_ARTIFACTS, MANAGED_UV_VERSION, } from './managed-uv-release.js';
10
12
  const execFileAsync = promisify(execFile);
11
13
  export const runtimeFeatureSchema = z.enum(['core', 'local-embeddings']);
12
14
  const runtimeAssetManifestSchema = z.object({
@@ -32,8 +34,10 @@ const installedRuntimeManifestSchema = z.object({
32
34
  }),
33
35
  installLog: z.string().min(1),
34
36
  });
35
- /** @internal */
36
- export const MISSING_UV_RUNTIME_INSTALL_MESSAGE = 'uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx admin runtime install --yes';
37
+ const PINNED_UV_RELEASE = {
38
+ version: MANAGED_UV_VERSION,
39
+ artifacts: MANAGED_UV_ARTIFACTS,
40
+ };
37
41
  function defaultAssetDir() {
38
42
  return fileURLToPath(new URL('../assets/python/', import.meta.url));
39
43
  }
@@ -144,7 +148,7 @@ export async function verifyRuntimeAsset(input) {
144
148
  throw new Error([
145
149
  `Missing bundled Python runtime manifest: ${manifestPath}`,
146
150
  'In a source checkout, build the local runtime assets with: pnpm run artifacts:build',
147
- 'Then retry the runtime-backed KTX command.',
151
+ 'Then retry the runtime-backed ktx command.',
148
152
  ].join('\n'));
149
153
  }
150
154
  throw error;
@@ -221,13 +225,123 @@ async function runLogged(input) {
221
225
  function managedRuntimeUvEnv(baseEnv) {
222
226
  return { ...baseEnv, UV_NO_CONFIG: '1' };
223
227
  }
224
- async function ensureUv(exec, env) {
228
+ function managedUvBinaryName(platform) {
229
+ return platform === 'win32' ? 'uv.exe' : 'uv';
230
+ }
231
+ /** @internal */
232
+ export function managedUvPath(options = {}) {
233
+ const platform = options.platform ?? process.platform;
234
+ const env = options.env ?? process.env;
235
+ const homeDir = options.homeDir ?? homedir();
236
+ const runtimeRoot = options.runtimeRoot ?? runtimeRootFor({ env, homeDir });
237
+ const version = (options.release ?? PINNED_UV_RELEASE).version;
238
+ return join(runtimeRoot, 'uv', version, managedUvBinaryName(platform));
239
+ }
240
+ async function defaultFetchUvArtifact(url) {
241
+ const response = await fetch(url);
242
+ if (!response.ok) {
243
+ throw new Error(`HTTP ${response.status}`);
244
+ }
245
+ return new Uint8Array(await response.arrayBuffer());
246
+ }
247
+ function readTarField(block, start, length) {
248
+ const field = block.subarray(start, start + length);
249
+ const end = field.indexOf(0);
250
+ return strFromU8(end < 0 ? field : field.subarray(0, end));
251
+ }
252
+ function findTarEntry(archive, matches) {
253
+ let offset = 0;
254
+ while (offset + 512 <= archive.length) {
255
+ const block = archive.subarray(offset, offset + 512);
256
+ const name = readTarField(block, 0, 100);
257
+ if (!name) {
258
+ return undefined;
259
+ }
260
+ const size = Number.parseInt(readTarField(block, 124, 12).trim() || '0', 8);
261
+ if (matches(name)) {
262
+ return archive.subarray(offset + 512, offset + 512 + size);
263
+ }
264
+ offset += 512 + Math.ceil(size / 512) * 512;
265
+ }
266
+ return undefined;
267
+ }
268
+ function extractUvFromArchive(input) {
269
+ const entry = input.file.endsWith('.zip')
270
+ ? unzipSync(input.contents)[input.binaryName]
271
+ : findTarEntry(gunzipSync(input.contents), (name) => name === input.binaryName || name.endsWith(`/${input.binaryName}`));
272
+ if (!entry) {
273
+ throw new Error(`uv archive ${input.file} is missing the ${input.binaryName} binary`);
274
+ }
275
+ return entry;
276
+ }
277
+ /**
278
+ * ktx provisions its own pinned uv under the runtime root; uv on PATH is never
279
+ * consulted, so runtime installs behave identically on every machine. All
280
+ * failures here are environment outcomes (offline host, intercepting proxy,
281
+ * unsupported platform) and stay out of Error Tracking via KtxExpectedError —
282
+ * except a pin/layout mismatch inside a checksum-verified archive, which is a
283
+ * ktx release fault and must reach Error Tracking.
284
+ * @internal
285
+ */
286
+ export async function ensureManagedUv(options = {}) {
287
+ const platform = options.platform ?? process.platform;
288
+ const arch = options.arch ?? process.arch;
289
+ const release = options.release ?? PINNED_UV_RELEASE;
290
+ const binaryName = managedUvBinaryName(platform);
291
+ const uvPath = managedUvPath(options);
292
+ if (await pathExists(uvPath)) {
293
+ return uvPath;
294
+ }
295
+ const artifact = release.artifacts[`${platform}-${arch}`];
296
+ if (!artifact) {
297
+ throw new KtxExpectedError(`ktx does not bundle uv for ${platform}-${arch}. Place a uv ${release.version} binary at ${uvPath} and retry: ktx admin runtime install --yes`);
298
+ }
299
+ const url = `https://github.com/astral-sh/uv/releases/download/${release.version}/${artifact.file}`;
300
+ let contents;
225
301
  try {
226
- const result = await exec('uv', ['--version'], { env });
227
- return result.stdout.trim() || 'uv available';
302
+ contents = await (options.fetchArtifact ?? defaultFetchUvArtifact)(url);
228
303
  }
229
- catch {
230
- throw new Error(MISSING_UV_RUNTIME_INSTALL_MESSAGE);
304
+ catch (error) {
305
+ throw new KtxExpectedError(`ktx could not download uv ${release.version} (required to install the ktx Python runtime). ` +
306
+ 'Check network access to github.com and retry: ktx admin runtime install --yes. ' +
307
+ `Air-gapped hosts: place the uv binary at ${uvPath}.`, { cause: error });
308
+ }
309
+ const sha256 = createHash('sha256').update(contents).digest('hex');
310
+ if (sha256 !== artifact.sha256) {
311
+ throw new KtxExpectedError(`Downloaded uv ${release.version} failed checksum verification (a proxy or captive portal may have altered the download). Retry: ktx admin runtime install --yes`);
312
+ }
313
+ const binary = extractUvFromArchive({ file: artifact.file, contents, binaryName });
314
+ await mkdir(dirname(uvPath), { recursive: true });
315
+ const stagedPath = `${uvPath}.${process.pid}.download`;
316
+ await writeFile(stagedPath, binary);
317
+ await chmod(stagedPath, 0o755);
318
+ try {
319
+ await rename(stagedPath, uvPath);
320
+ }
321
+ catch (error) {
322
+ // On Windows a concurrent install may have won the rename; the binary at
323
+ // uvPath is checksum-pinned identical, so reuse it.
324
+ await rm(stagedPath, { force: true });
325
+ if (!(await pathExists(uvPath))) {
326
+ throw error;
327
+ }
328
+ }
329
+ return uvPath;
330
+ }
331
+ async function ensureUv(input) {
332
+ const uvPath = await ensureManagedUv({
333
+ platform: input.options.platform,
334
+ env: input.options.env,
335
+ homeDir: input.options.homeDir,
336
+ runtimeRoot: input.options.runtimeRoot,
337
+ fetchArtifact: input.options.fetchUvArtifact,
338
+ });
339
+ try {
340
+ const result = await input.exec(uvPath, ['--version'], { env: input.uvEnv });
341
+ return { uvPath, version: result.stdout.trim() || `uv ${MANAGED_UV_VERSION}` };
342
+ }
343
+ catch (error) {
344
+ throw new KtxExpectedError(`Managed uv at ${uvPath} failed to run. Delete it and retry: ktx admin runtime install --yes`, { cause: error });
231
345
  }
232
346
  }
233
347
  export async function installManagedPythonRuntime(options) {
@@ -246,21 +360,23 @@ export async function installManagedPythonRuntime(options) {
246
360
  (await pathExists(existing.python.daemonExecutable))) {
247
361
  return { status: 'ready', layout, asset, manifest: existing };
248
362
  }
363
+ // uv is acquired before the version dir is wiped, so a failed acquisition
364
+ // never destroys a previously installed runtime.
365
+ const { uvPath } = await ensureUv({ exec, uvEnv, options });
249
366
  await rm(layout.versionDir, { recursive: true, force: true });
250
367
  await mkdir(layout.versionDir, { recursive: true });
251
368
  await writeFile(layout.installLogPath, '');
252
- await ensureUv(exec, uvEnv);
253
369
  await runLogged({
254
370
  exec,
255
371
  logPath: layout.installLogPath,
256
- command: 'uv',
372
+ command: uvPath,
257
373
  args: ['python', 'install', asset.requiresPython.minimumVersion],
258
374
  env: uvEnv,
259
375
  });
260
376
  await runLogged({
261
377
  exec,
262
378
  logPath: layout.installLogPath,
263
- command: 'uv',
379
+ command: uvPath,
264
380
  args: ['venv', '--python', asset.requiresPython.minimumVersion, layout.venvDir],
265
381
  env: uvEnv,
266
382
  });
@@ -268,7 +384,7 @@ export async function installManagedPythonRuntime(options) {
268
384
  await runLogged({
269
385
  exec,
270
386
  logPath: layout.installLogPath,
271
- command: 'uv',
387
+ command: uvPath,
272
388
  args: ['pip', 'install', '--python', layout.pythonPath, wheelSpec],
273
389
  env: uvEnv,
274
390
  });
@@ -326,15 +442,15 @@ export async function doctorManagedPythonRuntime(options) {
326
442
  const exec = options.exec ?? defaultExec;
327
443
  const checks = [];
328
444
  try {
329
- const version = await ensureUv(exec, managedRuntimeUvEnv(options.env ?? process.env));
330
- checks.push(check('pass', { id: 'uv', label: 'uv', detail: version }));
445
+ const uv = await ensureUv({ exec, uvEnv: managedRuntimeUvEnv(options.env ?? process.env), options });
446
+ checks.push(check('pass', { id: 'uv', label: 'uv', detail: `${uv.version} (managed: ${uv.uvPath})` }));
331
447
  }
332
448
  catch (error) {
333
449
  checks.push(check('fail', {
334
450
  id: 'uv',
335
451
  label: 'uv',
336
452
  detail: error instanceof Error ? error.message : String(error),
337
- fix: 'Install uv, make sure it is on PATH, and run: ktx admin runtime install --yes',
453
+ fix: 'Check network access to github.com and run: ktx admin runtime install --yes',
338
454
  }));
339
455
  }
340
456
  try {
@@ -0,0 +1,7 @@
1
+ export type ManagedUvPlatformKey = 'darwin-arm64' | 'darwin-x64' | 'linux-arm64' | 'linux-x64' | 'win32-arm64' | 'win32-x64';
2
+ export interface ManagedUvArtifact {
3
+ file: string;
4
+ sha256: string;
5
+ }
6
+ export declare const MANAGED_UV_VERSION = "0.11.21";
7
+ export declare const MANAGED_UV_ARTIFACTS: Record<ManagedUvPlatformKey, ManagedUvArtifact>;
@@ -0,0 +1,11 @@
1
+ // Generated by scripts/refresh-uv-manifest.mjs. Do not edit by hand.
2
+ // Regenerate with: node scripts/refresh-uv-manifest.mjs [<uv-version>]
3
+ export const MANAGED_UV_VERSION = '0.11.21';
4
+ export const MANAGED_UV_ARTIFACTS = {
5
+ 'darwin-arm64': { file: 'uv-aarch64-apple-darwin.tar.gz', sha256: '1f921d491ba5ffeea774eb04d6681ecee379101341cbb1500394993b541bf3f4' }, // pragma: allowlist secret
6
+ 'darwin-x64': { file: 'uv-x86_64-apple-darwin.tar.gz', sha256: 'f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765' }, // pragma: allowlist secret
7
+ 'linux-arm64': { file: 'uv-aarch64-unknown-linux-musl.tar.gz', sha256: 'e71badaed2a2c3a404a0a00974b51c7ed5f5bc7be947916846005b739c68a5a2' }, // pragma: allowlist secret
8
+ 'linux-x64': { file: 'uv-x86_64-unknown-linux-musl.tar.gz', sha256: '9dadff5b9e7b1d2d011e41852a1cbca713d9d5d88194f2eb6bd240fa4fb0a719' }, // pragma: allowlist secret
9
+ 'win32-arm64': { file: 'uv-aarch64-pc-windows-msvc.zip', sha256: '74e443f8004022dde57a1bd0d10c097830f9ea8feb4ec927db52cd5d805c2f48' }, // pragma: allowlist secret
10
+ 'win32-x64': { file: 'uv-x86_64-pc-windows-msvc.zip', sha256: 'ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589' }, // pragma: allowlist secret
11
+ };
@@ -38,7 +38,7 @@ function fullOrigin(value) {
38
38
  }
39
39
  export function buildMcpSecurityConfig(input) {
40
40
  if (!isLoopbackHost(input.host) && !input.token) {
41
- throw new Error(`Binding KTX MCP to ${input.host} requires --token or KTX_MCP_TOKEN`);
41
+ throw new Error(`Binding ktx MCP to ${input.host} requires --token or KTX_MCP_TOKEN`);
42
42
  }
43
43
  const allowedHostSet = new Set(DEFAULT_ALLOWED_HOSTS);
44
44
  if (!isLoopbackHost(input.host)) {
@@ -63,16 +63,16 @@ function headerValue(headers, name) {
63
63
  export function isMcpRequestAuthorized(request, config) {
64
64
  const host = headerValue(request.headers, 'host');
65
65
  if (!host || !config.allowedHosts.includes(normalizeHostHeader(host))) {
66
- return { ok: false, status: 403, message: 'Host header is not allowed for KTX MCP.' };
66
+ return { ok: false, status: 403, message: 'Host header is not allowed for ktx MCP.' };
67
67
  }
68
68
  const origin = headerValue(request.headers, 'origin');
69
69
  if (origin && !config.allowedOrigins.includes(origin)) {
70
- return { ok: false, status: 403, message: 'Origin header is not allowed for KTX MCP.' };
70
+ return { ok: false, status: 403, message: 'Origin header is not allowed for ktx MCP.' };
71
71
  }
72
72
  if (request.path === '/mcp' && config.token) {
73
73
  const auth = headerValue(request.headers, 'authorization');
74
74
  if (auth !== `Bearer ${config.token}`) {
75
- return { ok: false, status: 401, message: 'Missing or invalid KTX MCP bearer token.' };
75
+ return { ok: false, status: 401, message: 'Missing or invalid ktx MCP bearer token.' };
76
76
  }
77
77
  }
78
78
  return { ok: true };
@@ -5,7 +5,7 @@ import { createLocalProjectMemoryIngest } from './context/memory/local-memory.js
5
5
  import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
6
6
  import { createKtxCliIngestQueryExecutor } from './ingest-query-executor.js';
7
7
  import { createKtxCliScanConnector } from './local-scan-connectors.js';
8
- import { createManagedPythonSemanticLayerComputePort } from './managed-python-command.js';
8
+ import { createLazyManagedPythonSemanticLayerComputePort } from './managed-python-command.js';
9
9
  import { createManagedDaemonSqlAnalysisPort } from './managed-python-http.js';
10
10
  function noopMcpIo() {
11
11
  return {
@@ -16,7 +16,7 @@ function noopMcpIo() {
16
16
  export async function createKtxMcpServerFactory(input) {
17
17
  const io = input.io ?? noopMcpIo();
18
18
  const queryExecutor = createKtxCliIngestQueryExecutor(input.project);
19
- const semanticLayerCompute = await createManagedPythonSemanticLayerComputePort({
19
+ const semanticLayerCompute = createLazyManagedPythonSemanticLayerComputePort({
20
20
  cliVersion: input.cliVersion,
21
21
  installPolicy: 'auto',
22
22
  io,
@@ -54,7 +54,7 @@ export async function createKtxMcpServerFactory(input) {
54
54
  });
55
55
  }
56
56
  catch (error) {
57
- io.stderr.write(`KTX MCP memory_ingest disabled: ${error instanceof Error ? error.message : String(error)}\n`);
57
+ io.stderr.write(`ktx MCP memory_ingest disabled: ${error instanceof Error ? error.message : String(error)}\n`);
58
58
  }
59
59
  return () => createDefaultKtxMcpServer({
60
60
  name: 'ktx',
@@ -37,7 +37,7 @@ export async function runKtxMcpStdioServer(options) {
37
37
  };
38
38
  transport.onclose = () => settle(resolve);
39
39
  transport.onerror = (error) => {
40
- options.io?.stderr.write(`KTX MCP stdio transport error: ${error.message}\n`);
40
+ options.io?.stderr.write(`ktx MCP stdio transport error: ${error.message}\n`);
41
41
  settle(() => reject(error));
42
42
  };
43
43
  stdin.once('end', closeTransport);
@@ -198,7 +198,7 @@ export function ActivityFeed(props) {
198
198
  const barWidth = Math.min(40, props.width - 20);
199
199
  return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [!diffEvent && !workStarted && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Connecting to ", src.type.toLowerCase(), "..."] })), diffEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Connected \u2014 found ", src.sourceCount, " ", src.itemNounPlural, " to ingest"] })), diffEvent && isIncremental && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Compared with last sync \u2014 only re-analyzing what changed" })), diffEvent && !planEvent && !workStarted && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Grouping related ", src.itemNounPlural, " together for deeper analysis..."] })), planEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Grouped into ", planEvent.chunkCount, " business area", planEvent.chunkCount === 1 ? '' : 's'] })), workStarted && !allWorkDone && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Ingesting \u2014 ", finishedAreas, "/", totalChunks || '?', " business area", totalChunks === 1 ? '' : 's', " done"] }), _jsxs(Text, { color: props.theme.muted, children: [' ', src.ingestDescription] }), totalChunks > 0 && (_jsxs(Text, { color: props.theme.active, children: [' ', progressBarOverall(finishedAreas, actives.length, totalChunks, barWidth, props.frame)] }))] })), insights.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: props.theme.text, children: " Created so far:" }), insights.map((insight, idx) => (_jsxs(Text, { color: props.theme.muted, children: [' ', insight.icon, " ", insight.text] }, `result-${idx}`)))] })), isReconciling && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Deduplicating \u2014 removing overlaps between business areas and checking for conflicts..."] })), reconEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Deduplicated", reconEvent.conflictCount > 0
200
200
  ? ` — ${reconEvent.conflictCount} conflict${reconEvent.conflictCount === 1 ? '' : 's'} resolved`
201
- : ' — no conflicts'] })), isSaving && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Saving to context layer..."] })), savedEvent && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Saved \u2014 your agents can now use the KTX context layer" })), props.showCompletion && (isDone || isError) && (_jsx(CompletionSummary, { input: props.input, theme: props.theme, frame: props.completionFrame, holdComplete: props.holdComplete }))] }));
201
+ : ' — no conflicts'] })), isSaving && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Saving to context layer..."] })), savedEvent && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Saved \u2014 your agents can now use the ktx context layer" })), props.showCompletion && (isDone || isError) && (_jsx(CompletionSummary, { input: props.input, theme: props.theme, frame: props.completionFrame, holdComplete: props.holdComplete }))] }));
202
202
  }
203
203
  function CompletionSummary(props) {
204
204
  const saved = [...props.input.events].reverse().find((e) => e.type === 'saved');
@@ -207,5 +207,5 @@ function CompletionSummary(props) {
207
207
  const isError = props.input.status === 'error';
208
208
  const sl = counterValue(slCount, props.frame);
209
209
  const wiki = counterValue(wikiCount, props.frame);
210
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: isError ? (_jsx(Text, { bold: true, color: props.theme.failed, children: "\u2717 Something went wrong \u2014 review the errors above." })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: props.theme.border, children: '─'.repeat(60) }), _jsx(Text, { bold: true, color: props.theme.complete, children: "\u2605 KTX finished ingesting your data" }), (sl > 0 || wiki > 0) && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "KTX created:" }), sl > 0 && (_jsxs(Text, { color: props.theme.active, children: [' ', "\uD83D\uDCCA ", sl, " query definition", sl === 1 ? '' : 's', " \u2014 so agents can write accurate SQL for your data"] })), wiki > 0 && (_jsxs(Text, { color: props.theme.complete, children: [' ', "\uD83D\uDCDD ", wiki, " wiki page", wiki === 1 ? '' : 's', " \u2014 so agents understand your business context"] }))] })), _jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "What to do next:" }), formatNextStepLines().map((line) => (_jsx(Text, { color: props.theme.active, children: line }, line))), props.holdComplete && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.muted, children: "Press q to exit" })] }))] })) }));
210
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: isError ? (_jsx(Text, { bold: true, color: props.theme.failed, children: "\u2717 Something went wrong \u2014 review the errors above." })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: props.theme.border, children: '─'.repeat(60) }), _jsx(Text, { bold: true, color: props.theme.complete, children: "\u2605 ktx finished ingesting your data" }), (sl > 0 || wiki > 0) && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "ktx created:" }), sl > 0 && (_jsxs(Text, { color: props.theme.active, children: [' ', "\uD83D\uDCCA ", sl, " query definition", sl === 1 ? '' : 's', " \u2014 so agents can write accurate SQL for your data"] })), wiki > 0 && (_jsxs(Text, { color: props.theme.complete, children: [' ', "\uD83D\uDCDD ", wiki, " wiki page", wiki === 1 ? '' : 's', " \u2014 so agents understand your business context"] }))] })), _jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "What to do next:" }), formatNextStepLines().map((line) => (_jsx(Text, { color: props.theme.active, children: line }, line))), props.holdComplete && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.muted, children: "Press q to exit" })] }))] })) }));
211
211
  }
@@ -31,7 +31,7 @@ function commandLines(commands, indent) {
31
31
  }
32
32
  export function formatNextStepLines(indent = ' ') {
33
33
  return [
34
- `${indent}KTX context is ready for agents. Open your coding agent from the KTX project directory and ask a data question.`,
34
+ `${indent}ktx context is ready for agents. Open your coding agent from the ktx project directory and ask a data question.`,
35
35
  `${indent}Verify with:`,
36
36
  ...commandLines(KTX_NEXT_STEP_DIRECT_COMMANDS, indent),
37
37
  ];
@@ -59,7 +59,7 @@ export function formatSetupNextStepLines(state, indent = ' ') {
59
59
  }
60
60
  if (!state.agentIntegrationReady) {
61
61
  return [
62
- `${indent}KTX context is built. Install agent rules when you want your coding agent to use it.`,
62
+ `${indent}ktx context is built. Install agent rules when you want your coding agent to use it.`,
63
63
  `${indent}$ ${'ktx setup --agents'.padEnd(KTX_NEXT_STEP_COMMAND_WIDTH)} Install CLI-based agent rules`,
64
64
  `${indent}$ ${'ktx status'.padEnd(KTX_NEXT_STEP_COMMAND_WIDTH)} Check setup and context readiness`,
65
65
  ];
@@ -1,7 +1,24 @@
1
1
  /** @internal */
2
+ export declare const MULTISELECT_NAVIGATION_FRAGMENTS: {
3
+ readonly move: "Up/Down to move";
4
+ readonly expand: "Right/Left to expand or collapse";
5
+ readonly select: "Tab to select or unselect";
6
+ readonly search: "Type to search";
7
+ readonly confirm: "Enter to confirm";
8
+ readonly back: "Escape to go back";
9
+ readonly backSearchableTree: "Escape to clear search or go back";
10
+ readonly exit: "Ctrl+C to exit";
11
+ };
12
+ /** @internal */
13
+ export declare const FLAT_MULTISELECT_NAVIGATION_HINT: string;
14
+ /** @internal */
15
+ export declare const SEARCHABLE_MULTISELECT_NAVIGATION_HINT: string;
16
+ export declare const TREE_PICKER_NAVIGATION_HINT: string;
17
+ /** @internal */
2
18
  export declare function withMenuOptionSpacing(message: string): string;
3
19
  export declare function withMenuOptionsSpacing<T extends {
4
20
  message: string;
5
21
  }>(options: T): T;
6
22
  export declare function withMultiselectNavigation(message: string): string;
23
+ export declare function withSearchableMultiselectNavigation(message: string): string;
7
24
  export declare function withTextInputNavigation(message: string): string;
@@ -1,4 +1,44 @@
1
- const MULTISELECT_MENU_NAVIGATION_HINT = 'Use Up/Down to move, Space to select or unselect, Enter to confirm, Escape to go back, or Ctrl+C to exit.';
1
+ /** @internal */
2
+ export const MULTISELECT_NAVIGATION_FRAGMENTS = {
3
+ move: 'Up/Down to move',
4
+ expand: 'Right/Left to expand or collapse',
5
+ select: 'Tab to select or unselect',
6
+ search: 'Type to search',
7
+ confirm: 'Enter to confirm',
8
+ back: 'Escape to go back',
9
+ backSearchableTree: 'Escape to clear search or go back',
10
+ exit: 'Ctrl+C to exit',
11
+ };
12
+ function composeNavigationHint(fragments) {
13
+ return `${fragments.join(', ')}.`;
14
+ }
15
+ const fragment = MULTISELECT_NAVIGATION_FRAGMENTS;
16
+ /** @internal */
17
+ export const FLAT_MULTISELECT_NAVIGATION_HINT = composeNavigationHint([
18
+ fragment.move,
19
+ fragment.select,
20
+ fragment.confirm,
21
+ fragment.back,
22
+ fragment.exit,
23
+ ]);
24
+ /** @internal */
25
+ export const SEARCHABLE_MULTISELECT_NAVIGATION_HINT = composeNavigationHint([
26
+ fragment.move,
27
+ fragment.select,
28
+ fragment.search,
29
+ fragment.confirm,
30
+ fragment.back,
31
+ fragment.exit,
32
+ ]);
33
+ export const TREE_PICKER_NAVIGATION_HINT = composeNavigationHint([
34
+ fragment.move,
35
+ fragment.expand,
36
+ fragment.select,
37
+ fragment.search,
38
+ fragment.confirm,
39
+ fragment.backSearchableTree,
40
+ fragment.exit,
41
+ ]);
2
42
  const TEXT_INPUT_NAVIGATION_HINT = 'Press Escape to go back.';
3
43
  function removeTrailingBlankLines(message) {
4
44
  return message.replace(/\n+$/, '');
@@ -45,10 +85,16 @@ export function withMenuOptionsSpacing(options) {
45
85
  return { ...options, message: withMenuOptionSpacing(options.message) };
46
86
  }
47
87
  export function withMultiselectNavigation(message) {
48
- if (message.includes(MULTISELECT_MENU_NAVIGATION_HINT)) {
88
+ if (message.includes(FLAT_MULTISELECT_NAVIGATION_HINT)) {
89
+ return message;
90
+ }
91
+ return `${message}\n${FLAT_MULTISELECT_NAVIGATION_HINT}`;
92
+ }
93
+ export function withSearchableMultiselectNavigation(message) {
94
+ if (message.includes(SEARCHABLE_MULTISELECT_NAVIGATION_HINT)) {
49
95
  return message;
50
96
  }
51
- return `${message}\n${MULTISELECT_MENU_NAVIGATION_HINT}`;
97
+ return `${message}\n${SEARCHABLE_MULTISELECT_NAVIGATION_HINT}`;
52
98
  }
53
99
  export function withTextInputNavigation(message) {
54
100
  const messageWithoutHint = removeTrailingBlankLines(message)
@@ -2,7 +2,7 @@
2
2
  You are processing ONE WorkUnit of a multi-file ingest bundle. The WorkUnit
3
3
  gives you a slice of raw source files (LookML views, dbt/MetricFlow YAMLs,
4
4
  Metabase card JSONs, Notion pages, or similar) and you must translate that
5
- slice into KTX semantic-layer sources and/or knowledge wiki pages, in one pass.
5
+ slice into ktx semantic-layer sources and/or knowledge wiki pages, in one pass.
6
6
  You run in an isolated WorkUnit worktree. Deterministic projection output,
7
7
  existing project memory, and listed dependency paths are visible; sibling
8
8
  WorkUnit edits from this same job are not visible until the runner integrates
@@ -10,7 +10,7 @@ accepted patches.
10
10
  </role>
11
11
 
12
12
  <stance>
13
- Assertive. The bundle was explicitly submitted for ingest. Default to capturing everything the raw files declare that maps cleanly to KTX: one SL source per table/view, one wiki page per non-obvious business rule or alias. Do not abandon a WorkUnit because "some content overlaps with another WU"; use `ingest_triage` to reconcile, do not skip.
13
+ Assertive. The bundle was explicitly submitted for ingest. Default to capturing everything the raw files declare that maps cleanly to ktx: one SL source per table/view, one wiki page per non-obvious business rule or alias. Do not abandon a WorkUnit because "some content overlaps with another WU"; use `ingest_triage` to reconcile, do not skip.
14
14
  </stance>
15
15
 
16
16
  <workflow>
@@ -1,5 +1,5 @@
1
1
  <role>
2
- You are ingesting an external technical artifact (a LookML view, dbt model, schema description, business glossary, or other reference document) into KTX organizational memory. The user has explicitly submitted this content for bulk ingest. Assume it is intentional and worth capturing.
2
+ You are ingesting an external technical artifact (a LookML view, dbt model, schema description, business glossary, or other reference document) into ktx organizational memory. The user has explicitly submitted this content for bulk ingest. Assume it is intentional and worth capturing.
3
3
  </role>
4
4
 
5
5
  <stance>
@@ -18,7 +18,7 @@ A single artifact typically produces multiple actions: one SL source per table/v
18
18
  </workflow>
19
19
 
20
20
  <scope>
21
- All wiki writes go to the GLOBAL scope - they will be visible to every user of this KTX project. Phrase wiki pages as objective business knowledge, not personal preference. The `wiki_write` tool handles scope selection automatically for external ingest.
21
+ All wiki writes go to the GLOBAL scope - they will be visible to every user of this ktx project. Phrase wiki pages as objective business knowledge, not personal preference. The `wiki_write` tool handles scope selection automatically for external ingest.
22
22
  </scope>
23
23
 
24
24
  <do_not>
@@ -7,7 +7,7 @@ const DATABASE_INGEST_REPLACEMENTS = [
7
7
  [/\bWriting schema artifacts\b/gi, 'Writing schema context'],
8
8
  [/\bEnriching schema metadata\b/gi, 'Building enriched schema context'],
9
9
  [
10
- /\bKTX scan enrichment failed after structural scan completed\b/gi,
10
+ /\bktx scan enrichment failed after structural scan completed\b/gi,
11
11
  'Database enrichment failed after schema context completed',
12
12
  ],
13
13
  [/\bstructural scan\b/gi, 'schema context'],
@@ -535,8 +535,8 @@ function createPlainPublicIngestProgress(io, options) {
535
535
  };
536
536
  }
537
537
  const INTERNAL_STATUS_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
538
- const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
539
- const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/;
538
+ const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|ktx Python runtime is required|ktx daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
539
+ const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed ktx command\.?$/;
540
540
  function trimErrorPrefix(line) {
541
541
  return line.replace(/^Error:\s*/, '');
542
542
  }
@@ -545,7 +545,7 @@ function capturedFailureMessage(output) {
545
545
  .split(/\r?\n/)
546
546
  .map((line) => line.trim())
547
547
  .filter((line) => line.length > 0)
548
- .filter((line) => !line.startsWith('KTX scan completed'))
548
+ .filter((line) => !line.startsWith('ktx scan completed'))
549
549
  .filter((line) => !INTERNAL_STATUS_LINE_RE.test(line))
550
550
  .map(publicIngestOutputLine);
551
551
  const actionableIndex = lines.findIndex((line) => ACTIONABLE_FAILURE_LINE_RE.test(line));
@@ -1,7 +1,7 @@
1
1
  const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
2
2
  export function assertCliVersion(value, source) {
3
3
  if (typeof value !== 'string' || !semverPattern.test(value)) {
4
- throw new Error(`Invalid KTX CLI version in ${source}`);
4
+ throw new Error(`Invalid ktx CLI version in ${source}`);
5
5
  }
6
6
  return value;
7
7
  }
@@ -44,7 +44,7 @@ export function resolveProjectRuntimeRequirements(config, options = {}) {
44
44
  requirements.push({
45
45
  feature: 'core',
46
46
  reason: 'database-introspection',
47
- detail: 'Database introspection fallback uses the KTX daemon.',
47
+ detail: 'Database introspection fallback uses the ktx daemon.',
48
48
  });
49
49
  }
50
50
  for (const [connectionId, connection] of Object.entries(config.connections)) {