@kaelio/ktx 0.10.0 → 0.12.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 (193) hide show
  1. package/assets/python/{kaelio_ktx-0.10.0-py3-none-any.whl → kaelio_ktx-0.12.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 +7 -3
  9. package/dist/cli-runtime.d.ts +2 -0
  10. package/dist/cli-runtime.js +14 -8
  11. package/dist/commands/connection-commands.js +1 -1
  12. package/dist/commands/ingest-commands.js +4 -4
  13. package/dist/commands/mcp-commands.js +12 -12
  14. package/dist/commands/runtime-commands.js +4 -4
  15. package/dist/commands/setup-commands.js +6 -5
  16. package/dist/commands/sl-commands.js +1 -1
  17. package/dist/commands/sql-commands.js +1 -1
  18. package/dist/commands/status-commands.js +1 -1
  19. package/dist/community-cta.d.ts +11 -0
  20. package/dist/community-cta.js +19 -0
  21. package/dist/connection.js +1 -1
  22. package/dist/connectors/clickhouse/connector.js +1 -1
  23. package/dist/connectors/mysql/connector.js +1 -1
  24. package/dist/connectors/snowflake/connector.d.ts +1 -1
  25. package/dist/connectors/sqlite/connector.js +2 -25
  26. package/dist/connectors/sqlserver/connector.js +3 -3
  27. package/dist/context/connections/connection-type.d.ts +1 -1
  28. package/dist/context/connections/read-only-sql.d.ts +1 -0
  29. package/dist/context/connections/read-only-sql.js +116 -2
  30. package/dist/context/core/git-env.d.ts +12 -1
  31. package/dist/context/core/git-env.js +17 -2
  32. package/dist/context/core/git.service.d.ts +23 -0
  33. package/dist/context/core/git.service.js +86 -15
  34. package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
  35. package/dist/context/ingest/adapters/looker/client.js +7 -2
  36. package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
  37. package/dist/context/ingest/adapters/looker/factory.js +9 -0
  38. package/dist/context/ingest/adapters/looker/mapping.js +1 -1
  39. package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
  40. package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
  41. package/dist/context/ingest/adapters/metabase/client.js +1 -1
  42. package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
  43. package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
  44. package/dist/context/ingest/artifact-gates.d.ts +2 -6
  45. package/dist/context/ingest/artifact-gates.js +5 -47
  46. package/dist/context/ingest/constrained-repair.d.ts +55 -0
  47. package/dist/context/ingest/constrained-repair.js +167 -0
  48. package/dist/context/ingest/final-gate-repair.d.ts +9 -11
  49. package/dist/context/ingest/final-gate-repair.js +40 -128
  50. package/dist/context/ingest/finalization-scope.d.ts +1 -1
  51. package/dist/context/ingest/finalization-scope.js +15 -15
  52. package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
  53. package/dist/context/ingest/ingest-bundle.runner.js +101 -67
  54. package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
  55. package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
  56. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
  57. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
  58. package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
  59. package/dist/context/ingest/local-bundle-runtime.js +9 -10
  60. package/dist/context/ingest/local-ingest.d.ts +2 -0
  61. package/dist/context/ingest/local-ingest.js +2 -0
  62. package/dist/context/ingest/memory-flow/view-model.js +1 -1
  63. package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
  64. package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
  65. package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
  66. package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
  67. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
  68. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
  69. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
  70. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
  71. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
  72. package/dist/context/llm/ai-sdk-runtime.js +2 -2
  73. package/dist/context/llm/claude-code-runtime.js +1 -1
  74. package/dist/context/llm/local-config.js +1 -1
  75. package/dist/context/llm/runtime-tools.js +2 -2
  76. package/dist/context/mcp/context-tools.js +7 -7
  77. package/dist/context/mcp/local-project-ports.js +23 -54
  78. package/dist/context/memory/local-memory.js +4 -1
  79. package/dist/context/memory/memory-agent.service.js +1 -1
  80. package/dist/context/project/config.d.ts +11 -4
  81. package/dist/context/project/config.js +85 -30
  82. package/dist/context/project/driver-schemas.js +1 -1
  83. package/dist/context/project/mappings-yaml-schema.js +2 -2
  84. package/dist/context/project/project.js +12 -4
  85. package/dist/context/scan/description-generation.js +4 -4
  86. package/dist/context/scan/local-enrichment-artifacts.js +2 -1
  87. package/dist/context/scan/local-scan.js +2 -2
  88. package/dist/context/scan/local-structural-artifacts.js +5 -5
  89. package/dist/context/scan/relationship-benchmark-report.js +1 -1
  90. package/dist/context/scan/relationship-discovery.js +3 -3
  91. package/dist/context/scan/relationship-llm-proposal.js +3 -3
  92. package/dist/context/sl/local-query.js +3 -33
  93. package/dist/context/sl/local-sl.d.ts +0 -8
  94. package/dist/context/sl/local-sl.js +44 -69
  95. package/dist/context/sl/semantic-layer.service.d.ts +25 -8
  96. package/dist/context/sl/semantic-layer.service.js +109 -56
  97. package/dist/context/sl/source-files.d.ts +46 -0
  98. package/dist/context/sl/source-files.js +131 -0
  99. package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
  100. package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
  101. package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
  102. package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
  103. package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
  104. package/dist/context/sql-analysis/dialect.d.ts +2 -0
  105. package/dist/context/sql-analysis/dialect.js +20 -0
  106. package/dist/context/tools/base-tool.d.ts +6 -19
  107. package/dist/context/tools/base-tool.js +0 -14
  108. package/dist/context-build-view.js +5 -5
  109. package/dist/database-tree-picker.js +18 -3
  110. package/dist/demo-assets.js +0 -1
  111. package/dist/doctor.d.ts +1 -1
  112. package/dist/doctor.js +31 -23
  113. package/dist/errors.d.ts +31 -0
  114. package/dist/errors.js +44 -0
  115. package/dist/ingest.d.ts +1 -1
  116. package/dist/ingest.js +8 -2
  117. package/dist/io/symbols.d.ts +2 -0
  118. package/dist/io/symbols.js +2 -0
  119. package/dist/io/tty.d.ts +17 -0
  120. package/dist/io/tty.js +21 -0
  121. package/dist/links.d.ts +1 -0
  122. package/dist/links.js +1 -0
  123. package/dist/llm/embedding-health.js +1 -1
  124. package/dist/llm/embedding-provider.js +3 -3
  125. package/dist/llm/model-provider.js +1 -1
  126. package/dist/local-adapters.d.ts +1 -0
  127. package/dist/local-adapters.js +2 -2
  128. package/dist/local-scan-connectors.js +1 -1
  129. package/dist/managed-local-embeddings.js +17 -8
  130. package/dist/managed-mcp-daemon.js +3 -3
  131. package/dist/managed-python-command.d.ts +7 -0
  132. package/dist/managed-python-command.js +34 -8
  133. package/dist/managed-python-daemon.js +2 -2
  134. package/dist/managed-python-http.js +3 -3
  135. package/dist/managed-python-runtime.d.ts +30 -1
  136. package/dist/managed-python-runtime.js +134 -18
  137. package/dist/managed-uv-release.d.ts +7 -0
  138. package/dist/managed-uv-release.js +11 -0
  139. package/dist/mcp-http-server.js +4 -4
  140. package/dist/mcp-server-factory.js +3 -3
  141. package/dist/mcp-stdio-server.js +1 -1
  142. package/dist/memory-flow-hud.js +2 -2
  143. package/dist/next-steps.js +2 -2
  144. package/dist/prompt-navigation.d.ts +17 -0
  145. package/dist/prompt-navigation.js +49 -3
  146. package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
  147. package/dist/prompts/memory_agent_external_ingest.md +2 -2
  148. package/dist/public-ingest-copy.js +1 -1
  149. package/dist/public-ingest.js +3 -3
  150. package/dist/release-version.js +1 -1
  151. package/dist/runtime-requirements.js +1 -1
  152. package/dist/runtime.js +9 -9
  153. package/dist/scan.js +1 -1
  154. package/dist/setup-agents.js +22 -35
  155. package/dist/setup-banner.d.ts +20 -0
  156. package/dist/setup-banner.js +39 -0
  157. package/dist/setup-context.js +24 -15
  158. package/dist/setup-databases.js +31 -59
  159. package/dist/setup-demo-tour.js +12 -8
  160. package/dist/setup-embeddings.js +9 -9
  161. package/dist/setup-interrupt.js +1 -1
  162. package/dist/setup-models.d.ts +4 -1
  163. package/dist/setup-models.js +54 -28
  164. package/dist/setup-project.js +29 -5
  165. package/dist/setup-prompts.js +16 -5
  166. package/dist/setup-ready-menu.js +1 -1
  167. package/dist/setup-sources.js +27 -7
  168. package/dist/setup.d.ts +25 -0
  169. package/dist/setup.js +90 -19
  170. package/dist/skills/analytics/SKILL.md +3 -3
  171. package/dist/skills/dbt_ingest/SKILL.md +3 -3
  172. package/dist/skills/looker_ingest/SKILL.md +3 -3
  173. package/dist/skills/lookml_ingest/SKILL.md +7 -7
  174. package/dist/skills/metabase_ingest/SKILL.md +4 -4
  175. package/dist/skills/metricflow_ingest/SKILL.md +15 -15
  176. package/dist/skills/notion_synthesize/SKILL.md +1 -1
  177. package/dist/skills/sl/SKILL.md +3 -3
  178. package/dist/skills/sl_capture/SKILL.md +1 -1
  179. package/dist/skills/wiki_capture/SKILL.md +1 -1
  180. package/dist/source-mapping.js +1 -1
  181. package/dist/startup-profile.js +1 -1
  182. package/dist/status-project.d.ts +0 -2
  183. package/dist/status-project.js +4 -6
  184. package/dist/telemetry/command-hook.d.ts +24 -0
  185. package/dist/telemetry/command-hook.js +37 -3
  186. package/dist/telemetry/events.d.ts +1 -1
  187. package/dist/telemetry/exception.js +14 -0
  188. package/dist/telemetry/index.d.ts +2 -2
  189. package/dist/telemetry/index.js +2 -2
  190. package/dist/text-ingest.js +1 -1
  191. package/dist/tree-picker-tui.d.ts +0 -1
  192. package/dist/tree-picker-tui.js +2 -3
  193. package/package.json +1 -1
@@ -9,21 +9,11 @@ import { markKtxSetupStateStepComplete } from './context/project/setup-config.js
9
9
  import { serializeKtxProjectConfig } from './context/project/config.js';
10
10
  import { strToU8, zipSync } from 'fflate';
11
11
  import { errorMessage, writePrefixedLines } from './clack.js';
12
+ import { isWritableTtyOutput } from './io/tty.js';
12
13
  import { createKtxSetupPromptAdapter, createKtxSetupUiAdapter, } from './setup-prompts.js';
13
14
  import { readKtxMcpDaemonStatus } from './managed-mcp-daemon.js';
15
+ import { withMultiselectNavigation } from './prompt-navigation.js';
14
16
  const MCP_DAEMON_REQUIRED_NOTICE = 'mcp-daemon-required';
15
- function isWritableTtyOutput(output) {
16
- return (output.isTTY === true &&
17
- typeof output.on === 'function' &&
18
- typeof output.columns !== 'undefined');
19
- }
20
- function writeSetupInfo(io, message) {
21
- if (isWritableTtyOutput(io.stdout)) {
22
- log.info(message, { output: io.stdout });
23
- return;
24
- }
25
- io.stdout.write(`${message}\n`);
26
- }
27
17
  function writeSetupStep(io, message) {
28
18
  if (isWritableTtyOutput(io.stdout)) {
29
19
  log.step(message, { output: io.stdout });
@@ -152,7 +142,7 @@ function codexSnippet(endpoint) {
152
142
  if (endpoint.tokenAuth) {
153
143
  return [
154
144
  'Codex MCP config does not currently document HTTP headers.',
155
- 'Run KTX on loopback without token auth for Codex, or configure headers after Codex documents support.',
145
+ 'Run ktx on loopback without token auth for Codex, or configure headers after Codex documents support.',
156
146
  ].join('\n');
157
147
  }
158
148
  return [`[mcp_servers.ktx]`, `url = "${endpoint.url}"`].join('\n');
@@ -410,16 +400,16 @@ function cliInstructionContent(input) {
410
400
  return [
411
401
  '---',
412
402
  'name: ktx',
413
- 'description: Use local KTX semantic context and wiki knowledge for this project.',
403
+ 'description: Use local ktx semantic context and wiki knowledge for this project.',
414
404
  '---',
415
405
  '',
416
- '# KTX Local Context',
406
+ '# ktx Local Context',
417
407
  '',
418
- 'This is an admin/developer CLI helper. End-user data agents should use the KTX MCP tools when available.',
408
+ 'This is an admin/developer CLI helper. End-user data agents should use the ktx MCP tools when available.',
419
409
  '',
420
410
  `Use this project with \`--project-dir ${input.projectDir}\`.`,
421
- 'Commands are pinned to the local KTX CLI path that created this file, so agents do not need `ktx` in PATH.',
422
- 'If the CLI path no longer exists after moving this checkout or reinstalling KTX, rerun `ktx setup --agents`.',
411
+ 'Commands are pinned to the local ktx CLI path that created this file, so agents do not need `ktx` in PATH.',
412
+ 'If the CLI path no longer exists after moving this checkout or reinstalling ktx, rerun `ktx setup --agents`.',
423
413
  '',
424
414
  'Agents must not print secrets, credential references, environment variable values, or file contents from ' +
425
415
  '`.ktx/secrets`.',
@@ -530,7 +520,7 @@ function mergeManifest(projectDir, existing, installs, entries) {
530
520
  export async function removeKtxAgentInstall(projectDir, io) {
531
521
  const manifest = await readKtxAgentInstallManifest(projectDir);
532
522
  if (!manifest) {
533
- io.stdout.write('No KTX agent installation manifest found.\n');
523
+ io.stdout.write('No ktx agent installation manifest found.\n');
534
524
  return 0;
535
525
  }
536
526
  for (const entry of manifest.entries) {
@@ -540,7 +530,7 @@ export async function removeKtxAgentInstall(projectDir, io) {
540
530
  await removeJsonKey(entry.path, entry.jsonPath).catch(() => undefined);
541
531
  }
542
532
  await rm(agentInstallManifestPath(projectDir), { force: true });
543
- io.stdout.write('Removed KTX agent integration files from manifest.\n');
533
+ io.stdout.write('Removed ktx agent integration files from manifest.\n');
544
534
  return 0;
545
535
  }
546
536
  function createPromptAdapter() {
@@ -772,7 +762,7 @@ function formatAgentNextActions(input) {
772
762
  if (claudeCodeInstall) {
773
763
  lines.push(`${step}. Open Claude Code`);
774
764
  if (claudeCodeInstall.scope === 'project') {
775
- lines.push(' Open Claude Code from the KTX project directory:');
765
+ lines.push(' Open Claude Code from the ktx project directory:');
776
766
  lines.push('');
777
767
  lines.push(' RUN:');
778
768
  lines.push(` cd ${shellScriptQuote(projectDir)}`);
@@ -789,7 +779,7 @@ function formatAgentNextActions(input) {
789
779
  if (cursorInstall) {
790
780
  lines.push(`${step}. Open Cursor`);
791
781
  if (cursorInstall.scope === 'project') {
792
- lines.push(' Open Cursor from the KTX project directory:');
782
+ lines.push(' Open Cursor from the ktx project directory:');
793
783
  lines.push('');
794
784
  lines.push(' OPEN:');
795
785
  lines.push(` ${projectDir}`);
@@ -802,7 +792,7 @@ function formatAgentNextActions(input) {
802
792
  }
803
793
  if (input.installs.some((install) => install.target === 'claude-desktop')) {
804
794
  lines.push(`${step}. Restart Claude Desktop`);
805
- lines.push(' Claude Desktop loads KTX MCP after restart.');
795
+ lines.push(' Claude Desktop loads ktx MCP after restart.');
806
796
  pushBlankLine(lines);
807
797
  step += 1;
808
798
  const skillBundlePaths = claudeDesktopSkillBundlePathsForInstalls(projectDir, input.installs);
@@ -813,7 +803,7 @@ function formatAgentNextActions(input) {
813
803
  for (const path of skillBundlePaths) {
814
804
  lines.push(` ${path}`);
815
805
  }
816
- lines.push(' Toggle the uploaded KTX skills on.');
806
+ lines.push(' Toggle the uploaded ktx skills on.');
817
807
  pushBlankLine(lines);
818
808
  step += 1;
819
809
  }
@@ -863,22 +853,19 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
863
853
  return { status: 'skipped', projectDir: args.projectDir };
864
854
  }
865
855
  const prompts = deps.prompts ?? createPromptAdapter();
866
- if (args.inputMode === 'auto' && args.target === undefined) {
867
- writeSetupInfo(io, 'Space to select, Enter to confirm, Esc to go back.');
868
- }
869
856
  const mode = args.inputMode === 'disabled'
870
857
  ? args.mode
871
858
  : (await prompts.select({
872
- message: 'What should agents be allowed to do with this KTX project?',
859
+ message: 'What should agents be allowed to do with this ktx project?',
873
860
  options: [
874
861
  {
875
862
  value: 'mcp',
876
- label: 'Ask data questions with KTX MCP',
863
+ label: 'Ask data questions with ktx MCP',
877
864
  hint: 'Installs the MCP connection and analytics workflow skill. Best for normal use.',
878
865
  },
879
866
  {
880
867
  value: 'mcp-cli',
881
- label: 'Ask data questions + manage KTX with CLI commands',
868
+ label: 'Ask data questions + manage ktx with CLI commands',
882
869
  hint: 'Adds an admin CLI skill so agents can run ktx status, sl, wiki, and setup commands.',
883
870
  },
884
871
  {
@@ -899,7 +886,7 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
899
886
  : args.inputMode === 'disabled'
900
887
  ? []
901
888
  : (await prompts.multiselect({
902
- message: 'Which agent targets should KTX install?',
889
+ message: withMultiselectNavigation('Which agent targets should ktx install?'),
903
890
  options: [
904
891
  { value: 'claude-code', label: 'Claude Code' },
905
892
  { value: 'claude-desktop', label: 'Claude Desktop' },
@@ -924,17 +911,17 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
924
911
  scopeTargets.length > 0 &&
925
912
  scopeTargets.every(targetSupportsGlobalScope)
926
913
  ? (await prompts.select({
927
- message: `Where should KTX install supported agent config?\n\nKTX project: ${resolve(args.projectDir)}`,
914
+ message: `Where should ktx install supported agent config?\n\nktx project: ${resolve(args.projectDir)}`,
928
915
  options: [
929
916
  {
930
917
  value: 'project',
931
- label: 'Project scope (KTX project directory)',
932
- hint: 'Only agents opened from this KTX project path load the project-scoped config.',
918
+ label: 'Project scope (ktx project directory)',
919
+ hint: 'Only agents opened from this ktx project path load the project-scoped config.',
933
920
  },
934
921
  {
935
922
  value: 'global',
936
923
  label: 'Global scope (user config)',
937
- hint: 'Agents can load this KTX project from any working directory.',
924
+ hint: 'Agents can load this ktx project from any working directory.',
938
925
  },
939
926
  ],
940
927
  }))
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The `ktx setup` intro banner: a lowercase ktx wordmark drawn with
3
+ * half-block glyphs over the brand-orange gradient, followed by the product
4
+ * tagline. Rendering is a pure function of the options so output stays
5
+ * deterministic in tests.
6
+ */
7
+ export interface KtxSetupBannerOptions {
8
+ /** Terminal width in columns. */
9
+ columns: number;
10
+ /** Color depth in bits, as reported by `tty.WriteStream#getColorDepth`; 1 disables color. */
11
+ colorDepth: number;
12
+ /** Whether the terminal renders Unicode block glyphs. */
13
+ unicode: boolean;
14
+ }
15
+ /**
16
+ * Returns the banner block ending right above the clack intro line, or an
17
+ * empty string when the terminal cannot display it (no Unicode support or
18
+ * too narrow).
19
+ */
20
+ export declare function renderKtxSetupBanner(options: KtxSetupBannerOptions): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The `ktx setup` intro banner: a lowercase ktx wordmark drawn with
3
+ * half-block glyphs over the brand-orange gradient, followed by the product
4
+ * tagline. Rendering is a pure function of the options so output stays
5
+ * deterministic in tests.
6
+ */
7
+ const WORDMARK = [
8
+ { art: '███ ███', rgb: [253, 186, 116], ansi256: 215 },
9
+ { art: '███ ▄██▀ ███████ ▀██▄ ▄██▀', rgb: [251, 146, 60], ansi256: 214 },
10
+ { art: '███▄██▀ ███ ▀████▀', rgb: [249, 115, 22], ansi256: 208 },
11
+ { art: '███▀██▄ ███ ▄████▄', rgb: [234, 88, 12], ansi256: 202 },
12
+ { art: '███ ▀██▄ ███ ▄██▀ ▀██▄', rgb: [194, 65, 12], ansi256: 166 },
13
+ ];
14
+ const TAGLINE = 'context layer for data agents';
15
+ const INDENT = ' ';
16
+ const BANNER_WIDTH = Math.max(...WORDMARK.map((row) => row.art.length), TAGLINE.length) + INDENT.length;
17
+ /**
18
+ * Returns the banner block ending right above the clack intro line, or an
19
+ * empty string when the terminal cannot display it (no Unicode support or
20
+ * too narrow).
21
+ */
22
+ export function renderKtxSetupBanner(options) {
23
+ if (!options.unicode || options.columns < BANNER_WIDTH) {
24
+ return '';
25
+ }
26
+ const art = WORDMARK.map((row) => INDENT + colorizeBannerRow(row, options.colorDepth));
27
+ const tagline = INDENT + (options.colorDepth > 1 ? `\u001b[2m${TAGLINE}\u001b[22m` : TAGLINE);
28
+ return `\n${art.join('\n')}\n\n${tagline}\n\n`;
29
+ }
30
+ function colorizeBannerRow(row, colorDepth) {
31
+ if (colorDepth >= 24) {
32
+ const [r, g, b] = row.rgb;
33
+ return `\u001b[38;2;${r};${g};${b}m${row.art}\u001b[39m`;
34
+ }
35
+ if (colorDepth >= 8) {
36
+ return `\u001b[38;5;${row.ansi256}m${row.art}\u001b[39m`;
37
+ }
38
+ return row.art;
39
+ }
@@ -4,7 +4,7 @@ import { join, resolve } from 'node:path';
4
4
  import { loadKtxProject } from './context/project/project.js';
5
5
  import { markKtxSetupStateStepComplete, readKtxSetupState } from './context/project/setup-config.js';
6
6
  import { serializeKtxProjectConfig } from './context/project/config.js';
7
- import { errorMessage, writePrefixedLines } from './clack.js';
7
+ import { createCliSpinner, errorMessage, writePrefixedLines } from './clack.js';
8
8
  import { formatErrorDetail } from './telemetry/scrubber.js';
9
9
  import { buildPublicIngestPlan } from './public-ingest.js';
10
10
  import { runKtxConnection } from './connection.js';
@@ -189,13 +189,22 @@ async function defaultGateTestConnection(projectDir, connectionId, io) {
189
189
  * test's output is captured in a buffer and discarded so raw error text never
190
190
  * reaches the user — callers surface only the connection id and connector type.
191
191
  */
192
- async function testRequiredConnections(projectDir, project, targets, testConnection) {
192
+ async function testRequiredConnections(projectDir, project, targets, testConnection, io) {
193
193
  const failures = [];
194
- for (const connectionId of requiredConnectionIds(targets)) {
194
+ const connectionIds = requiredConnectionIds(targets);
195
+ for (const [index, connectionId] of connectionIds.entries()) {
196
+ const driver = connectorTypeLabel(project, connectionId);
197
+ const counter = connectionIds.length > 1 ? ` (${index + 1}/${connectionIds.length})` : '';
198
+ const spinner = createCliSpinner(io);
199
+ spinner.start(`Testing connection ${connectionId}${counter}…`);
195
200
  const buffered = createBufferedCommandIo();
196
201
  const exitCode = await testConnection(projectDir, connectionId, buffered);
197
- if (exitCode !== 0) {
198
- failures.push({ connectionId, driver: connectorTypeLabel(project, connectionId) });
202
+ if (exitCode === 0) {
203
+ spinner.stop(`Connection ${connectionId} (${driver}) is reachable`);
204
+ }
205
+ else {
206
+ spinner.error(`Connection ${connectionId} (${driver}) is not reachable`);
207
+ failures.push({ connectionId, driver });
199
208
  }
200
209
  }
201
210
  return failures.length === 0 ? { ok: true } : { ok: false, failures };
@@ -213,7 +222,7 @@ async function prepareBuildTargets(args, io) {
213
222
  if (args.allowEmpty === true) {
214
223
  return { kind: 'result', result: { status: 'skipped', projectDir: args.projectDir } };
215
224
  }
216
- io.stderr.write('No databases or context sources are configured for a KTX context build.\n');
225
+ io.stderr.write('No databases or context sources are configured for a ktx context build.\n');
217
226
  return { kind: 'result', result: { status: 'failed', projectDir: args.projectDir } };
218
227
  }
219
228
  const preflightPlan = buildPublicIngestPlan(project, { projectDir: project.projectDir, all: true });
@@ -228,12 +237,12 @@ async function prepareBuildTargets(args, io) {
228
237
  return { kind: 'ready', project, targets };
229
238
  }
230
239
  function writeConnectionGateFailureLines(io, projectDir, failures) {
231
- io.stderr.write('KTX cannot build context: a required connection failed its live test.\n\n');
240
+ io.stderr.write('ktx cannot build context: a required connection failed its live test.\n\n');
232
241
  io.stderr.write('Failed connections:\n');
233
242
  for (const failure of failures) {
234
243
  io.stderr.write(` ${failure.connectionId} (${failure.driver})\n`);
235
244
  }
236
- io.stderr.write('\nEach connection must be reachable before KTX builds context.\n');
245
+ io.stderr.write('\nEach connection must be reachable before ktx builds context.\n');
237
246
  io.stderr.write(`Run \`ktx connection test <id> --project-dir ${resolve(projectDir)}\` to see the error, fix the connection, then retry.\n`);
238
247
  }
239
248
  function connectionGateFailureReason(failures) {
@@ -391,7 +400,7 @@ async function markContextComplete(projectDir) {
391
400
  await markKtxSetupStateStepComplete(projectDir, 'context');
392
401
  }
393
402
  function writeMissingCapabilities(missing, io) {
394
- io.stderr.write('KTX cannot build agent-ready context yet.\n\n');
403
+ io.stderr.write('ktx cannot build agent-ready context yet.\n\n');
395
404
  io.stderr.write('Missing:\n');
396
405
  for (const item of missing) {
397
406
  io.stderr.write(` ${item}\n`);
@@ -404,7 +413,7 @@ function writeSkippedContext(io) {
404
413
  io.stdout.write('\nLeaving context unbuilt for now.\n');
405
414
  }
406
415
  function writeSuccess(readiness, targets, io) {
407
- io.stdout.write('\nKTX context is ready for agents.\n\n');
416
+ io.stdout.write('\nktx context is ready for agents.\n\n');
408
417
  io.stdout.write('Databases:\n');
409
418
  if (targets.primarySourceConnectionIds.length === 0) {
410
419
  io.stdout.write(' none\n');
@@ -428,7 +437,7 @@ function writeSuccess(readiness, targets, io) {
428
437
  io.stdout.write(` Semantic search: ${readiness.semanticSearchReady ? 'ready' : 'not ready'}\n`);
429
438
  }
430
439
  function writeExistingContextSuccess(readiness, io) {
431
- io.stdout.write('\nKTX context is ready for agents.\n\n');
440
+ io.stdout.write('\nktx context is ready for agents.\n\n');
432
441
  io.stdout.write('Existing context artifacts were found from setup ingest.\n\n');
433
442
  io.stdout.write('Verification:\n');
434
443
  io.stdout.write(` Agent context: ${readiness.agentContextReady ? 'ready' : 'not ready'}\n`);
@@ -436,8 +445,8 @@ function writeExistingContextSuccess(readiness, io) {
436
445
  }
437
446
  async function promptForBuild(prompts) {
438
447
  return (await prompts.select({
439
- message: 'Build KTX context for agents?\n\n' +
440
- 'KTX is fully configured and ready to build context. This may take a few minutes to a few hours.',
448
+ message: 'Build ktx context for agents?\n\n' +
449
+ 'ktx is fully configured and ready to build context. This may take a few minutes to a few hours.',
441
450
  options: [
442
451
  { value: 'build', label: 'Build context now (recommended)' },
443
452
  { value: 'skip', label: 'Leave context unbuilt and exit setup' },
@@ -517,7 +526,7 @@ async function runBuild(args, io, deps, project, targets) {
517
526
  failureReason: readiness.details.join(' '),
518
527
  ...(lastSourceProgress ? { sourceProgress: lastSourceProgress } : {}),
519
528
  });
520
- io.stderr.write('KTX context build did not pass agent-readiness verification.\n');
529
+ io.stderr.write('ktx context build did not pass agent-readiness verification.\n');
521
530
  for (const detail of readiness.details) {
522
531
  io.stderr.write(` ${detail}\n`);
523
532
  }
@@ -607,7 +616,7 @@ export async function runKtxSetupContextStep(args, io, deps = {}) {
607
616
  // error text.
608
617
  const testConnection = deps.testConnection ?? defaultGateTestConnection;
609
618
  while (true) {
610
- const gate = await testRequiredConnections(args.projectDir, project, targets, testConnection);
619
+ const gate = await testRequiredConnections(args.projectDir, project, targets, testConnection, io);
611
620
  if (gate.ok) {
612
621
  return await runBuild(args, io, deps, project, targets);
613
622
  }
@@ -119,7 +119,7 @@ function driverLabel(driver) {
119
119
  return DRIVER_LABELS[driver];
120
120
  }
121
121
  function connectionNamePrompt(label) {
122
- return `Name this ${label} connection\nKTX will use this short name in commands and config. You can rename it now.`;
122
+ return `Name this ${label} connection\nktx will use this short name in commands and config. You can rename it now.`;
123
123
  }
124
124
  function missingConnectionDetailsPrompt(label, canReturnToDriverSelection) {
125
125
  const backDestination = canReturnToDriverSelection ? 'database selection' : 'the previous setup step';
@@ -163,12 +163,6 @@ function numberConfigField(connection, field) {
163
163
  const value = connection?.[field];
164
164
  return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
165
165
  }
166
- function historicSqlConfigRecord(connection) {
167
- const historicSql = connection?.historicSql;
168
- return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql)
169
- ? historicSql
170
- : null;
171
- }
172
166
  function contextRecord(connection) {
173
167
  const context = connection?.context;
174
168
  return context && typeof context === 'object' && !Array.isArray(context) ? context : {};
@@ -179,28 +173,15 @@ function queryHistoryConfigRecord(connection) {
179
173
  ? queryHistory
180
174
  : null;
181
175
  }
182
- function stripLegacyHistoricSql(connection) {
183
- const { historicSql: _historicSql, ...rest } = connection;
184
- return rest;
185
- }
186
176
  function withQueryHistoryConfig(connection, queryHistory) {
187
177
  return {
188
- ...stripLegacyHistoricSql(connection),
178
+ ...connection,
189
179
  context: {
190
180
  ...contextRecord(connection),
191
181
  queryHistory,
192
182
  },
193
183
  };
194
184
  }
195
- function migrateLegacyHistoricSqlConnection(connection) {
196
- const existingQueryHistory = queryHistoryConfigRecord(connection);
197
- const legacy = historicSqlConfigRecord(connection);
198
- if (existingQueryHistory || !legacy) {
199
- return existingQueryHistory ? stripLegacyHistoricSql(connection) : connection;
200
- }
201
- const { dialect: _dialect, ...queryHistory } = legacy;
202
- return withQueryHistoryConfig(connection, queryHistory);
203
- }
204
185
  function setupHistoricSqlProbeResult(outcome) {
205
186
  if (!outcome) {
206
187
  return { ok: true, lines: [] };
@@ -871,7 +852,7 @@ async function disableConnectionQueryHistory(projectDir, connectionId) {
871
852
  if (!connection) {
872
853
  return;
873
854
  }
874
- const existing = queryHistoryConfigRecord(connection) ?? historicSqlConfigRecord(connection) ?? {};
855
+ const existing = queryHistoryConfigRecord(connection) ?? {};
875
856
  await writeConnectionConfig({
876
857
  projectDir,
877
858
  connectionId,
@@ -1004,32 +985,29 @@ async function maybeConfigureDatabaseScope(input) {
1004
985
  }
1005
986
  const cliSchemas = input.args.databaseSchemas;
1006
987
  if (input.args.inputMode === 'disabled') {
1007
- if (spec) {
1008
- let scopeToWrite = cliSchemas;
1009
- if (scopeToWrite.length === 0) {
1010
- try {
1011
- scopeToWrite = unique(await (input.deps.listSchemas ?? defaultListSchemas)(input.projectDir, input.connectionId));
1012
- }
1013
- catch (error) {
1014
- const detail = error instanceof Error ? error.message : String(error);
1015
- input.io.stderr.write(`Could not discover ${spec.promptLabel.toLowerCase()} for ${input.connectionId}; ${detail}\n`);
1016
- return okValidateResult();
1017
- }
1018
- }
1019
- if (scopeToWrite.length > 0) {
1020
- await writeScopeConfig({
1021
- projectDir: input.projectDir,
1022
- connectionId: input.connectionId,
1023
- values: scopeToWrite,
1024
- spec,
1025
- });
1026
- const capitalNounPlural = spec.nounPlural[0].toUpperCase() + spec.nounPlural.slice(1);
1027
- writeSetupSection(input.io, `${capitalNounPlural} saved for ${input.connectionId}`, [
1028
- `✓ ${scopeToWrite.join(', ')}`,
1029
- ]);
1030
- }
988
+ if (!spec) {
989
+ return okValidateResult();
1031
990
  }
1032
- return okValidateResult();
991
+ if (cliSchemas.length > 0) {
992
+ await writeScopeConfig({
993
+ projectDir: input.projectDir,
994
+ connectionId: input.connectionId,
995
+ values: cliSchemas,
996
+ spec,
997
+ });
998
+ const capitalNounPlural = spec.nounPlural[0].toUpperCase() + spec.nounPlural.slice(1);
999
+ writeSetupSection(input.io, `${capitalNounPlural} saved for ${input.connectionId}`, [
1000
+ `✓ ${cliSchemas.join(', ')}`,
1001
+ ]);
1002
+ return okValidateResult();
1003
+ }
1004
+ if (existingScope.length > 0) {
1005
+ return okValidateResult();
1006
+ }
1007
+ writePrefixedLines((chunk) => input.io.stderr.write(chunk), `No ${spec.nounPlural} configured for ${input.connectionId}. ` +
1008
+ `Pass --database-schema <${spec.noun}> (repeatable) or set ` +
1009
+ `connections.${input.connectionId}.${spec.configArrayField} in ktx.yaml.`);
1010
+ return failedValidateResult();
1033
1011
  }
1034
1012
  if (spec && cliSchemas.length > 0) {
1035
1013
  await writeScopeConfig({
@@ -1158,20 +1136,14 @@ async function ensureHistoricSqlIngestDefaults(projectDir) {
1158
1136
  }
1159
1137
  async function markDatabasesComplete(projectDir, connectionIds) {
1160
1138
  const project = await loadKtxProject({ projectDir });
1161
- const config = setKtxSetupDatabaseConnectionIds({
1162
- ...project.config,
1163
- connections: Object.fromEntries(Object.entries(project.config.connections).map(([connectionId, connection]) => [
1164
- connectionId,
1165
- migrateLegacyHistoricSqlConnection(connection),
1166
- ])),
1167
- }, unique(connectionIds));
1139
+ const config = setKtxSetupDatabaseConnectionIds(project.config, unique(connectionIds));
1168
1140
  await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
1169
1141
  await markKtxSetupStateStepComplete(projectDir, 'databases');
1170
1142
  }
1171
1143
  async function maybeRunHistoricSqlSetupProbe(input) {
1172
1144
  const project = await loadKtxProject({ projectDir: input.projectDir });
1173
1145
  const connection = project.config.connections[input.connectionId];
1174
- const queryHistory = queryHistoryConfigRecord(connection) ?? historicSqlConfigRecord(connection);
1146
+ const queryHistory = queryHistoryConfigRecord(connection);
1175
1147
  if (queryHistory?.enabled !== true) {
1176
1148
  return true;
1177
1149
  }
@@ -1494,13 +1466,13 @@ async function chooseDrivers(args, io, prompts, options) {
1494
1466
  return [];
1495
1467
  }
1496
1468
  if (args.inputMode === 'disabled') {
1497
- io.stderr.write('KTX cannot work without a database. Pass --database or --database-connection-id, or pass --skip-databases to leave setup incomplete.\n');
1469
+ io.stderr.write('ktx cannot work without a database. Pass --database or --database-connection-id, or pass --skip-databases to leave setup incomplete.\n');
1498
1470
  return 'missing-input';
1499
1471
  }
1500
1472
  const initialValues = unique(options?.initialDrivers ?? []);
1501
1473
  createKtxSetupUiAdapter().note(`Get demo credentials: ${KTX_DEMO_START_URL}`, '🎁 Need a warehouse to play with?', io);
1502
1474
  const choices = await prompts.multiselect({
1503
- message: withMultiselectNavigation('Which databases should KTX connect to?'),
1475
+ message: withMultiselectNavigation('Which databases should ktx connect to?'),
1504
1476
  options: [...DRIVER_OPTIONS],
1505
1477
  ...(initialValues.length > 0 ? { initialValues } : {}),
1506
1478
  required: true,
@@ -1722,7 +1694,7 @@ async function runPrimarySourceFullEdit(input) {
1722
1694
  }
1723
1695
  export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1724
1696
  if (args.skipDatabases) {
1725
- io.stdout.write('│ Database setup skipped. KTX cannot work until you add a database.\n');
1697
+ io.stdout.write('│ Database setup skipped. ktx cannot work until you add a database.\n');
1726
1698
  return { status: 'skipped', projectDir: args.projectDir };
1727
1699
  }
1728
1700
  const prompts = deps.prompts ?? createPromptAdapter();
@@ -1836,7 +1808,7 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1836
1808
  return { status: 'missing-input', projectDir: args.projectDir };
1837
1809
  if (drivers.length === 0) {
1838
1810
  await markDatabasesComplete(args.projectDir, []);
1839
- io.stdout.write('│ KTX cannot work without a database.\n');
1811
+ io.stdout.write('│ ktx cannot work without a database.\n');
1840
1812
  return { status: 'skipped', projectDir: args.projectDir };
1841
1813
  }
1842
1814
  let returnToDriverSelection = false;
@@ -1,3 +1,4 @@
1
+ import { createStaticCliSpinner, runWithCliSpinner } from './clack.js';
1
2
  import { createRepainter, renderContextBuildView } from './context-build-view.js';
2
3
  import { defaultDemoProjectDir, ensureSeededDemoProject } from './demo-assets.js';
3
4
  import { runKtxSetupAgentsStep } from './setup-agents.js';
@@ -48,7 +49,7 @@ function createTargetState(target) {
48
49
  export function renderDemoBanner(projectDir) {
49
50
  const lines = [
50
51
  '',
51
- `┌ ${cyan('Demo mode')} — data has been pre-processed and KTX context is already built.`,
52
+ `┌ ${cyan('Demo mode')} — data has been pre-processed and ktx context is already built.`,
52
53
  '│ This walkthrough illustrates the setup steps. Selections are pre-filled and read-only.',
53
54
  ];
54
55
  if (projectDir) {
@@ -73,7 +74,7 @@ export function renderDemoAgentTransition() {
73
74
  const lines = [
74
75
  '┌ Demo project is ready — let\'s connect your agent',
75
76
  '│',
76
- '│ Your KTX context has been built with demo data.',
77
+ '│ Your ktx context has been built with demo data.',
77
78
  '│ Select an agent to start using it.',
78
79
  '└',
79
80
  ];
@@ -83,17 +84,17 @@ export function renderDemoAgentTransition() {
83
84
  export function renderDemoCompletionSummary(projectDir, agentInstalled) {
84
85
  const lines = [
85
86
  '',
86
- `${cyan('★')} KTX demo is ready`,
87
+ `${cyan('★')} ktx demo is ready`,
87
88
  '',
88
89
  ];
89
90
  if (agentInstalled) {
90
- lines.push(' Your agent is connected to a demo KTX project.');
91
+ lines.push(' Your agent is connected to a demo ktx project.');
91
92
  }
92
93
  else {
93
94
  lines.push(' Demo project created. Connect an agent to start using it:');
94
95
  lines.push(` $ ${cyan(`ktx setup --agents --project-dir ${projectDir}`)}`);
95
96
  }
96
- lines.push('', ` ${dim('⚠')} This project is in a temporary directory and will be`, ' cleaned up by your system. To set up KTX with your own', ' data, run: ktx setup', '', ` Project: ${projectDir}`);
97
+ lines.push('', ` ${dim('⚠')} This project is in a temporary directory and will be`, ' cleaned up by your system. To set up ktx with your own', ' data, run: ktx setup', '', ` Project: ${projectDir}`);
97
98
  return lines.join('\n');
98
99
  }
99
100
  // ---------------------------------------------------------------------------
@@ -171,9 +172,9 @@ export function buildDemoReplayTimeline() {
171
172
  function renderDemoContextCompletionSummary() {
172
173
  const lines = [
173
174
  '',
174
- `${cyan('★')} KTX finished building context`,
175
+ `${cyan('★')} ktx finished building context`,
175
176
  '',
176
- ' KTX created:',
177
+ ' ktx created:',
177
178
  ` ${cyan('📊')} 46 semantic layer definitions`,
178
179
  ` ${cyan('📝')} 28 wiki pages`,
179
180
  '',
@@ -249,7 +250,10 @@ export async function runDemoTour(args, io, deps = {}) {
249
250
  const waitNav = deps.waitForNavigation ?? waitForDemoNavigation;
250
251
  const ensureProject = deps.ensureProject ?? ensureSeededDemoProject;
251
252
  const projectDir = defaultDemoProjectDir();
252
- await ensureProject({ projectDir, force: false, io, cliVersion: args.cliVersion });
253
+ // Static (stderr-only) spinner: the demo navigation below reads stdin in raw mode,
254
+ // and an animated clack spinner would leave stdin dirty so the first keypress wait
255
+ // sees a stray key and skips the intro.
256
+ await runWithCliSpinner(createStaticCliSpinner(io), { start: 'Preparing demo project…', success: 'Demo project ready', failure: 'Could not prepare demo project' }, () => ensureProject({ projectDir, force: false, io, cliVersion: args.cliVersion }));
253
257
  io.stdout.write(renderDemoBanner(projectDir) + '\n');
254
258
  io.stdout.write(`\n│ ${dim('Press Enter to continue, Escape to go back')}\n└\n`);
255
259
  const introDirection = await waitNav();
@@ -4,7 +4,7 @@ import { serializeKtxProjectConfig } from './context/project/config.js';
4
4
  import { loadKtxProject } from './context/project/project.js';
5
5
  import { markKtxSetupStateStepComplete, readKtxSetupState } from './context/project/setup-config.js';
6
6
  import { runKtxEmbeddingHealthCheck } from './llm/embedding-health.js';
7
- import { createStaticCliSpinner, errorMessage, writePrefixedLines } from './clack.js';
7
+ import { createCliSpinner, errorMessage, writePrefixedLines } from './clack.js';
8
8
  import { ensureManagedLocalEmbeddingsDaemon, managedLocalEmbeddingHealthConfig, } from './managed-local-embeddings.js';
9
9
  import { ManagedPythonDaemonStartError } from './managed-python-daemon.js';
10
10
  import { withTextInputNavigation } from './prompt-navigation.js';
@@ -20,7 +20,7 @@ const DEFAULTS = {
20
20
  },
21
21
  };
22
22
  const LOCAL_EMBEDDING_BACKEND = 'sentence-transformers';
23
- const EMBEDDING_OPTION_PROMPT_CONTEXT = 'KTX uses embeddings for semantic search over semantic-layer sources, wiki context, schema metadata, ' +
23
+ const EMBEDDING_OPTION_PROMPT_CONTEXT = 'ktx uses embeddings for semantic search over semantic-layer sources, wiki context, schema metadata, ' +
24
24
  'and relationship evidence.';
25
25
  const LOCAL_EMBEDDING_HEALTH_TIMEOUT_MS = 120_000;
26
26
  const LOCAL_EMBEDDING_STDERR_TAIL_LINES = 40;
@@ -136,7 +136,7 @@ async function chooseCredentialRef(backend, args, io, deps) {
136
136
  const defaultEnv = DEFAULTS[backend].envName ?? 'EMBEDDING_API_KEY';
137
137
  const prompts = deps.prompts ?? createPromptAdapter();
138
138
  const choice = await prompts.select({
139
- message: `How should KTX find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
139
+ message: `How should ktx find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
140
140
  options: [
141
141
  { value: 'paste', label: 'Paste a key and save it as a local secret file' },
142
142
  { value: 'env', label: `Use ${defaultEnv} from the environment` },
@@ -148,7 +148,7 @@ async function chooseCredentialRef(backend, args, io, deps) {
148
148
  }
149
149
  if (choice === 'paste') {
150
150
  io.stdout.write(`│ ${[
151
- `KTX will save the key in .ktx/secrets/${backend}-api-key with local file permissions,`,
151
+ `ktx will save the key in .ktx/secrets/${backend}-api-key with local file permissions,`,
152
152
  'then write a file: reference in ktx.yaml.',
153
153
  ].join(' ')}\n`);
154
154
  const value = await prompts.password({ message: withTextInputNavigation(`${backend} embedding API key`) });
@@ -181,7 +181,7 @@ async function chooseEmbeddingBackend(args, deps) {
181
181
  return LOCAL_EMBEDDING_BACKEND;
182
182
  }
183
183
  const choice = await (deps.prompts ?? createPromptAdapter()).select({
184
- message: `Which embedding option should KTX use?\n\n${EMBEDDING_OPTION_PROMPT_CONTEXT}`,
184
+ message: `Which embedding option should ktx use?\n\n${EMBEDDING_OPTION_PROMPT_CONTEXT}`,
185
185
  options: [
186
186
  { value: 'sentence-transformers', label: 'Local sentence-transformers embeddings' },
187
187
  { value: 'openai', label: 'OpenAI embeddings', hint: 'recommended' },
@@ -211,19 +211,19 @@ async function readLocalEmbeddingDaemonStderrTail(stderrLog) {
211
211
  function localEmbeddingSetupMessage(message, stderrTail = []) {
212
212
  const lines = [
213
213
  `Local embedding health check failed: ${message}`,
214
- 'Local embeddings use the KTX-managed Python runtime.',
214
+ 'Local embeddings use the ktx-managed Python runtime.',
215
215
  'Prepare the runtime with: ktx admin runtime start --feature local-embeddings',
216
216
  'Use --yes with setup to install and start the runtime without prompting.',
217
217
  'The first run may download Python packages and the all-MiniLM-L6-v2 model.',
218
218
  ];
219
219
  if (stderrTail.length > 0) {
220
- lines.push('Recent KTX daemon stderr:', ...stderrTail);
220
+ lines.push('Recent ktx daemon stderr:', ...stderrTail);
221
221
  }
222
222
  return lines.join('\n');
223
223
  }
224
224
  async function promptAfterLocalEmbeddingFailure(deps) {
225
225
  const choice = await (deps.prompts ?? createPromptAdapter()).select({
226
- message: 'Local embeddings are not reachable. Start the local KTX daemon, then retry.',
226
+ message: 'Local embeddings are not reachable. Start the local ktx daemon, then retry.',
227
227
  options: [
228
228
  { value: 'retry', label: 'Retry' },
229
229
  { value: 'openai', label: 'Use OpenAI embeddings' },
@@ -329,7 +329,7 @@ export async function runKtxSetupEmbeddingsStep(args, io, deps = {}) {
329
329
  dimensions,
330
330
  credentialValue,
331
331
  });
332
- const healthSpinner = (deps.spinner ?? (() => createStaticCliSpinner(io)))();
332
+ const healthSpinner = (deps.spinner ?? (() => createCliSpinner(io)))();
333
333
  const progress = startHealthCheckProgress(healthSpinner, healthCheckStartText(selectedBackend, model, dimensions));
334
334
  let health;
335
335
  try {