@kaelio/ktx 0.8.0 → 0.10.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 (183) hide show
  1. package/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/clack.d.ts +6 -0
  5. package/dist/clack.js +17 -2
  6. package/dist/cli-program.d.ts +3 -0
  7. package/dist/cli-program.js +42 -2
  8. package/dist/cli-runtime.d.ts +3 -0
  9. package/dist/cli-runtime.js +94 -3
  10. package/dist/commands/setup-commands.js +3 -4
  11. package/dist/connection-recovery.d.ts +34 -0
  12. package/dist/connection-recovery.js +82 -0
  13. package/dist/connection.js +26 -2
  14. package/dist/connectors/bigquery/connector.d.ts +2 -5
  15. package/dist/connectors/bigquery/connector.js +2 -2
  16. package/dist/connectors/clickhouse/connector.d.ts +2 -5
  17. package/dist/connectors/clickhouse/connector.js +2 -2
  18. package/dist/connectors/mysql/connector.d.ts +7 -6
  19. package/dist/connectors/mysql/connector.js +25 -5
  20. package/dist/connectors/mysql/dialect.d.ts +1 -1
  21. package/dist/connectors/mysql/dialect.js +12 -2
  22. package/dist/connectors/postgres/connector.d.ts +2 -5
  23. package/dist/connectors/postgres/connector.js +2 -2
  24. package/dist/connectors/snowflake/connector.d.ts +2 -5
  25. package/dist/connectors/snowflake/connector.js +2 -2
  26. package/dist/connectors/sqlite/connector.d.ts +2 -5
  27. package/dist/connectors/sqlite/connector.js +2 -2
  28. package/dist/connectors/sqlserver/connector.d.ts +2 -5
  29. package/dist/connectors/sqlserver/connector.js +2 -2
  30. package/dist/context/connections/drivers.d.ts +0 -1
  31. package/dist/context/connections/drivers.js +0 -7
  32. package/dist/context/connections/query-executor.d.ts +2 -1
  33. package/dist/context/core/abort.d.ts +9 -0
  34. package/dist/context/core/abort.js +36 -0
  35. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
  36. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
  37. package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
  38. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
  39. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
  40. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
  41. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
  42. package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
  43. package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
  44. package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
  45. package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
  46. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
  47. package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
  48. package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
  49. package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
  50. package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
  51. package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
  52. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  53. package/dist/context/ingest/final-gate-repair.d.ts +1 -0
  54. package/dist/context/ingest/final-gate-repair.js +1 -0
  55. package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
  56. package/dist/context/ingest/ingest-bundle.runner.js +127 -53
  57. package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
  58. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
  59. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
  60. package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
  61. package/dist/context/ingest/local-adapters.js +21 -4
  62. package/dist/context/ingest/local-bundle-runtime.js +13 -5
  63. package/dist/context/ingest/local-ingest.d.ts +1 -0
  64. package/dist/context/ingest/local-ingest.js +13 -3
  65. package/dist/context/ingest/memory-flow/events.js +1 -1
  66. package/dist/context/ingest/memory-flow/schema.js +8 -3
  67. package/dist/context/ingest/memory-flow/types.d.ts +7 -3
  68. package/dist/context/ingest/ports.d.ts +3 -5
  69. package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
  70. package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
  71. package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
  72. package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
  73. package/dist/context/ingest/types.d.ts +1 -0
  74. package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
  75. package/dist/context/llm/ai-sdk-runtime.js +152 -16
  76. package/dist/context/llm/claude-code-runtime.d.ts +6 -4
  77. package/dist/context/llm/claude-code-runtime.js +127 -48
  78. package/dist/context/llm/codex-exec-events.d.ts +20 -0
  79. package/dist/context/llm/codex-exec-events.js +155 -0
  80. package/dist/context/llm/codex-isolation.d.ts +3 -0
  81. package/dist/context/llm/codex-isolation.js +5 -0
  82. package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
  83. package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
  84. package/dist/context/llm/codex-models.d.ts +2 -0
  85. package/dist/context/llm/codex-models.js +17 -0
  86. package/dist/context/llm/codex-runtime-config.d.ts +16 -0
  87. package/dist/context/llm/codex-runtime-config.js +19 -0
  88. package/dist/context/llm/codex-runtime.d.ts +37 -0
  89. package/dist/context/llm/codex-runtime.js +347 -0
  90. package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
  91. package/dist/context/llm/codex-sdk-runner.js +63 -0
  92. package/dist/context/llm/local-config.d.ts +16 -4
  93. package/dist/context/llm/local-config.js +18 -2
  94. package/dist/context/llm/rate-limit-governor.d.ts +103 -0
  95. package/dist/context/llm/rate-limit-governor.js +285 -0
  96. package/dist/context/llm/runtime-port.d.ts +3 -6
  97. package/dist/context/mcp/context-tools.js +43 -13
  98. package/dist/context/project/config.d.ts +14 -0
  99. package/dist/context/project/config.js +37 -2
  100. package/dist/context/scan/types.d.ts +15 -2
  101. package/dist/context/scan/types.js +12 -0
  102. package/dist/context/sl/description-normalization.js +4 -14
  103. package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
  104. package/dist/context/sql-analysis/ports.d.ts +12 -2
  105. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  106. package/dist/context-build-view.d.ts +13 -0
  107. package/dist/context-build-view.js +63 -32
  108. package/dist/demo-metrics.d.ts +0 -2
  109. package/dist/demo-metrics.js +1 -11
  110. package/dist/ingest.d.ts +1 -0
  111. package/dist/ingest.js +32 -3
  112. package/dist/io/buffered-command-io.d.ts +11 -0
  113. package/dist/io/buffered-command-io.js +28 -0
  114. package/dist/io/symbols.d.ts +2 -0
  115. package/dist/io/symbols.js +2 -0
  116. package/dist/llm/types.d.ts +1 -1
  117. package/dist/local-adapters.d.ts +10 -2
  118. package/dist/local-adapters.js +19 -3
  119. package/dist/memory-flow-hud.js +8 -16
  120. package/dist/next-steps.js +1 -2
  121. package/dist/progress-port-adapter.d.ts +6 -0
  122. package/dist/progress-port-adapter.js +18 -0
  123. package/dist/public-ingest.d.ts +20 -1
  124. package/dist/public-ingest.js +228 -42
  125. package/dist/reveal-password-prompt.d.ts +24 -0
  126. package/dist/reveal-password-prompt.js +78 -0
  127. package/dist/scan.js +21 -3
  128. package/dist/setup-context.d.ts +2 -0
  129. package/dist/setup-context.js +133 -27
  130. package/dist/setup-databases.d.ts +18 -1
  131. package/dist/setup-databases.js +378 -249
  132. package/dist/setup-demo-tour.js +1 -0
  133. package/dist/setup-embeddings.js +1 -1
  134. package/dist/setup-models.d.ts +11 -15
  135. package/dist/setup-models.js +140 -276
  136. package/dist/setup-prompts.js +3 -2
  137. package/dist/setup-ready-menu.d.ts +16 -2
  138. package/dist/setup-ready-menu.js +37 -5
  139. package/dist/setup-sources.js +115 -35
  140. package/dist/setup.d.ts +1 -1
  141. package/dist/setup.js +23 -11
  142. package/dist/sl.d.ts +2 -2
  143. package/dist/sl.js +20 -4
  144. package/dist/sql.js +18 -2
  145. package/dist/star-prompt/cache.d.ts +16 -0
  146. package/dist/star-prompt/cache.js +45 -0
  147. package/dist/star-prompt/star-count.d.ts +7 -0
  148. package/dist/star-prompt/star-count.js +66 -0
  149. package/dist/star-prompt/star-line.d.ts +12 -0
  150. package/dist/star-prompt/star-line.js +26 -0
  151. package/dist/status-project.d.ts +11 -0
  152. package/dist/status-project.js +50 -1
  153. package/dist/telemetry/command-hook.d.ts +1 -0
  154. package/dist/telemetry/command-hook.js +3 -1
  155. package/dist/telemetry/emitter.d.ts +10 -0
  156. package/dist/telemetry/emitter.js +31 -0
  157. package/dist/telemetry/events.d.ts +35 -6
  158. package/dist/telemetry/events.js +25 -2
  159. package/dist/telemetry/exception.d.ts +18 -0
  160. package/dist/telemetry/exception.js +162 -0
  161. package/dist/telemetry/identity.d.ts +0 -1
  162. package/dist/telemetry/identity.js +6 -6
  163. package/dist/telemetry/index.d.ts +15 -2
  164. package/dist/telemetry/index.js +15 -3
  165. package/dist/telemetry/redaction-secrets.d.ts +11 -0
  166. package/dist/telemetry/redaction-secrets.js +92 -0
  167. package/dist/telemetry/scrubber.d.ts +10 -0
  168. package/dist/telemetry/scrubber.js +20 -0
  169. package/dist/update-check/cache.d.ts +21 -0
  170. package/dist/update-check/cache.js +38 -0
  171. package/dist/update-check/channel.d.ts +15 -0
  172. package/dist/update-check/channel.js +30 -0
  173. package/dist/update-check/registry.d.ts +1 -0
  174. package/dist/update-check/registry.js +45 -0
  175. package/dist/update-check/update-check.d.ts +43 -0
  176. package/dist/update-check/update-check.js +116 -0
  177. package/package.json +12 -4
  178. package/dist/context/connections/local-query-executor.d.ts +0 -6
  179. package/dist/context/connections/local-query-executor.js +0 -39
  180. package/dist/context/connections/postgres-query-executor.d.ts +0 -25
  181. package/dist/context/connections/postgres-query-executor.js +0 -53
  182. package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
  183. package/dist/context/connections/sqlite-query-executor.js +0 -74
@@ -59,6 +59,13 @@ function optionalString(raw, field) {
59
59
  }
60
60
  throw new Error(`sql analysis response has invalid optional string field ${field}`);
61
61
  }
62
+ function optionalNullableStringField(raw, field) {
63
+ const value = raw[field];
64
+ if (value === null || value === undefined || typeof value === 'string') {
65
+ return value ?? null;
66
+ }
67
+ throw new Error(`sql analysis response has invalid optional nullable string field ${field}`);
68
+ }
62
69
  function requiredStringArray(raw, field) {
63
70
  const value = raw[field];
64
71
  if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
@@ -136,10 +143,32 @@ function mapColumnsByClause(raw) {
136
143
  }
137
144
  return result;
138
145
  }
146
+ function requiredTableRef(raw, field) {
147
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
148
+ throw new Error(`sql analysis response contains invalid table ref in ${field}`);
149
+ }
150
+ const record = raw;
151
+ const name = record.name;
152
+ if (typeof name !== 'string' || name.length === 0) {
153
+ throw new Error(`sql analysis response table ref in ${field} is missing name`);
154
+ }
155
+ return {
156
+ catalog: optionalNullableStringField(record, 'catalog'),
157
+ db: optionalNullableStringField(record, 'db'),
158
+ name,
159
+ };
160
+ }
161
+ function requiredTableRefArray(raw, field) {
162
+ const value = raw[field];
163
+ if (!Array.isArray(value)) {
164
+ throw new Error(`sql analysis response is missing table-ref[] field ${field}`);
165
+ }
166
+ return value.map((item, index) => requiredTableRef(item, `${field}.${index}`));
167
+ }
139
168
  function mapBatchResult(raw) {
140
169
  const error = optionalString(raw, 'error');
141
170
  return {
142
- tablesTouched: requiredStringArray(raw, 'tables_touched'),
171
+ tablesTouched: requiredTableRefArray(raw, 'tables_touched'),
143
172
  columnsByClause: mapColumnsByClause(raw),
144
173
  ...(error !== undefined ? { error } : {}),
145
174
  };
@@ -170,10 +199,11 @@ export function createHttpSqlAnalysisPort(options) {
170
199
  });
171
200
  return mapResult(raw);
172
201
  },
173
- async analyzeBatch(items, dialect) {
202
+ async analyzeBatch(items, dialect, options) {
174
203
  const raw = await requestJson('/sql/analyze-batch', {
175
204
  dialect,
176
205
  items,
206
+ ...(options?.catalog ? { catalog: options.catalog } : {}),
177
207
  });
178
208
  return mapBatchResponse(raw);
179
209
  },
@@ -1,3 +1,4 @@
1
+ import type { KtxTableRef } from '../scan/types.js';
1
2
  export type SqlAnalysisDialect = 'bigquery' | 'snowflake' | 'postgres' | 'redshift' | 'mysql' | 'sqlite' | 'tsql' | 'clickhouse' | (string & {});
2
3
  export type SqlAnalysisLiteralSlotType = 'string' | 'number' | 'timestamp' | 'date' | 'boolean' | 'null' | 'unknown';
3
4
  export interface SqlAnalysisLiteralSlot {
@@ -17,8 +18,17 @@ export interface SqlAnalysisBatchItem {
17
18
  id: string;
18
19
  sql: string;
19
20
  }
21
+ interface SqlAnalysisCatalogTable extends KtxTableRef {
22
+ columns?: string[];
23
+ }
24
+ interface SqlAnalysisCatalog {
25
+ tables: SqlAnalysisCatalogTable[];
26
+ }
27
+ export interface SqlAnalysisBatchOptions {
28
+ catalog?: SqlAnalysisCatalog;
29
+ }
20
30
  export interface SqlAnalysisBatchResult {
21
- tablesTouched: string[];
31
+ tablesTouched: KtxTableRef[];
22
32
  columnsByClause: Partial<Record<SqlAnalysisClause, string[]>>;
23
33
  error?: string | null;
24
34
  }
@@ -28,7 +38,7 @@ export interface SqlReadOnlyValidationResult {
28
38
  }
29
39
  export interface SqlAnalysisPort {
30
40
  analyzeForFingerprint(sql: string, dialect: SqlAnalysisDialect): Promise<SqlAnalysisFingerprintResult>;
31
- analyzeBatch(items: SqlAnalysisBatchItem[], dialect: SqlAnalysisDialect): Promise<Map<string, SqlAnalysisBatchResult>>;
41
+ analyzeBatch(items: SqlAnalysisBatchItem[], dialect: SqlAnalysisDialect, options?: SqlAnalysisBatchOptions): Promise<Map<string, SqlAnalysisBatchResult>>;
32
42
  validateReadOnly(sql: string, dialect: SqlAnalysisDialect): Promise<SqlReadOnlyValidationResult>;
33
43
  }
34
44
  export {};
@@ -7,9 +7,9 @@ declare const contextCandidateMarkInputSchema: z.ZodObject<{
7
7
  status: z.ZodEnum<{
8
8
  conflict: "conflict";
9
9
  merged: "merged";
10
+ rejected: "rejected";
10
11
  pending: "pending";
11
12
  promoted: "promoted";
12
- rejected: "rejected";
13
13
  }>;
14
14
  rejectionReason: z.ZodDefault<z.ZodNullable<z.ZodString>>;
15
15
  }, z.core.$strip>;
@@ -30,9 +30,9 @@ export declare class ContextCandidateMarkTool extends BaseTool<typeof contextCan
30
30
  status: z.ZodEnum<{
31
31
  conflict: "conflict";
32
32
  merged: "merged";
33
+ rejected: "rejected";
33
34
  pending: "pending";
34
35
  promoted: "promoted";
35
- rejected: "rejected";
36
36
  }>;
37
37
  rejectionReason: z.ZodDefault<z.ZodNullable<z.ZodString>>;
38
38
  }, z.core.$strip>;
@@ -2,6 +2,7 @@ import type { KtxCliIo } from './index.js';
2
2
  import type { KtxManagedPythonInstallPolicy } from './managed-python-command.js';
3
3
  import type { KtxPublicIngestArgs, KtxPublicIngestPlanTarget, KtxPublicIngestProject } from './public-ingest.js';
4
4
  import { executePublicIngestTarget } from './public-ingest.js';
5
+ import { fetchGitHubStarCount as defaultFetchGitHubStarCount } from './star-prompt/star-count.js';
5
6
  type PhaseKey = 'database-schema' | 'query-history' | 'source-ingest';
6
7
  type PhaseStatus = 'queued' | 'running' | 'done' | 'failed' | 'skipped';
7
8
  interface PhaseState {
@@ -32,6 +33,7 @@ export interface ContextBuildViewState {
32
33
  frame: number;
33
34
  startedAt: number | null;
34
35
  totalElapsedMs: number;
36
+ starCount: number | null;
35
37
  }
36
38
  export interface ContextBuildArgs {
37
39
  projectDir: string;
@@ -73,6 +75,8 @@ interface CompletedItemName {
73
75
  interface ContextBuildRenderOptions {
74
76
  styled?: boolean;
75
77
  showHint?: boolean;
78
+ showStarPrompt?: boolean;
79
+ columns?: number;
76
80
  hintText?: string;
77
81
  projectDir?: string;
78
82
  title?: string;
@@ -89,6 +93,14 @@ export interface ContextBuildDeps {
89
93
  now?: () => number;
90
94
  onSourceProgress?: (sources: ContextBuildSourceProgressUpdate[]) => void;
91
95
  sourceProgressThrottleMs?: number;
96
+ fetchStarCount?: typeof defaultFetchGitHubStarCount;
97
+ starPromptEnv?: StarPromptEnv;
98
+ starPromptHomeDir?: string;
99
+ }
100
+ interface StarPromptEnv extends NodeJS.ProcessEnv {
101
+ CI?: string;
102
+ DO_NOT_TRACK?: string;
103
+ KTX_NO_STAR?: string;
92
104
  }
93
105
  export declare function renderContextBuildView(state: ContextBuildViewState, options?: ContextBuildRenderOptions): string;
94
106
  /** @internal */
@@ -101,6 +113,7 @@ export declare function parseIngestSummary(output: string): string | null;
101
113
  export declare function viewStateFromSourceProgress(sources: ContextBuildSourceProgressUpdate[], now: number, startedAtMs?: number): ContextBuildViewState;
102
114
  export declare function createRepainter(io: KtxCliIo): {
103
115
  paint(content: string): void;
116
+ columns(): number;
104
117
  };
105
118
  export declare function initViewState(targets: KtxPublicIngestPlanTarget[]): ContextBuildViewState;
106
119
  export declare function runContextBuild(project: KtxPublicIngestProject, args: ContextBuildArgs, io: KtxCliIo, deps?: ContextBuildDeps): Promise<ContextBuildResult>;
@@ -1,7 +1,10 @@
1
- import { publicDatabaseIngestMessage, publicQueryHistoryMessage } from './public-ingest-copy.js';
2
- import { buildPublicIngestPlan, executePublicIngestTarget } from './public-ingest.js';
1
+ import { buildPublicIngestPlan, executePublicIngestTarget, publicProgressMessage } from './public-ingest.js';
2
+ import { createAggregateProgressPort } from './progress-port-adapter.js';
3
3
  import { formatDuration } from './demo-metrics.js';
4
4
  import { profileMark } from './startup-profile.js';
5
+ import { isFreshStarCountCache, readStarCountCache, writeStarCountCache, } from './star-prompt/cache.js';
6
+ import { fetchGitHubStarCount as defaultFetchGitHubStarCount } from './star-prompt/star-count.js';
7
+ import { renderStarPromptLine } from './star-prompt/star-line.js';
5
8
  profileMark('module:context-build-view');
6
9
  const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
7
10
  const ESC = String.fromCharCode(0x1b);
@@ -286,6 +289,13 @@ export function renderContextBuildView(state, options = {}) {
286
289
  lines.push(styled ? green(summary) : summary);
287
290
  lines.push('');
288
291
  }
292
+ if (options.showStarPrompt && hasActive) {
293
+ const starPrompt = renderStarPromptLine({
294
+ count: state.starCount,
295
+ columns: options.columns ?? 80,
296
+ });
297
+ lines.push(styled ? dim(starPrompt) : starPrompt);
298
+ }
289
299
  if (options.showHint && hasActive) {
290
300
  const hintContent = options.hintText ?? 'Ctrl+C to stop';
291
301
  const hint = ` ${hintContent}`;
@@ -423,6 +433,7 @@ export function viewStateFromSourceProgress(sources, now, startedAtMs) {
423
433
  frame: 0,
424
434
  startedAt: startedAtMs ?? null,
425
435
  totalElapsedMs: startedAtMs ? now - startedAtMs : 0,
436
+ starCount: null,
426
437
  };
427
438
  }
428
439
  // --- Repaint ---
@@ -465,6 +476,9 @@ export function createRepainter(io) {
465
476
  hasPainted = true;
466
477
  lastCursorUpRows = cursorUpRowsAfterWrite(content);
467
478
  },
479
+ columns() {
480
+ return terminalColumns();
481
+ },
468
482
  };
469
483
  }
470
484
  // --- Orchestration ---
@@ -616,39 +630,41 @@ export function initViewState(targets) {
616
630
  frame: 0,
617
631
  startedAt: null,
618
632
  totalElapsedMs: 0,
633
+ starCount: null,
619
634
  };
620
635
  }
621
- function publicProgressMessage(message, target) {
622
- let current = message;
623
- if (target.operation === 'database-ingest') {
624
- current = publicDatabaseIngestMessage(current);
625
- }
626
- if (target.steps.includes('query-history')) {
627
- current = publicQueryHistoryMessage(current, target.connectionId);
628
- }
629
- return current;
630
- }
631
636
  function formatProgressDetail(update, target) {
632
637
  const percent = Math.max(0, Math.min(100, Math.round(update.percent)));
633
638
  return `[${percent}%] ${publicProgressMessage(update.message, target)}`;
634
639
  }
635
- function createContextBuildProgressPort(onProgress, state = { progress: 0 }, start = 0, weight = 1) {
636
- return {
637
- async update(value, message, options) {
638
- const absoluteValue = start + Math.max(0, Math.min(1, value)) * weight;
639
- state.progress = Math.max(state.progress, Math.min(1, absoluteValue));
640
- if (!message)
641
- return;
642
- onProgress({
643
- percent: Math.max(0, Math.min(100, Math.round(state.progress * 100))),
644
- message,
645
- ...(options?.transient !== undefined ? { transient: options.transient } : {}),
646
- });
647
- },
648
- startPhase(phaseWeight) {
649
- return createContextBuildProgressPort(onProgress, state, state.progress, weight * phaseWeight);
650
- },
651
- };
640
+ const STAR_COUNT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
641
+ function envFlag(value) {
642
+ return value !== undefined && value !== '' && value !== '0' && value !== 'false';
643
+ }
644
+ function shouldSuppressStarPrompt(env) {
645
+ return envFlag(env.CI) || envFlag(env.DO_NOT_TRACK) || envFlag(env.KTX_NO_STAR);
646
+ }
647
+ function startStarPromptCountRefresh(input) {
648
+ const cached = readStarCountCache({ homeDir: input.homeDir });
649
+ if (cached) {
650
+ input.state.starCount = cached.count;
651
+ }
652
+ if (isFreshStarCountCache(cached, new Date(input.now()), STAR_COUNT_CACHE_TTL_MS)) {
653
+ return;
654
+ }
655
+ void input.fetchStarCount()
656
+ .then((count) => {
657
+ if (typeof count !== 'number' || !Number.isFinite(count)) {
658
+ return;
659
+ }
660
+ input.state.starCount = count;
661
+ input.paint();
662
+ void writeStarCountCache({
663
+ count,
664
+ fetchedAt: new Date(input.now()).toISOString(),
665
+ }, { homeDir: input.homeDir });
666
+ })
667
+ .catch(() => undefined);
652
668
  }
653
669
  export async function runContextBuild(project, args, io, deps = {}) {
654
670
  const plan = buildPublicIngestPlan(project, {
@@ -664,13 +680,28 @@ export async function runContextBuild(project, args, io, deps = {}) {
664
680
  const nowFn = deps.now ?? (() => Date.now());
665
681
  state.startedAt = nowFn();
666
682
  const repainter = isTTY ? createRepainter(io) : null;
683
+ const starPromptEnabled = repainter !== null && !shouldSuppressStarPrompt(deps.starPromptEnv ?? process.env);
667
684
  const viewOpts = {
668
685
  styled: true,
669
686
  projectDir: args.projectDir,
670
687
  notices: plan.notices ?? [],
671
688
  warnings: plan.warnings,
672
689
  };
673
- const paint = (hint) => repainter?.paint(renderContextBuildView(state, { ...viewOpts, showHint: hint }));
690
+ const paint = (hint) => repainter?.paint(renderContextBuildView(state, {
691
+ ...viewOpts,
692
+ showHint: hint,
693
+ showStarPrompt: starPromptEnabled && hint,
694
+ columns: repainter.columns(),
695
+ }));
696
+ if (starPromptEnabled) {
697
+ startStarPromptCountRefresh({
698
+ fetchStarCount: deps.fetchStarCount ?? defaultFetchGitHubStarCount,
699
+ homeDir: deps.starPromptHomeDir,
700
+ now: nowFn,
701
+ paint: () => paint(true),
702
+ state,
703
+ });
704
+ }
674
705
  paint(true);
675
706
  let spinnerInterval = null;
676
707
  if (repainter) {
@@ -802,7 +833,7 @@ export async function runContextBuild(project, args, io, deps = {}) {
802
833
  hasPendingProgressPublish = !publishSourceProgress(false);
803
834
  };
804
835
  const progressDeps = {
805
- scanProgress: createContextBuildProgressPort(updateSchemaPhase),
836
+ scanProgress: createAggregateProgressPort(updateSchemaPhase),
806
837
  ingestProgress: updateIngestPhase,
807
838
  runtimeIo: io,
808
839
  onPhaseStart,
@@ -811,7 +842,7 @@ export async function runContextBuild(project, args, io, deps = {}) {
811
842
  let result = null;
812
843
  let thrownError = null;
813
844
  try {
814
- result = await execTarget(targetState.target, runArgs, capture.io, progressDeps);
845
+ result = await execTarget(targetState.target, runArgs, capture.io, progressDeps, project);
815
846
  }
816
847
  catch (error) {
817
848
  thrownError = error;
@@ -8,8 +8,6 @@ interface DemoMetricsTuning {
8
8
  interface DemoMetricsSnapshot {
9
9
  elapsedMs: number;
10
10
  etaMs: number | null;
11
- agentSteps: number;
12
- agentStepBudget: number;
13
11
  toolCalls: number;
14
12
  workUnitsStarted: number;
15
13
  workUnitsFinished: number;
@@ -5,13 +5,6 @@ const DEFAULT_OUTPUT_PRICE_PER_MTOK_USD = 15;
5
5
  function eventsOf(events, type) {
6
6
  return events.filter((event) => event.type === type);
7
7
  }
8
- function maxAgentStep(events) {
9
- const steps = eventsOf(events, 'work_unit_step');
10
- const started = eventsOf(events, 'work_unit_started');
11
- const stepIndex = steps.reduce((max, event) => Math.max(max, event.stepIndex), 0);
12
- const stepBudget = Math.max(0, ...steps.map((event) => event.stepBudget), ...started.map((event) => event.stepBudget));
13
- return { step: stepIndex, budget: stepBudget };
14
- }
15
8
  function totalToolCalls(input) {
16
9
  return input.details.transcripts.reduce((total, transcript) => total + transcript.toolCallCount, 0);
17
10
  }
@@ -49,11 +42,10 @@ export function buildDemoMetrics(input, options = {}) {
49
42
  const outputPrice = tuning.outputPricePerMTokUsd ?? DEFAULT_OUTPUT_PRICE_PER_MTOK_USD;
50
43
  const nowMs = (options.now ?? Date.now)();
51
44
  const elapsedMs = elapsedMsFromEvents(input.events, nowMs);
52
- const { step, budget } = maxAgentStep(input.events);
53
45
  const toolCalls = totalToolCalls(input);
54
46
  const progress = workUnitProgress(input);
55
47
  const finishedCount = eventsOf(input.events, 'work_unit_finished').length;
56
- const stepDriver = Math.max(step, toolCalls, finishedCount * 4);
48
+ const stepDriver = Math.max(toolCalls, finishedCount * 4);
57
49
  const inputTokens = stepDriver * inputTokensPerStep;
58
50
  const outputTokens = stepDriver * outputTokensPerStep;
59
51
  const totalTokens = inputTokens + outputTokens;
@@ -63,8 +55,6 @@ export function buildDemoMetrics(input, options = {}) {
63
55
  return {
64
56
  elapsedMs,
65
57
  etaMs: estimateEtaMs(elapsedMs, progress.finished, progress.total, input.status),
66
- agentSteps: step,
67
- agentStepBudget: budget,
68
58
  toolCalls,
69
59
  workUnitsStarted: progress.started,
70
60
  workUnitsFinished: progress.finished,
package/dist/ingest.d.ts CHANGED
@@ -58,6 +58,7 @@ export interface KtxIngestDeps {
58
58
  readReportFile?: typeof readIngestReportSnapshotFile;
59
59
  renderStoredMemoryFlow?: typeof renderMemoryFlowTui;
60
60
  startLiveMemoryFlow?: typeof startLiveMemoryFlowTui;
61
+ abortSignal?: AbortSignal;
61
62
  env?: NodeJS.ProcessEnv;
62
63
  localIngestOptions?: Pick<RunLocalIngestOptions, 'agentRunner' | 'llmRuntime' | 'memoryModel' | 'semanticLayerCompute' | 'queryExecutor' | 'logger' | 'pullConfigOptions'>;
63
64
  progress?: (update: KtxIngestProgressUpdate) => void;
package/dist/ingest.js CHANGED
@@ -17,6 +17,22 @@ import { renderMemoryFlowTui, startLiveMemoryFlowTui, } from './memory-flow-tui.
17
17
  import { resolveVizFallback, warnVizFallbackOnce } from './viz-fallback.js';
18
18
  import { profileMark } from './startup-profile.js';
19
19
  profileMark('module:ingest');
20
+ function createCliAbortSignal() {
21
+ const controller = new AbortController();
22
+ let interrupted = false;
23
+ const onSigint = () => {
24
+ if (interrupted) {
25
+ process.exit(130);
26
+ }
27
+ interrupted = true;
28
+ controller.abort(new DOMException('Aborted', 'AbortError'));
29
+ };
30
+ process.on('SIGINT', onSigint);
31
+ return {
32
+ signal: controller.signal,
33
+ dispose: () => process.off('SIGINT', onSigint),
34
+ };
35
+ }
20
36
  const REPORT_SOURCE_LABELS = new Map([
21
37
  ['live-database', 'Database schema'],
22
38
  ['historic-sql', 'Query history'],
@@ -249,6 +265,12 @@ function plainIngestEventProgress(event, snapshot, eventIndex) {
249
265
  message: event.message,
250
266
  ...(event.transient !== undefined ? { transient: event.transient } : {}),
251
267
  };
268
+ case 'rate_limit_wait':
269
+ return {
270
+ percent: 50,
271
+ message: `Rate-limited (${event.provider}${event.rateLimitType ? ` ${event.rateLimitType}` : ''}); resuming in ${Math.ceil(event.remainingMs / 1_000)}s`,
272
+ transient: true,
273
+ };
252
274
  case 'work_unit_started': {
253
275
  const total = plannedWorkUnitCountThrough(snapshot, eventIndex);
254
276
  const ordinal = workUnitOrdinalThrough(snapshot, eventIndex, event.unitKey);
@@ -259,9 +281,8 @@ function plainIngestEventProgress(event, snapshot, eventIndex) {
259
281
  const total = plannedWorkUnitCountThrough(snapshot, eventIndex);
260
282
  const completed = completedWorkUnitCountThrough(snapshot, eventIndex);
261
283
  const active = activeWorkUnitCountThrough(snapshot, eventIndex);
262
- const stepFraction = event.stepBudget > 0 ? Math.min(1, event.stepIndex / event.stepBudget) : 0;
263
- const percent = total > 0 ? 55 + Math.ceil(((completed + stepFraction) / total) * 25) : 55;
264
- const latest = `${event.unitKey} step ${event.stepIndex}/${event.stepBudget}`;
284
+ const percent = total > 0 ? 55 + Math.ceil((completed / total) * 25) : 55;
285
+ const latest = `${event.unitKey} · ${pluralize(event.toolCalls, 'action')}`;
265
286
  return {
266
287
  percent,
267
288
  message: `Processing tasks: ${completed}/${total} complete, ${active} active; latest ${latest}`,
@@ -546,6 +567,8 @@ export async function runKtxIngest(args, io = process, deps = {}) {
546
567
  : io, deps.progress);
547
568
  plainProgress?.start();
548
569
  structuredProgress?.start();
570
+ const cliAbort = deps.abortSignal ? null : createCliAbortSignal();
571
+ const abortSignal = deps.abortSignal ?? cliAbort?.signal;
549
572
  let result;
550
573
  try {
551
574
  result = await executeMetabaseFanout({
@@ -559,6 +582,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
559
582
  embeddingProvider,
560
583
  ...(memoryFlow ? { memoryFlow } : {}),
561
584
  ...(progress ? { progress } : {}),
585
+ ...(abortSignal ? { abortSignal } : {}),
562
586
  });
563
587
  plainProgress?.flush();
564
588
  if (args.outputMode === 'json') {
@@ -570,6 +594,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
570
594
  }
571
595
  finally {
572
596
  plainProgress?.flush();
597
+ cliAbort?.dispose();
573
598
  }
574
599
  return result.status === 'all_failed' ? 1 : 0;
575
600
  }
@@ -612,6 +637,8 @@ export async function runKtxIngest(args, io = process, deps = {}) {
612
637
  : undefined;
613
638
  plainProgress?.start();
614
639
  structuredProgress?.start();
640
+ const cliAbort = deps.abortSignal ? null : createCliAbortSignal();
641
+ const abortSignal = deps.abortSignal ?? cliAbort?.signal;
615
642
  try {
616
643
  const result = await executeLocalIngest({
617
644
  project: ingestProject,
@@ -627,6 +654,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
627
654
  embeddingProvider,
628
655
  ...(args.debugLlmRequestFile ? { llmDebugRequestFile: args.debugLlmRequestFile } : {}),
629
656
  ...(memoryFlow ? { memoryFlow } : {}),
657
+ ...(abortSignal ? { abortSignal } : {}),
630
658
  });
631
659
  if (shouldUseLiveViz && memoryFlow) {
632
660
  latestMemoryFlowSnapshot = finalRunMemoryFlowInput(memoryFlow.snapshot(), result.report);
@@ -646,6 +674,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
646
674
  finally {
647
675
  plainProgress?.flush();
648
676
  liveTui?.close();
677
+ cliAbort?.dispose();
649
678
  }
650
679
  }
651
680
  if (args.reportFile) {
@@ -0,0 +1,11 @@
1
+ import type { KtxCliIo } from '../cli-runtime.js';
2
+ export interface BufferedCommandIo extends KtxCliIo {
3
+ stdoutText(): string;
4
+ stderrText(): string;
5
+ }
6
+ /**
7
+ * Captures stdout/stderr from a command (e.g. `runKtxConnection`) into buffers
8
+ * instead of the terminal. Callers decide whether to flush the captured text to
9
+ * the user or discard it.
10
+ */
11
+ export declare function createBufferedCommandIo(): BufferedCommandIo;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Captures stdout/stderr from a command (e.g. `runKtxConnection`) into buffers
3
+ * instead of the terminal. Callers decide whether to flush the captured text to
4
+ * the user or discard it.
5
+ */
6
+ export function createBufferedCommandIo() {
7
+ let stdout = '';
8
+ let stderr = '';
9
+ return {
10
+ stdout: {
11
+ isTTY: false,
12
+ write(chunk) {
13
+ stdout += chunk;
14
+ },
15
+ },
16
+ stderr: {
17
+ write(chunk) {
18
+ stderr += chunk;
19
+ },
20
+ },
21
+ stdoutText() {
22
+ return stdout;
23
+ },
24
+ stderrText() {
25
+ return stderr;
26
+ },
27
+ };
28
+ }
@@ -1,6 +1,8 @@
1
1
  export declare const SYMBOLS: {
2
2
  readonly middot: "-" | "·";
3
3
  readonly emDash: "--" | "—";
4
+ readonly star: "*" | "★";
5
+ readonly rightArrow: "→" | "->";
4
6
  };
5
7
  export declare function dim(text: string): string;
6
8
  export declare function bold(text: string): string;
@@ -12,6 +12,8 @@ const unicode = detectUnicodeSupport();
12
12
  export const SYMBOLS = {
13
13
  middot: unicode ? '·' : '-',
14
14
  emDash: unicode ? '—' : '--',
15
+ star: unicode ? '★' : '*',
16
+ rightArrow: unicode ? '→' : '->',
15
17
  };
16
18
  export function dim(text) {
17
19
  return styleText('dim', text);
@@ -1,7 +1,7 @@
1
1
  import type { LanguageModel, TelemetrySettings, ToolCallRepairFunction, ToolSet } from 'ai';
2
2
  export declare const KTX_MODEL_ROLES: readonly ["default", "triage", "candidateExtraction", "curator", "reconcile", "repair"];
3
3
  export type KtxModelRole = (typeof KTX_MODEL_ROLES)[number];
4
- type KtxLlmBackend = 'anthropic' | 'vertex' | 'gateway' | 'claude-code';
4
+ type KtxLlmBackend = 'anthropic' | 'vertex' | 'gateway' | 'claude-code' | 'codex';
5
5
  export type KtxPromptCacheTtl = '5m' | '1h';
6
6
  type KtxJsonValue = null | string | number | boolean | KtxJsonValue[] | {
7
7
  [key: string]: KtxJsonValue | undefined;
@@ -1,14 +1,22 @@
1
1
  import { type DefaultLocalIngestAdaptersOptions } from './context/ingest/local-adapters.js';
2
+ import type { HistoricSqlDialect, HistoricSqlReader } from './context/ingest/adapters/historic-sql/types.js';
2
3
  import type { SourceAdapter } from './context/ingest/types.js';
3
4
  import type { KtxLocalProject } from './context/project/project.js';
4
5
  import type { SqlAnalysisPort } from './context/sql-analysis/ports.js';
5
- import { type ManagedPythonCoreDaemonOptions } from './managed-python-http.js';
6
+ import { type ManagedPythonDaemonHttpOptions } from './managed-python-http.js';
6
7
  import type { KtxOperationalLogger } from './io/logger.js';
7
8
  export interface KtxCliLocalIngestAdaptersOptions extends DefaultLocalIngestAdaptersOptions {
8
9
  historicSqlConnectionId?: string;
9
10
  sqlAnalysis?: SqlAnalysisPort;
10
11
  sqlAnalysisUrl?: string;
11
- managedDaemon?: ManagedPythonCoreDaemonOptions;
12
+ managedDaemon?: ManagedPythonDaemonHttpOptions;
12
13
  logger?: KtxOperationalLogger;
13
14
  }
15
+ export interface KtxCliHistoricSqlRuntime {
16
+ dialect: HistoricSqlDialect;
17
+ sqlAnalysis: SqlAnalysisPort;
18
+ reader: HistoricSqlReader;
19
+ queryClient: unknown;
20
+ }
21
+ export declare function createKtxCliHistoricSqlRuntime(project: KtxLocalProject, connectionId: string, options?: KtxCliLocalIngestAdaptersOptions): KtxCliHistoricSqlRuntime | undefined;
14
22
  export declare function createKtxCliLocalIngestAdapters(project: KtxLocalProject, options?: KtxCliLocalIngestAdaptersOptions): SourceAdapter[];
@@ -12,7 +12,7 @@ import { isKtxSqliteConnectionConfig } from './connectors/sqlite/connector.js';
12
12
  import { createSqlServerLiveDatabaseIntrospection } from './connectors/sqlserver/live-database-introspection.js';
13
13
  import { isKtxSqlServerConnectionConfig } from './connectors/sqlserver/connector.js';
14
14
  import { BigQueryHistoricSqlQueryHistoryReader } from './context/ingest/adapters/historic-sql/bigquery-query-history-reader.js';
15
- import { queryHistoryDialectForConnection } from './context/ingest/adapters/historic-sql/connection-dialect.js';
15
+ import { historicSqlDialectForConnectionDriver } from './context/ingest/adapters/historic-sql/connection-dialect.js';
16
16
  import { createDaemonLiveDatabaseIntrospection } from './context/ingest/adapters/live-database/daemon-introspection.js';
17
17
  import { createDefaultLocalIngestAdapters } from './context/ingest/local-adapters.js';
18
18
  import { LiveDatabaseSourceAdapter } from './context/ingest/adapters/live-database/live-database.adapter.js';
@@ -224,7 +224,12 @@ function historicSqlOptionsForLocalRun(project, options) {
224
224
  return undefined;
225
225
  }
226
226
  const connection = project.config.connections[connectionId];
227
- const dialect = queryHistoryDialectForConnection(connection);
227
+ // historicSqlConnectionId is only set when query history was explicitly
228
+ // requested for this run (e.g. `--query-history`), so resolve the dialect from
229
+ // driver capability rather than the persisted context.queryHistory.enabled
230
+ // flag — otherwise the adapter is missing and findAdapter('historic-sql')
231
+ // throws even though the run asked for it.
232
+ const dialect = historicSqlDialectForConnectionDriver(connection);
228
233
  if (!dialect) {
229
234
  return undefined;
230
235
  }
@@ -234,6 +239,7 @@ function historicSqlOptionsForLocalRun(project, options) {
234
239
  if (dialect === 'postgres') {
235
240
  return {
236
241
  ...base,
242
+ dialect,
237
243
  reader: new PostgresPgssReader(),
238
244
  queryClient: createEphemeralPostgresHistoricSqlClient(project, connectionId),
239
245
  };
@@ -245,6 +251,7 @@ function historicSqlOptionsForLocalRun(project, options) {
245
251
  }
246
252
  return {
247
253
  ...base,
254
+ dialect,
248
255
  reader: new BigQueryHistoricSqlQueryHistoryReader({
249
256
  projectId: bigQueryProjectId(connection, process.env),
250
257
  region: bigQueryRegion(connection),
@@ -254,6 +261,7 @@ function historicSqlOptionsForLocalRun(project, options) {
254
261
  }
255
262
  return {
256
263
  ...base,
264
+ dialect,
257
265
  reader: new SnowflakeHistoricSqlQueryHistoryReader(),
258
266
  queryClient: {
259
267
  async executeQuery(query) {
@@ -264,8 +272,16 @@ function historicSqlOptionsForLocalRun(project, options) {
264
272
  },
265
273
  };
266
274
  }
275
+ export function createKtxCliHistoricSqlRuntime(project, connectionId, options = {}) {
276
+ return historicSqlOptionsForLocalRun(project, {
277
+ ...options,
278
+ historicSqlConnectionId: connectionId,
279
+ });
280
+ }
267
281
  export function createKtxCliLocalIngestAdapters(project, options = {}) {
268
- const historicSql = historicSqlOptionsForLocalRun(project, options);
282
+ const historicSql = options.historicSqlConnectionId
283
+ ? createKtxCliHistoricSqlRuntime(project, options.historicSqlConnectionId, options)
284
+ : undefined;
269
285
  const base = createDefaultLocalIngestAdapters(project, {
270
286
  ...options,
271
287
  databaseIntrospection: ktxCliDaemonDatabaseIntrospectionOptions(options),