@kaelio/ktx 0.1.0-rc.5 → 0.1.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.
- package/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl +0 -0
- package/assets/python/manifest.json +2 -2
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +23 -0
- package/dist/cli-program.js +5 -2
- package/dist/cli-program.test.js +7 -1
- package/dist/cli-runtime.d.ts +4 -0
- package/dist/cli-runtime.js +8 -1
- package/dist/command-schemas.d.ts +1 -1
- package/dist/commands/ingest-commands.js +1 -0
- package/dist/commands/knowledge-commands.js +5 -0
- package/dist/commands/mcp-commands.js +11 -3
- package/dist/commands/mcp-commands.test.js +30 -1
- package/dist/commands/sql-commands.d.ts +3 -0
- package/dist/commands/sql-commands.js +43 -0
- package/dist/commands/sql-commands.test.d.ts +1 -0
- package/dist/commands/sql-commands.test.js +68 -0
- package/dist/context-build-view.js +5 -1
- package/dist/dev.test.js +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.test.js +56 -21
- package/dist/ingest.js +123 -18
- package/dist/ingest.test.js +206 -0
- package/dist/io/print-list.d.ts +2 -1
- package/dist/io/print-list.js +7 -0
- package/dist/io/print-list.test.js +13 -11
- package/dist/io/symbols.d.ts +2 -2
- package/dist/knowledge.d.ts +1 -0
- package/dist/knowledge.js +34 -16
- package/dist/knowledge.test.js +27 -0
- package/dist/managed-python-command.d.ts +2 -0
- package/dist/managed-python-command.js +17 -9
- package/dist/managed-python-command.test.js +59 -4
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.test.js +2 -0
- package/dist/print-command-tree.js +7 -1
- package/dist/public-ingest.d.ts +9 -1
- package/dist/public-ingest.js +50 -7
- package/dist/public-ingest.test.js +69 -2
- package/dist/release-version.d.ts +5 -0
- package/dist/release-version.js +44 -0
- package/dist/runtime-requirements.d.ts +23 -0
- package/dist/runtime-requirements.js +99 -0
- package/dist/runtime-requirements.test.d.ts +1 -0
- package/dist/runtime-requirements.test.js +63 -0
- package/dist/setup-agents.d.ts +11 -3
- package/dist/setup-agents.js +397 -134
- package/dist/setup-agents.test.js +359 -61
- package/dist/setup-embeddings.js +3 -6
- package/dist/setup-embeddings.test.js +18 -2
- package/dist/setup-models.js +2 -2
- package/dist/setup-models.test.js +5 -3
- package/dist/setup-ready-menu.d.ts +1 -1
- package/dist/setup-ready-menu.js +2 -0
- package/dist/setup-ready-menu.test.js +3 -0
- package/dist/setup-runtime.d.ts +45 -0
- package/dist/setup-runtime.js +47 -0
- package/dist/setup-runtime.test.d.ts +1 -0
- package/dist/setup-runtime.test.js +110 -0
- package/dist/setup-sources-notion.test.d.ts +1 -0
- package/dist/setup-sources-notion.test.js +107 -0
- package/dist/setup-sources.js +5 -2
- package/dist/setup.d.ts +19 -1
- package/dist/setup.js +104 -29
- package/dist/setup.test.js +221 -57
- package/dist/sl.js +2 -2
- package/dist/sl.test.js +10 -0
- package/dist/source-mapping.js +9 -1
- package/dist/source-mapping.test.d.ts +1 -0
- package/dist/source-mapping.test.js +65 -0
- package/dist/sql.d.ts +22 -0
- package/dist/sql.js +125 -0
- package/dist/sql.test.d.ts +1 -0
- package/dist/sql.test.js +226 -0
- package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
- package/node_modules/@ktx/context/dist/connections/connection-type.d.ts +4 -4
- package/node_modules/@ktx/context/dist/core/git.service.d.ts +3 -0
- package/node_modules/@ktx/context/dist/core/git.service.js +47 -1
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/types.d.ts +5 -5
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/looker.adapter.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/types.d.ts +16 -16
- package/node_modules/@ktx/context/dist/ingest/adapters/lookml/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.js +16 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.test.js +41 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.d.ts +2 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.test.js +116 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.d.ts +25 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.js +149 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.js +167 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.js +178 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.js +109 -0
- package/node_modules/@ktx/context/dist/ingest/index.d.ts +8 -1
- package/node_modules/@ktx/context/dist/ingest/index.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +18 -2
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +1761 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +1695 -901
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +135 -118
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.d.ts +50 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.js +88 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.d.ts +16 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.js +78 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.d.ts +58 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.js +223 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.js +369 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.js +190 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.js +101 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.d.ts +15 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.js +61 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.js +137 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +54 -10
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +65 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +23 -5
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +17 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.test.js +1 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +6 -0
- package/node_modules/@ktx/context/dist/ingest/parsed-target-table.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/ports.d.ts +3 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +32 -7
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.test.js +124 -0
- package/node_modules/@ktx/context/dist/ingest/reports.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.d.ts +11 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.js +26 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.d.ts +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.js +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.test.js +29 -0
- package/node_modules/@ktx/context/dist/ingest/tools/emit-unmapped-fallback.tool.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/types.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.js +111 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.js +138 -0
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.js +19 -2
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.test.js +33 -0
- package/node_modules/@ktx/context/dist/project/setup-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/project/setup-config.js +10 -1
- package/node_modules/@ktx/context/dist/project/setup-config.test.js +3 -2
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.test.js +15 -0
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.test.js +22 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.d.ts +9 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.js +14 -0
- package/node_modules/@ktx/context/dist/tools/context-candidate-write.tool.d.ts +4 -4
- package/node_modules/@ktx/context/dist/tools/index.d.ts +1 -0
- package/node_modules/@ktx/context/dist/tools/index.js +1 -0
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.js +4 -1
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.test.js +44 -0
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.js +3 -48
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.test.js +28 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.d.ts +17 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.js +79 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.js +64 -0
- package/node_modules/@ktx/context/prompts/memory_agent_bundle_ingest_work_unit.md +23 -4
- package/node_modules/@ktx/context/skills/ingest_triage/SKILL.md +7 -3
- package/package.json +4 -4
package/dist/io/print-list.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface PrintListColumn<Row> {
|
|
|
22
22
|
* - `'suffix'` — trailing em-dash optional value. Default: any column with `optional: true`.
|
|
23
23
|
*/
|
|
24
24
|
role?: 'name' | 'metric' | 'badge' | 'suffix';
|
|
25
|
-
/** Custom pretty-mode value formatter (
|
|
25
|
+
/** Custom pretty-mode value formatter (for example, score -> "#1"). Plain/JSON unaffected. */
|
|
26
26
|
prettyFormat?: (value: Row[keyof Row & string], row: Row) => string;
|
|
27
27
|
}
|
|
28
28
|
export interface PrintListArgs<Row> {
|
|
@@ -40,3 +40,4 @@ export interface PrintListArgs<Row> {
|
|
|
40
40
|
io: KtxCliIo;
|
|
41
41
|
}
|
|
42
42
|
export declare function printList<Row extends object>(args: PrintListArgs<Row>): void;
|
|
43
|
+
export declare function createRankBadgeFormatter<Row extends object>(rows: ReadonlyArray<Row>): (_value: Row[keyof Row & string], row: Row) => string;
|
package/dist/io/print-list.js
CHANGED
|
@@ -15,6 +15,13 @@ export function printList(args) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
export function createRankBadgeFormatter(rows) {
|
|
19
|
+
const ranks = new WeakMap();
|
|
20
|
+
rows.forEach((row, index) => {
|
|
21
|
+
ranks.set(row, index + 1);
|
|
22
|
+
});
|
|
23
|
+
return (_value, row) => `#${ranks.get(row) ?? rows.indexOf(row) + 1}`;
|
|
24
|
+
}
|
|
18
25
|
function isEmpty(value) {
|
|
19
26
|
return value === undefined || value === null || value === '';
|
|
20
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { printList } from './print-list.js';
|
|
2
|
+
import { createRankBadgeFormatter, printList } from './print-list.js';
|
|
3
3
|
import { SYMBOLS } from './symbols.js';
|
|
4
4
|
function recorder() {
|
|
5
5
|
let stdout = '';
|
|
@@ -209,25 +209,25 @@ describe('printList — pretty mode', () => {
|
|
|
209
209
|
expect(out).toContain('(2 pages)');
|
|
210
210
|
expect(out).toContain('2 pages');
|
|
211
211
|
});
|
|
212
|
-
it('renders a leading badge column
|
|
212
|
+
it('renders a leading rank badge column in pretty mode', () => {
|
|
213
213
|
const r = recorder();
|
|
214
|
+
const rows = [
|
|
215
|
+
{ score: 0.87, scope: 'GLOBAL', key: 'alpha', summary: 'first' },
|
|
216
|
+
{ score: 0.04, scope: 'GLOBAL', key: 'beta', summary: 'second' },
|
|
217
|
+
];
|
|
214
218
|
const SEARCH_COLUMNS = [
|
|
215
219
|
{
|
|
216
220
|
key: 'score',
|
|
217
221
|
label: 'SCORE',
|
|
218
222
|
plain: 'score=',
|
|
219
223
|
role: 'badge',
|
|
220
|
-
prettyFormat: (
|
|
224
|
+
prettyFormat: createRankBadgeFormatter(rows),
|
|
221
225
|
dim: true,
|
|
222
226
|
},
|
|
223
227
|
{ key: 'scope', label: 'SCOPE', plain: '' },
|
|
224
228
|
{ key: 'key', label: 'KEY', plain: '' },
|
|
225
229
|
{ key: 'summary', label: 'SUMMARY', plain: '', optional: true, dim: true },
|
|
226
230
|
];
|
|
227
|
-
const rows = [
|
|
228
|
-
{ score: 0.87, scope: 'GLOBAL', key: 'alpha', summary: 'first' },
|
|
229
|
-
{ score: 0.04, scope: 'GLOBAL', key: 'beta', summary: 'second' },
|
|
230
|
-
];
|
|
231
231
|
printList({
|
|
232
232
|
rows,
|
|
233
233
|
columns: SEARCH_COLUMNS,
|
|
@@ -239,18 +239,20 @@ describe('printList — pretty mode', () => {
|
|
|
239
239
|
io: r.io,
|
|
240
240
|
});
|
|
241
241
|
const out = stripAnsi(r.out());
|
|
242
|
-
expect(out).toMatch(
|
|
243
|
-
expect(out).toMatch(
|
|
242
|
+
expect(out).toMatch(/#1\s+alpha\s+/);
|
|
243
|
+
expect(out).toMatch(/#2\s+beta\s+/);
|
|
244
|
+
expect(out).not.toContain('%');
|
|
244
245
|
});
|
|
245
246
|
it('emits the badge column in plain mode using its plain prefix', () => {
|
|
246
247
|
const r = recorder();
|
|
248
|
+
const rows = [{ score: 0.87, scope: 'GLOBAL', key: 'alpha', summary: 'first' }];
|
|
247
249
|
const SEARCH_COLUMNS = [
|
|
248
250
|
{
|
|
249
251
|
key: 'score',
|
|
250
252
|
label: 'SCORE',
|
|
251
253
|
plain: 'score=',
|
|
252
254
|
role: 'badge',
|
|
253
|
-
prettyFormat: (
|
|
255
|
+
prettyFormat: createRankBadgeFormatter(rows),
|
|
254
256
|
dim: true,
|
|
255
257
|
},
|
|
256
258
|
{ key: 'scope', label: 'SCOPE', plain: '' },
|
|
@@ -258,7 +260,7 @@ describe('printList — pretty mode', () => {
|
|
|
258
260
|
{ key: 'summary', label: 'SUMMARY', plain: '', optional: true, dim: true },
|
|
259
261
|
];
|
|
260
262
|
printList({
|
|
261
|
-
rows
|
|
263
|
+
rows,
|
|
262
264
|
columns: SEARCH_COLUMNS,
|
|
263
265
|
groupBy: 'scope',
|
|
264
266
|
mode: 'plain',
|
package/dist/io/symbols.d.ts
CHANGED
package/dist/knowledge.d.ts
CHANGED
package/dist/knowledge.js
CHANGED
|
@@ -2,25 +2,27 @@ import { createLocalKtxEmbeddingProviderFromConfig, KtxIngestEmbeddingPortAdapte
|
|
|
2
2
|
import { loadKtxProject } from '@ktx/context/project';
|
|
3
3
|
import { listLocalKnowledgePages, searchLocalKnowledgePages, } from '@ktx/context/wiki';
|
|
4
4
|
import { resolveOutputMode } from './io/mode.js';
|
|
5
|
-
import { printList } from './io/print-list.js';
|
|
5
|
+
import { createRankBadgeFormatter, printList } from './io/print-list.js';
|
|
6
6
|
const WIKI_LIST_COLUMNS = [
|
|
7
7
|
{ key: 'scope', label: 'SCOPE', plain: '' },
|
|
8
8
|
{ key: 'key', label: 'KEY', plain: '' },
|
|
9
9
|
{ key: 'summary', label: 'SUMMARY', plain: '', optional: true, dim: true },
|
|
10
10
|
];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
function wikiSearchColumns(rows) {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
key: 'score',
|
|
15
|
+
label: 'SCORE',
|
|
16
|
+
plain: 'score=',
|
|
17
|
+
role: 'badge',
|
|
18
|
+
prettyFormat: createRankBadgeFormatter(rows),
|
|
19
|
+
dim: true,
|
|
20
|
+
},
|
|
21
|
+
{ key: 'scope', label: 'SCOPE', plain: '' },
|
|
22
|
+
{ key: 'key', label: 'KEY', plain: '' },
|
|
23
|
+
{ key: 'summary', label: 'SUMMARY', plain: '', optional: true, dim: true },
|
|
24
|
+
];
|
|
25
|
+
}
|
|
24
26
|
function wikiSearchEmbeddingService(project, deps) {
|
|
25
27
|
if ('embeddingService' in deps) {
|
|
26
28
|
return deps.embeddingService ?? null;
|
|
@@ -28,6 +30,14 @@ function wikiSearchEmbeddingService(project, deps) {
|
|
|
28
30
|
const provider = (deps.createEmbeddingProvider ?? createLocalKtxEmbeddingProviderFromConfig)(project.config.ingest.embeddings);
|
|
29
31
|
return provider ? new KtxIngestEmbeddingPortAdapter(provider) : null;
|
|
30
32
|
}
|
|
33
|
+
function writeWikiSearchDebug(io, input) {
|
|
34
|
+
io.stderr.write(`[debug] wiki search mode=${input.mode} embedding=${input.embeddingConfigured ? 'configured' : 'unconfigured'} results=${input.results.length}\n`);
|
|
35
|
+
const lanes = input.results[0]?.lanes ?? [];
|
|
36
|
+
for (const lane of lanes) {
|
|
37
|
+
const reason = lane.reason ? ` reason=${lane.reason}` : '';
|
|
38
|
+
io.stderr.write(`[debug] wiki search lane=${lane.lane} status=${lane.status} returned=${lane.returnedCandidateCount} weight=${lane.weight}${reason}\n`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
32
42
|
try {
|
|
33
43
|
const project = await loadKtxProject({ projectDir: args.projectDir });
|
|
@@ -48,12 +58,20 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
|
48
58
|
return 0;
|
|
49
59
|
}
|
|
50
60
|
if (args.command === 'search') {
|
|
61
|
+
const embeddingService = wikiSearchEmbeddingService(project, deps);
|
|
51
62
|
const results = await searchLocalKnowledgePages(project, {
|
|
52
63
|
query: args.query,
|
|
53
64
|
userId: args.userId,
|
|
54
|
-
embeddingService
|
|
65
|
+
embeddingService,
|
|
55
66
|
limit: args.limit,
|
|
56
67
|
});
|
|
68
|
+
if (args.debug) {
|
|
69
|
+
writeWikiSearchDebug(io, {
|
|
70
|
+
mode: project.config.storage.search,
|
|
71
|
+
embeddingConfigured: embeddingService !== null,
|
|
72
|
+
results,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
57
75
|
const mode = resolveOutputMode({ explicit: args.output, json: args.json, io });
|
|
58
76
|
let emptyMessage = `No local wiki pages matched "${args.query}"`;
|
|
59
77
|
let emptyHint = 'Run `ktx wiki list` to inspect available pages.';
|
|
@@ -66,7 +84,7 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
|
66
84
|
}
|
|
67
85
|
printList({
|
|
68
86
|
rows: results,
|
|
69
|
-
columns:
|
|
87
|
+
columns: wikiSearchColumns(results),
|
|
70
88
|
groupBy: 'scope',
|
|
71
89
|
emptyMessage,
|
|
72
90
|
emptyHint,
|
package/dist/knowledge.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
4
5
|
import { initKtxProject, loadKtxProject } from '@ktx/context/project';
|
|
5
6
|
import { writeLocalKnowledgePage } from '@ktx/context/wiki';
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
@@ -67,6 +68,16 @@ describe('runKtxKnowledge', () => {
|
|
|
67
68
|
await expect(runKtxKnowledge({ command: 'search', projectDir, query: 'paid order', userId: 'local' }, searchIo.io)).resolves.toBe(0);
|
|
68
69
|
expect(searchIo.stdout()).toContain('metrics-revenue');
|
|
69
70
|
});
|
|
71
|
+
it('prints wiki search rank badges in pretty output', async () => {
|
|
72
|
+
const projectDir = join(tempDir, 'rank-project');
|
|
73
|
+
await initKtxProject({ projectDir });
|
|
74
|
+
await seedWikiPage(projectDir);
|
|
75
|
+
const searchIo = makeIo();
|
|
76
|
+
await expect(runKtxKnowledge({ command: 'search', projectDir, query: 'paid order', userId: 'local', output: 'pretty' }, searchIo.io)).resolves.toBe(0);
|
|
77
|
+
const stdout = stripVTControlCharacters(searchIo.stdout());
|
|
78
|
+
expect(stdout).toMatch(/#1\s+metrics-revenue/);
|
|
79
|
+
expect(stdout).not.toContain('%');
|
|
80
|
+
});
|
|
70
81
|
it('prints wiki list and search as public JSON envelopes', async () => {
|
|
71
82
|
const projectDir = join(tempDir, 'project');
|
|
72
83
|
await initKtxProject({ projectDir });
|
|
@@ -110,4 +121,20 @@ describe('runKtxKnowledge', () => {
|
|
|
110
121
|
expect(searchIo.stdout()).toContain('active-contract-arr-open-tickets');
|
|
111
122
|
expect(searchIo.stderr()).toBe('');
|
|
112
123
|
});
|
|
124
|
+
it('writes wiki search lane diagnostics to stderr when debug is enabled', async () => {
|
|
125
|
+
const projectDir = join(tempDir, 'debug-project');
|
|
126
|
+
await initKtxProject({ projectDir });
|
|
127
|
+
await seedWikiPage(projectDir);
|
|
128
|
+
const searchIo = makeIo();
|
|
129
|
+
await expect(runKtxKnowledge({ command: 'search', projectDir, query: 'paid order', userId: 'local', json: true, debug: true }, searchIo.io, { embeddingService: new FakeEmbeddingPort() })).resolves.toBe(0);
|
|
130
|
+
expect(JSON.parse(searchIo.stdout())).toMatchObject({
|
|
131
|
+
kind: 'list',
|
|
132
|
+
data: { items: [expect.objectContaining({ key: 'metrics-revenue' })] },
|
|
133
|
+
meta: { command: 'wiki search' },
|
|
134
|
+
});
|
|
135
|
+
expect(searchIo.stderr()).toContain('[debug] wiki search mode=sqlite-fts5');
|
|
136
|
+
expect(searchIo.stderr()).toContain('embedding=configured');
|
|
137
|
+
expect(searchIo.stderr()).toContain('lane=lexical status=available');
|
|
138
|
+
expect(searchIo.stderr()).toContain('lane=semantic status=available');
|
|
139
|
+
});
|
|
113
140
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createPythonSemanticLayerComputePort, type KtxSemanticLayerComputePort } from '@ktx/context/daemon';
|
|
2
2
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
3
|
+
import { type KtxCliSpinner } from './clack.js';
|
|
3
4
|
import { type InstalledKtxRuntimeManifest, type KtxRuntimeFeature, type ManagedPythonRuntimeInstallOptions, type ManagedPythonRuntimeInstallResult, type ManagedPythonRuntimeLayout, type ManagedPythonRuntimeLayoutOptions, type ManagedPythonRuntimeStatus } from './managed-python-runtime.js';
|
|
4
5
|
export type KtxManagedPythonInstallPolicy = 'prompt' | 'auto' | 'never';
|
|
5
6
|
export declare function runtimeInstallPolicyFromFlags(options: {
|
|
@@ -14,6 +15,7 @@ export interface ManagedPythonCommandDeps {
|
|
|
14
15
|
readStatus?: (options: ManagedPythonRuntimeLayoutOptions) => Promise<ManagedPythonRuntimeStatus>;
|
|
15
16
|
installRuntime?: (options: ManagedPythonRuntimeInstallOptions) => Promise<ManagedPythonRuntimeInstallResult>;
|
|
16
17
|
confirmInstall?: (message: string, io: KtxCliIo) => Promise<boolean>;
|
|
18
|
+
spinner?: () => KtxCliSpinner;
|
|
17
19
|
}
|
|
18
20
|
export interface ManagedPythonCommandOptions extends ManagedPythonCommandDeps {
|
|
19
21
|
cliVersion: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createPythonSemanticLayerComputePort } from '@ktx/context/daemon';
|
|
2
|
-
import { createClackPromptAdapter } from './clack.js';
|
|
2
|
+
import { createClackPromptAdapter, createStaticCliSpinner } from './clack.js';
|
|
3
3
|
import { installManagedPythonRuntime, readManagedPythonRuntimeStatus, } from './managed-python-runtime.js';
|
|
4
4
|
export function runtimeInstallPolicyFromFlags(options) {
|
|
5
5
|
if (options.yes === true && options.input === false) {
|
|
@@ -50,14 +50,21 @@ export async function ensureManagedPythonCommandRuntime(options) {
|
|
|
50
50
|
throw new Error(`KTX Python runtime installation was cancelled. Run: ${managedRuntimeInstallCommand(feature)}`);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
options.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const progress = (options.spinner ?? (() => createStaticCliSpinner(options.io)))();
|
|
54
|
+
progress.start(`Installing KTX Python runtime (${feature}) with uv...`);
|
|
55
|
+
try {
|
|
56
|
+
const installed = await installRuntime({
|
|
57
|
+
cliVersion: options.cliVersion,
|
|
58
|
+
features: [feature],
|
|
59
|
+
force: false,
|
|
60
|
+
});
|
|
61
|
+
progress.stop(`KTX Python runtime ready: ${installed.layout.versionDir}`);
|
|
62
|
+
return { layout: installed.layout, manifest: installed.manifest };
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
progress.error(`KTX Python runtime install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
export async function createManagedPythonSemanticLayerComputePort(options) {
|
|
63
70
|
const runtime = await ensureManagedPythonCommandRuntime({
|
|
@@ -68,6 +75,7 @@ export async function createManagedPythonSemanticLayerComputePort(options) {
|
|
|
68
75
|
...(options.readStatus ? { readStatus: options.readStatus } : {}),
|
|
69
76
|
...(options.installRuntime ? { installRuntime: options.installRuntime } : {}),
|
|
70
77
|
...(options.confirmInstall ? { confirmInstall: options.confirmInstall } : {}),
|
|
78
|
+
...(options.spinner ? { spinner: options.spinner } : {}),
|
|
71
79
|
});
|
|
72
80
|
const createPythonCompute = options.createPythonCompute ?? createPythonSemanticLayerComputePort;
|
|
73
81
|
return createPythonCompute({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { createManagedPythonSemanticLayerComputePort, managedRuntimeInstallCommand, runtimeInstallPolicyFromFlags, } from './managed-python-command.js';
|
|
2
|
+
import { createManagedPythonSemanticLayerComputePort, ensureManagedPythonCommandRuntime, managedRuntimeInstallCommand, runtimeInstallPolicyFromFlags, } from './managed-python-command.js';
|
|
3
3
|
function makeIo() {
|
|
4
4
|
let stdout = '';
|
|
5
5
|
let stderr = '';
|
|
@@ -85,6 +85,16 @@ function installResult(features = ['core']) {
|
|
|
85
85
|
manifest: installedManifest,
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
+
function makeSpinnerEvents() {
|
|
89
|
+
const events = [];
|
|
90
|
+
const spinner = vi.fn(() => ({
|
|
91
|
+
start: (msg) => events.push(`start:${msg}`),
|
|
92
|
+
message: (msg) => events.push(`message:${msg}`),
|
|
93
|
+
stop: (msg) => events.push(`stop:${msg}`),
|
|
94
|
+
error: (msg) => events.push(`error:${msg}`),
|
|
95
|
+
}));
|
|
96
|
+
return { events, spinner };
|
|
97
|
+
}
|
|
88
98
|
describe('managedRuntimeInstallCommand', () => {
|
|
89
99
|
it('prints the exact command for each managed runtime feature', () => {
|
|
90
100
|
expect(managedRuntimeInstallCommand('core')).toBe('ktx dev runtime install --yes');
|
|
@@ -103,6 +113,42 @@ describe('runtimeInstallPolicyFromFlags', () => {
|
|
|
103
113
|
});
|
|
104
114
|
});
|
|
105
115
|
describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
116
|
+
it('uses non-animated runtime setup status by default', async () => {
|
|
117
|
+
const io = makeIo();
|
|
118
|
+
await expect(ensureManagedPythonCommandRuntime({
|
|
119
|
+
cliVersion: '0.2.0',
|
|
120
|
+
installPolicy: 'auto',
|
|
121
|
+
io: io.io,
|
|
122
|
+
readStatus: vi.fn(async () => missingStatus()),
|
|
123
|
+
installRuntime: vi.fn(async () => installResult(['local-embeddings'])),
|
|
124
|
+
feature: 'local-embeddings',
|
|
125
|
+
})).resolves.toMatchObject({
|
|
126
|
+
layout: { versionDir: '/runtime/0.2.0' },
|
|
127
|
+
});
|
|
128
|
+
expect(io.stderr()).toContain('Installing KTX Python runtime (local-embeddings) with uv...');
|
|
129
|
+
expect(io.stderr()).toContain('KTX Python runtime ready: /runtime/0.2.0');
|
|
130
|
+
expect(io.stderr().match(/Installing KTX Python runtime/g)).toHaveLength(1);
|
|
131
|
+
});
|
|
132
|
+
it('shows runtime installation progress with the CLI spinner', async () => {
|
|
133
|
+
const io = makeIo();
|
|
134
|
+
const { events, spinner } = makeSpinnerEvents();
|
|
135
|
+
const options = {
|
|
136
|
+
cliVersion: '0.2.0',
|
|
137
|
+
installPolicy: 'auto',
|
|
138
|
+
io: io.io,
|
|
139
|
+
readStatus: vi.fn(async () => missingStatus()),
|
|
140
|
+
installRuntime: vi.fn(async () => installResult(['local-embeddings'])),
|
|
141
|
+
feature: 'local-embeddings',
|
|
142
|
+
spinner,
|
|
143
|
+
};
|
|
144
|
+
await expect(ensureManagedPythonCommandRuntime(options)).resolves.toMatchObject({
|
|
145
|
+
layout: { versionDir: '/runtime/0.2.0' },
|
|
146
|
+
});
|
|
147
|
+
expect(events).toEqual([
|
|
148
|
+
'start:Installing KTX Python runtime (local-embeddings) with uv...',
|
|
149
|
+
'stop:KTX Python runtime ready: /runtime/0.2.0',
|
|
150
|
+
]);
|
|
151
|
+
});
|
|
106
152
|
it('uses the managed ktx-daemon executable when the runtime is ready', async () => {
|
|
107
153
|
const io = makeIo();
|
|
108
154
|
const compute = { query: vi.fn(), validateSources: vi.fn(), generateSources: vi.fn() };
|
|
@@ -135,6 +181,7 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
|
135
181
|
});
|
|
136
182
|
it('installs the core runtime without prompting when policy is auto', async () => {
|
|
137
183
|
const io = makeIo();
|
|
184
|
+
const { events, spinner } = makeSpinnerEvents();
|
|
138
185
|
const compute = { query: vi.fn(), validateSources: vi.fn(), generateSources: vi.fn() };
|
|
139
186
|
const createPythonCompute = vi.fn(() => compute);
|
|
140
187
|
const installRuntime = vi.fn(async () => installResult());
|
|
@@ -145,17 +192,21 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
|
145
192
|
readStatus: vi.fn(async () => missingStatus()),
|
|
146
193
|
installRuntime,
|
|
147
194
|
createPythonCompute,
|
|
195
|
+
spinner,
|
|
148
196
|
})).resolves.toBe(compute);
|
|
149
197
|
expect(installRuntime).toHaveBeenCalledWith({
|
|
150
198
|
cliVersion: '0.2.0',
|
|
151
199
|
features: ['core'],
|
|
152
200
|
force: false,
|
|
153
201
|
});
|
|
154
|
-
expect(
|
|
155
|
-
|
|
202
|
+
expect(events).toEqual([
|
|
203
|
+
'start:Installing KTX Python runtime (core) with uv...',
|
|
204
|
+
'stop:KTX Python runtime ready: /runtime/0.2.0',
|
|
205
|
+
]);
|
|
156
206
|
});
|
|
157
207
|
it('prompts before installing when policy is prompt', async () => {
|
|
158
208
|
const io = makeIo();
|
|
209
|
+
const { events, spinner } = makeSpinnerEvents();
|
|
159
210
|
const confirmInstall = vi.fn(async () => true);
|
|
160
211
|
const installRuntime = vi.fn(async () => installResult());
|
|
161
212
|
await createManagedPythonSemanticLayerComputePort({
|
|
@@ -166,6 +217,7 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
|
166
217
|
installRuntime,
|
|
167
218
|
createPythonCompute: vi.fn(() => ({ query: vi.fn(), validateSources: vi.fn(), generateSources: vi.fn() })),
|
|
168
219
|
confirmInstall,
|
|
220
|
+
spinner,
|
|
169
221
|
});
|
|
170
222
|
expect(confirmInstall).toHaveBeenCalledWith('KTX needs to install the core Python runtime. This downloads Python dependencies with uv. Continue?', io.io);
|
|
171
223
|
expect(installRuntime).toHaveBeenCalledWith({
|
|
@@ -173,9 +225,11 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
|
173
225
|
features: ['core'],
|
|
174
226
|
force: false,
|
|
175
227
|
});
|
|
228
|
+
expect(events).toContainEqual('start:Installing KTX Python runtime (core) with uv...');
|
|
176
229
|
});
|
|
177
230
|
it('uses injected runtime confirmation instead of reading process TTY directly', async () => {
|
|
178
231
|
const io = makeIo();
|
|
232
|
+
const { events, spinner } = makeSpinnerEvents();
|
|
179
233
|
const compute = { query: vi.fn(), validateSources: vi.fn(), generateSources: vi.fn() };
|
|
180
234
|
const installRuntime = vi.fn(async () => installResult());
|
|
181
235
|
const confirmInstall = vi.fn(async () => true);
|
|
@@ -187,9 +241,10 @@ describe('createManagedPythonSemanticLayerComputePort', () => {
|
|
|
187
241
|
installRuntime,
|
|
188
242
|
confirmInstall,
|
|
189
243
|
createPythonCompute: () => compute,
|
|
244
|
+
spinner,
|
|
190
245
|
})).resolves.toBe(compute);
|
|
191
246
|
expect(confirmInstall).toHaveBeenCalledWith('KTX needs to install the core Python runtime. This downloads Python dependencies with uv. Continue?', io.io);
|
|
192
|
-
expect(
|
|
247
|
+
expect(events).toContainEqual('start:Installing KTX Python runtime (core) with uv...');
|
|
193
248
|
});
|
|
194
249
|
it('can decide default runtime prompting from injected io capabilities', async () => {
|
|
195
250
|
const io = makeIo();
|
package/dist/next-steps.js
CHANGED
|
@@ -29,7 +29,7 @@ function commandLines(commands, indent) {
|
|
|
29
29
|
}
|
|
30
30
|
export function formatNextStepLines(indent = ' ') {
|
|
31
31
|
return [
|
|
32
|
-
`${indent}KTX context is ready for agents. Open your coding agent
|
|
32
|
+
`${indent}KTX context is ready for agents. Open your coding agent from the KTX project directory and ask a data question.`,
|
|
33
33
|
`${indent}Verify with:`,
|
|
34
34
|
...commandLines(KTX_NEXT_STEP_DIRECT_COMMANDS, indent),
|
|
35
35
|
];
|
package/dist/next-steps.test.js
CHANGED
|
@@ -38,8 +38,10 @@ describe('KTX demo next steps', () => {
|
|
|
38
38
|
it('explains what the next-step commands are for', () => {
|
|
39
39
|
const rendered = formatNextStepLines().join('\n');
|
|
40
40
|
expect(rendered).toContain('KTX context is ready for agents.');
|
|
41
|
+
expect(rendered).toContain('KTX project directory');
|
|
41
42
|
expect(rendered).toContain('ask a data question');
|
|
42
43
|
expect(rendered).toContain('Verify with:');
|
|
44
|
+
expect(rendered).not.toContain('this directory');
|
|
43
45
|
expect(rendered).not.toContain('Preferred route');
|
|
44
46
|
expect(rendered).not.toContain('Optional MCP:');
|
|
45
47
|
});
|
|
@@ -8,7 +8,13 @@ function silentIo() {
|
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
function stubPackageInfo() {
|
|
11
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
name: '@ktx/cli',
|
|
13
|
+
version: '0.0.0-docs',
|
|
14
|
+
packageVersion: '0.0.0-private',
|
|
15
|
+
runtimeVersion: '0.0.0-docs',
|
|
16
|
+
contextPackageName: '@ktx/context',
|
|
17
|
+
};
|
|
12
18
|
}
|
|
13
19
|
export function renderKtxCommandTree() {
|
|
14
20
|
const program = buildKtxProgram({
|
package/dist/public-ingest.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { KtxProgressPort } from '@ktx/context/scan';
|
|
|
3
3
|
import type { KtxCliIo } from './index.js';
|
|
4
4
|
import type { KtxIngestArgs, KtxIngestDeps, KtxIngestProgressUpdate } from './ingest.js';
|
|
5
5
|
import { type KtxDatabaseContextDepth } from './ingest-depth.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type KtxManagedPythonInstallPolicy, type ManagedPythonCommandRuntime } from './managed-python-command.js';
|
|
7
|
+
import type { KtxRuntimeFeature } from './managed-python-runtime.js';
|
|
7
8
|
import type { KtxScanArgs, KtxScanDeps } from './scan.js';
|
|
8
9
|
type KtxPublicIngestStepName = 'database-schema' | 'query-history' | 'source-ingest' | 'memory-update';
|
|
9
10
|
type KtxPublicIngestStepStatus = 'done' | 'skipped' | 'failed' | 'not-run';
|
|
@@ -75,6 +76,13 @@ export interface KtxPublicIngestDeps {
|
|
|
75
76
|
}>;
|
|
76
77
|
scanProgress?: KtxProgressPort;
|
|
77
78
|
ingestProgress?: (update: KtxIngestProgressUpdate) => void;
|
|
79
|
+
ensureRuntime?: (options: {
|
|
80
|
+
cliVersion: string;
|
|
81
|
+
installPolicy: KtxManagedPythonInstallPolicy;
|
|
82
|
+
io: KtxCliIo;
|
|
83
|
+
feature: KtxRuntimeFeature;
|
|
84
|
+
}) => Promise<ManagedPythonCommandRuntime>;
|
|
85
|
+
env?: NodeJS.ProcessEnv;
|
|
78
86
|
runtimeIo?: KtxCliIo;
|
|
79
87
|
onPhaseStart?: (phaseKey: KtxPublicIngestPhaseKey) => void;
|
|
80
88
|
onPhaseEnd?: (phaseKey: KtxPublicIngestPhaseKey, status: 'done' | 'failed' | 'skipped', summary?: string) => void;
|
package/dist/public-ingest.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { loadKtxProject } from '@ktx/context/project';
|
|
2
2
|
import { databaseContextDepth, deepReadinessGaps, isDatabaseDriver, normalizeConnectionDriver, } from './ingest-depth.js';
|
|
3
|
+
import { ensureManagedPythonCommandRuntime, } from './managed-python-command.js';
|
|
3
4
|
import { publicIngestOutputLine } from './public-ingest-copy.js';
|
|
5
|
+
import { resolvePublicIngestRuntimeRequirements } from './runtime-requirements.js';
|
|
4
6
|
import { profileMark } from './startup-profile.js';
|
|
5
7
|
profileMark('module:public-ingest');
|
|
6
8
|
const sourceAdapterByDriver = new Map([
|
|
@@ -304,6 +306,7 @@ function failureDetailWithRetry(input) {
|
|
|
304
306
|
}
|
|
305
307
|
function markTargetResult(target, args, status, failedOperation, failureDetail) {
|
|
306
308
|
const selectedFailedOperation = failedOperation ?? (target.operation === 'database-ingest' ? 'database-schema' : 'source-ingest');
|
|
309
|
+
const selectedFailedOperationIndex = target.steps.indexOf(selectedFailedOperation);
|
|
307
310
|
return {
|
|
308
311
|
connectionId: target.connectionId,
|
|
309
312
|
driver: target.driver,
|
|
@@ -314,6 +317,10 @@ function markTargetResult(target, args, status, failedOperation, failureDetail)
|
|
|
314
317
|
if (status === 'done') {
|
|
315
318
|
return { ...step, status: 'done' };
|
|
316
319
|
}
|
|
320
|
+
const stepIndex = target.steps.indexOf(step.operation);
|
|
321
|
+
if (selectedFailedOperationIndex >= 0 && stepIndex >= 0 && stepIndex < selectedFailedOperationIndex) {
|
|
322
|
+
return { ...step, status: 'done' };
|
|
323
|
+
}
|
|
317
324
|
if (step.operation === selectedFailedOperation) {
|
|
318
325
|
return {
|
|
319
326
|
...step,
|
|
@@ -386,15 +393,34 @@ function createCapturedPublicIngestIo() {
|
|
|
386
393
|
};
|
|
387
394
|
}
|
|
388
395
|
const INTERNAL_STATUS_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
|
|
389
|
-
|
|
390
|
-
|
|
396
|
+
const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX managed daemon|Error:|Failed\b|Could not\b|Cannot\b)/;
|
|
397
|
+
const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/;
|
|
398
|
+
function trimErrorPrefix(line) {
|
|
399
|
+
return line.replace(/^Error:\s*/, '');
|
|
400
|
+
}
|
|
401
|
+
function capturedFailureMessage(output) {
|
|
402
|
+
const lines = output
|
|
391
403
|
.split(/\r?\n/)
|
|
392
404
|
.map((line) => line.trim())
|
|
393
405
|
.filter((line) => line.length > 0)
|
|
394
406
|
.filter((line) => !line.startsWith('KTX scan completed'))
|
|
395
407
|
.filter((line) => !INTERNAL_STATUS_LINE_RE.test(line))
|
|
396
|
-
.map(publicIngestOutputLine)
|
|
397
|
-
|
|
408
|
+
.map(publicIngestOutputLine);
|
|
409
|
+
const actionableIndex = lines.findIndex((line) => ACTIONABLE_FAILURE_LINE_RE.test(line));
|
|
410
|
+
if (actionableIndex < 0) {
|
|
411
|
+
const line = lines.find((candidate) => candidate.length > 0);
|
|
412
|
+
return line ? trimErrorPrefix(line) : undefined;
|
|
413
|
+
}
|
|
414
|
+
const firstLine = lines[actionableIndex];
|
|
415
|
+
if (!firstLine?.startsWith('Missing bundled Python runtime manifest')) {
|
|
416
|
+
return trimErrorPrefix(firstLine);
|
|
417
|
+
}
|
|
418
|
+
const followupLines = lines
|
|
419
|
+
.slice(actionableIndex + 1)
|
|
420
|
+
.filter((line) => !RUNTIME_BACKED_RETRY_LINE_RE.test(line))
|
|
421
|
+
.filter((line) => !/\bRetry:\s/.test(line))
|
|
422
|
+
.filter((line) => line.startsWith('In a source checkout, build the local runtime assets with:'));
|
|
423
|
+
return [firstLine, ...followupLines].join('\n');
|
|
398
424
|
}
|
|
399
425
|
export async function executePublicIngestTarget(target, args, io, deps) {
|
|
400
426
|
if (target.preflightFailure) {
|
|
@@ -445,7 +471,7 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
445
471
|
if (target.queryHistory?.enabled === true) {
|
|
446
472
|
deps.onPhaseEnd?.('query-history', 'skipped');
|
|
447
473
|
}
|
|
448
|
-
return markTargetResult(target, args, 'failed', 'database-schema', capturedScanIo ?
|
|
474
|
+
return markTargetResult(target, args, 'failed', 'database-schema', capturedScanIo ? capturedFailureMessage(capturedScanIo.capturedOutput()) : undefined);
|
|
449
475
|
}
|
|
450
476
|
deps.onPhaseEnd?.('database-schema', 'done');
|
|
451
477
|
if (target.queryHistory?.enabled === true) {
|
|
@@ -478,7 +504,7 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
478
504
|
: await runIngest(ingestArgs, ingestIo);
|
|
479
505
|
if (qhExitCode !== 0) {
|
|
480
506
|
deps.onPhaseEnd?.('query-history', 'failed');
|
|
481
|
-
return markTargetResult(target, args, 'failed', 'query-history', capturedIngestIo ?
|
|
507
|
+
return markTargetResult(target, args, 'failed', 'query-history', capturedIngestIo ? capturedFailureMessage(capturedIngestIo.capturedOutput()) : undefined);
|
|
482
508
|
}
|
|
483
509
|
deps.onPhaseEnd?.('query-history', 'done');
|
|
484
510
|
}
|
|
@@ -509,12 +535,29 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
509
535
|
? await runIngest(ingestArgs, ingestIo, ingestDeps)
|
|
510
536
|
: await runIngest(ingestArgs, ingestIo);
|
|
511
537
|
deps.onPhaseEnd?.('source-ingest', exitCode === 0 ? 'done' : 'failed');
|
|
512
|
-
return markTargetResult(target, args, exitCode === 0 ? 'done' : 'failed', 'source-ingest', capturedIngestIo ?
|
|
538
|
+
return markTargetResult(target, args, exitCode === 0 ? 'done' : 'failed', 'source-ingest', capturedIngestIo ? capturedFailureMessage(capturedIngestIo.capturedOutput()) : undefined);
|
|
513
539
|
}
|
|
514
540
|
export async function runKtxPublicIngest(args, io, deps = {}) {
|
|
515
541
|
const loadProject = deps.loadProject ?? loadKtxProject;
|
|
516
542
|
const project = await loadProject({ projectDir: args.projectDir });
|
|
517
543
|
if (shouldUseForegroundContextBuildView(args, io)) {
|
|
544
|
+
const plan = buildPublicIngestPlan(project, args);
|
|
545
|
+
const requirements = resolvePublicIngestRuntimeRequirements(plan, { env: deps.env ?? process.env });
|
|
546
|
+
const ensureRuntime = deps.ensureRuntime ?? ensureManagedPythonCommandRuntime;
|
|
547
|
+
for (const feature of requirements.features) {
|
|
548
|
+
try {
|
|
549
|
+
await ensureRuntime({
|
|
550
|
+
cliVersion: args.cliVersion ?? '0.0.0-private',
|
|
551
|
+
installPolicy: args.runtimeInstallPolicy ?? 'prompt',
|
|
552
|
+
io,
|
|
553
|
+
feature,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
558
|
+
return 1;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
518
561
|
const { runContextBuild } = await import('./context-build-view.js');
|
|
519
562
|
const contextBuild = deps.runContextBuild ?? runContextBuild;
|
|
520
563
|
const result = await contextBuild(project, {
|