@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.
- package/assets/python/{kaelio_ktx-0.10.0-py3-none-any.whl → kaelio_ktx-0.12.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin.js +1 -1
- package/dist/clack.d.ts +16 -0
- package/dist/clack.js +37 -6
- package/dist/claude-code-prompt-caching.js +1 -1
- package/dist/cli-program.js +7 -3
- package/dist/cli-runtime.d.ts +2 -0
- package/dist/cli-runtime.js +14 -8
- package/dist/commands/connection-commands.js +1 -1
- package/dist/commands/ingest-commands.js +4 -4
- package/dist/commands/mcp-commands.js +12 -12
- package/dist/commands/runtime-commands.js +4 -4
- package/dist/commands/setup-commands.js +6 -5
- package/dist/commands/sl-commands.js +1 -1
- package/dist/commands/sql-commands.js +1 -1
- package/dist/commands/status-commands.js +1 -1
- package/dist/community-cta.d.ts +11 -0
- package/dist/community-cta.js +19 -0
- package/dist/connection.js +1 -1
- package/dist/connectors/clickhouse/connector.js +1 -1
- package/dist/connectors/mysql/connector.js +1 -1
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +3 -3
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +116 -2
- package/dist/context/core/git-env.d.ts +12 -1
- package/dist/context/core/git-env.js +17 -2
- package/dist/context/core/git.service.d.ts +23 -0
- package/dist/context/core/git.service.js +86 -15
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- package/dist/context/ingest/adapters/looker/client.js +7 -2
- package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
- package/dist/context/ingest/adapters/looker/factory.js +9 -0
- package/dist/context/ingest/adapters/looker/mapping.js +1 -1
- package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.js +1 -1
- package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
- package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
- package/dist/context/ingest/artifact-gates.d.ts +2 -6
- package/dist/context/ingest/artifact-gates.js +5 -47
- package/dist/context/ingest/constrained-repair.d.ts +55 -0
- package/dist/context/ingest/constrained-repair.js +167 -0
- package/dist/context/ingest/final-gate-repair.d.ts +9 -11
- package/dist/context/ingest/final-gate-repair.js +40 -128
- package/dist/context/ingest/finalization-scope.d.ts +1 -1
- package/dist/context/ingest/finalization-scope.js +15 -15
- package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
- package/dist/context/ingest/ingest-bundle.runner.js +101 -67
- package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
- package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
- package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
- package/dist/context/ingest/local-bundle-runtime.js +9 -10
- package/dist/context/ingest/local-ingest.d.ts +2 -0
- package/dist/context/ingest/local-ingest.js +2 -0
- package/dist/context/ingest/memory-flow/view-model.js +1 -1
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
- package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
- package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
- package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
- package/dist/context/llm/ai-sdk-runtime.js +2 -2
- package/dist/context/llm/claude-code-runtime.js +1 -1
- package/dist/context/llm/local-config.js +1 -1
- package/dist/context/llm/runtime-tools.js +2 -2
- package/dist/context/mcp/context-tools.js +7 -7
- package/dist/context/mcp/local-project-ports.js +23 -54
- package/dist/context/memory/local-memory.js +4 -1
- package/dist/context/memory/memory-agent.service.js +1 -1
- package/dist/context/project/config.d.ts +11 -4
- package/dist/context/project/config.js +85 -30
- package/dist/context/project/driver-schemas.js +1 -1
- package/dist/context/project/mappings-yaml-schema.js +2 -2
- package/dist/context/project/project.js +12 -4
- package/dist/context/scan/description-generation.js +4 -4
- package/dist/context/scan/local-enrichment-artifacts.js +2 -1
- package/dist/context/scan/local-scan.js +2 -2
- package/dist/context/scan/local-structural-artifacts.js +5 -5
- package/dist/context/scan/relationship-benchmark-report.js +1 -1
- package/dist/context/scan/relationship-discovery.js +3 -3
- package/dist/context/scan/relationship-llm-proposal.js +3 -3
- package/dist/context/sl/local-query.js +3 -33
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +44 -69
- package/dist/context/sl/semantic-layer.service.d.ts +25 -8
- package/dist/context/sl/semantic-layer.service.js +109 -56
- package/dist/context/sl/source-files.d.ts +46 -0
- package/dist/context/sl/source-files.js +131 -0
- package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
- package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
- package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
- package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
- package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
- package/dist/context/sql-analysis/dialect.d.ts +2 -0
- package/dist/context/sql-analysis/dialect.js +20 -0
- package/dist/context/tools/base-tool.d.ts +6 -19
- package/dist/context/tools/base-tool.js +0 -14
- package/dist/context-build-view.js +5 -5
- package/dist/database-tree-picker.js +18 -3
- package/dist/demo-assets.js +0 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +31 -23
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +44 -0
- package/dist/ingest.d.ts +1 -1
- package/dist/ingest.js +8 -2
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/io/tty.d.ts +17 -0
- package/dist/io/tty.js +21 -0
- package/dist/links.d.ts +1 -0
- package/dist/links.js +1 -0
- package/dist/llm/embedding-health.js +1 -1
- package/dist/llm/embedding-provider.js +3 -3
- package/dist/llm/model-provider.js +1 -1
- package/dist/local-adapters.d.ts +1 -0
- package/dist/local-adapters.js +2 -2
- package/dist/local-scan-connectors.js +1 -1
- package/dist/managed-local-embeddings.js +17 -8
- package/dist/managed-mcp-daemon.js +3 -3
- package/dist/managed-python-command.d.ts +7 -0
- package/dist/managed-python-command.js +34 -8
- package/dist/managed-python-daemon.js +2 -2
- package/dist/managed-python-http.js +3 -3
- package/dist/managed-python-runtime.d.ts +30 -1
- package/dist/managed-python-runtime.js +134 -18
- package/dist/managed-uv-release.d.ts +7 -0
- package/dist/managed-uv-release.js +11 -0
- package/dist/mcp-http-server.js +4 -4
- package/dist/mcp-server-factory.js +3 -3
- package/dist/mcp-stdio-server.js +1 -1
- package/dist/memory-flow-hud.js +2 -2
- package/dist/next-steps.js +2 -2
- package/dist/prompt-navigation.d.ts +17 -0
- package/dist/prompt-navigation.js +49 -3
- package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
- package/dist/prompts/memory_agent_external_ingest.md +2 -2
- package/dist/public-ingest-copy.js +1 -1
- package/dist/public-ingest.js +3 -3
- package/dist/release-version.js +1 -1
- package/dist/runtime-requirements.js +1 -1
- package/dist/runtime.js +9 -9
- package/dist/scan.js +1 -1
- package/dist/setup-agents.js +22 -35
- package/dist/setup-banner.d.ts +20 -0
- package/dist/setup-banner.js +39 -0
- package/dist/setup-context.js +24 -15
- package/dist/setup-databases.js +31 -59
- package/dist/setup-demo-tour.js +12 -8
- package/dist/setup-embeddings.js +9 -9
- package/dist/setup-interrupt.js +1 -1
- package/dist/setup-models.d.ts +4 -1
- package/dist/setup-models.js +54 -28
- package/dist/setup-project.js +29 -5
- package/dist/setup-prompts.js +16 -5
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +27 -7
- package/dist/setup.d.ts +25 -0
- package/dist/setup.js +90 -19
- package/dist/skills/analytics/SKILL.md +3 -3
- package/dist/skills/dbt_ingest/SKILL.md +3 -3
- package/dist/skills/looker_ingest/SKILL.md +3 -3
- package/dist/skills/lookml_ingest/SKILL.md +7 -7
- package/dist/skills/metabase_ingest/SKILL.md +4 -4
- package/dist/skills/metricflow_ingest/SKILL.md +15 -15
- package/dist/skills/notion_synthesize/SKILL.md +1 -1
- package/dist/skills/sl/SKILL.md +3 -3
- package/dist/skills/sl_capture/SKILL.md +1 -1
- package/dist/skills/wiki_capture/SKILL.md +1 -1
- package/dist/source-mapping.js +1 -1
- package/dist/startup-profile.js +1 -1
- package/dist/status-project.d.ts +0 -2
- package/dist/status-project.js +4 -6
- package/dist/telemetry/command-hook.d.ts +24 -0
- package/dist/telemetry/command-hook.js +37 -3
- package/dist/telemetry/events.d.ts +1 -1
- package/dist/telemetry/exception.js +14 -0
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- package/dist/text-ingest.js +1 -1
- package/dist/tree-picker-tui.d.ts +0 -1
- package/dist/tree-picker-tui.js +2 -3
- package/package.json +1 -1
package/dist/setup.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ktxLocalStateDbPath } from './context/project/local-state-db.js';
|
|
|
6
6
|
import { loadKtxProject } from './context/project/project.js';
|
|
7
7
|
import { readKtxSetupState } from './context/project/setup-config.js';
|
|
8
8
|
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
|
9
|
+
import { SLACK_SETUP_NOTE } from './community-cta.js';
|
|
9
10
|
import { formatNextStepLines, formatSetupNextStepLines } from './next-steps.js';
|
|
10
11
|
import { runtimeInstallPolicyFromFlags } from './managed-python-command.js';
|
|
11
12
|
import { readManagedPythonRuntimeStatus } from './managed-python-runtime.js';
|
|
@@ -36,6 +37,61 @@ function setupTelemetryOutcome(status) {
|
|
|
36
37
|
return 'skipped';
|
|
37
38
|
return 'abandoned';
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Classify a terminal non-ready setup status into the `command` telemetry
|
|
42
|
+
* outcome. The setup flow is the decision-maker and knows the difference:
|
|
43
|
+
* - `failed` is a genuine error; attach a step-scoped reason so the dashboard
|
|
44
|
+
* shows an actionable signature instead of a blank.
|
|
45
|
+
* - `missing-input` from a *non-interactive* run is an automation error
|
|
46
|
+
* (required flags absent and no prompt was possible); attach a reason too.
|
|
47
|
+
* - `missing-input` from an interactive prompt, or a project `cancelled`, is the
|
|
48
|
+
* user backing out of the wizard — an abort, not a failure. Keep it out of
|
|
49
|
+
* error telemetry so it stops inflating the error count.
|
|
50
|
+
*
|
|
51
|
+
* `interactive` must reflect whether a prompt could actually be shown — input
|
|
52
|
+
* is enabled AND a TTY is attached. `inputMode: 'auto'` alone is not enough: a
|
|
53
|
+
* piped/CI run without `--no-input` is still non-interactive, and steps such as
|
|
54
|
+
* the project step return `missing-input` ("pass --yes …") there without ever
|
|
55
|
+
* prompting. Treating that as an abort would make a broken automation run exit
|
|
56
|
+
* 0, so it must classify as an error.
|
|
57
|
+
*
|
|
58
|
+
* Reasons are synthetic, step-scoped strings (no user input), so they satisfy
|
|
59
|
+
* the telemetry privacy rules. The step's own `errorDetail`, when present, has
|
|
60
|
+
* already been vetted for the `setup_step` event and is safe to reuse.
|
|
61
|
+
*/
|
|
62
|
+
function setupCommandOutcomeAnnotation(input) {
|
|
63
|
+
if (input.status === 'failed') {
|
|
64
|
+
return {
|
|
65
|
+
outcome: 'error',
|
|
66
|
+
errorClass: 'KtxSetupStepFailed',
|
|
67
|
+
errorDetail: input.errorDetail ?? `${input.step} setup step failed`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (input.status === 'missing-input' && !input.interactive) {
|
|
71
|
+
return {
|
|
72
|
+
outcome: 'error',
|
|
73
|
+
errorClass: 'KtxSetupMissingInput',
|
|
74
|
+
errorDetail: `${input.step} setup step requires input not provided in a non-interactive run`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { outcome: 'aborted' };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Single source of truth for how a non-ready setup step ends: the process exit
|
|
81
|
+
* code and the telemetry annotation are both derived from one classification,
|
|
82
|
+
* so they can never disagree. A genuine failure (`error`) exits non-zero; an
|
|
83
|
+
* abort — the user leaving an interactive wizard — exits 0, matching the entry
|
|
84
|
+
* menu's "Exit", a project cancellation, and a confirmed Ctrl+C.
|
|
85
|
+
*/
|
|
86
|
+
/** @internal */
|
|
87
|
+
export function setupTerminalOutcome(input) {
|
|
88
|
+
const annotation = setupCommandOutcomeAnnotation(input);
|
|
89
|
+
return { exitCode: annotation.outcome === 'error' ? 1 : 0, annotation };
|
|
90
|
+
}
|
|
91
|
+
async function annotateSetupCommandOutcome(annotation) {
|
|
92
|
+
const { annotateCommandOutcome } = await import('./telemetry/index.js');
|
|
93
|
+
annotateCommandOutcome(annotation);
|
|
94
|
+
}
|
|
39
95
|
async function recordSetupStep(input) {
|
|
40
96
|
const { emitTelemetryEvent } = await import('./telemetry/index.js');
|
|
41
97
|
await emitTelemetryEvent({
|
|
@@ -56,16 +112,16 @@ async function runKtxSetupEntryMenu(status, deps = {}) {
|
|
|
56
112
|
const options = status.project.ready
|
|
57
113
|
? [
|
|
58
114
|
{ value: 'setup', label: 'Resume or change an existing setup' },
|
|
59
|
-
{ value: 'new-project', label: 'Create a new
|
|
60
|
-
{ value: 'agents', label: 'Connect a coding agent to
|
|
115
|
+
{ value: 'new-project', label: 'Create a new ktx project' },
|
|
116
|
+
{ value: 'agents', label: 'Connect a coding agent to ktx' },
|
|
61
117
|
{ value: 'status', label: 'Check setup status' },
|
|
62
|
-
{ value: 'demo', label: 'Explore a pre-built
|
|
118
|
+
{ value: 'demo', label: 'Explore a pre-built ktx project' },
|
|
63
119
|
{ value: 'exit', label: 'Exit' },
|
|
64
120
|
]
|
|
65
121
|
: [
|
|
66
|
-
{ value: 'setup', label: 'Set up
|
|
122
|
+
{ value: 'setup', label: 'Set up ktx for my data' },
|
|
67
123
|
{ value: 'status', label: 'Check setup status' },
|
|
68
|
-
{ value: 'demo', label: 'Explore a pre-built
|
|
124
|
+
{ value: 'demo', label: 'Explore a pre-built ktx project' },
|
|
69
125
|
{ value: 'exit', label: 'Exit' },
|
|
70
126
|
];
|
|
71
127
|
const action = (await prompts.select({
|
|
@@ -226,16 +282,16 @@ function formatContextBuilt(status) {
|
|
|
226
282
|
export function formatKtxSetupStatus(status) {
|
|
227
283
|
if (!status.project.ready) {
|
|
228
284
|
return [
|
|
229
|
-
`No
|
|
285
|
+
`No ktx project found at ${status.project.path}.`,
|
|
230
286
|
'',
|
|
231
287
|
'Check another project: ktx --project-dir <folder> status',
|
|
232
288
|
'Or from that folder: ktx status',
|
|
233
|
-
'Create a new
|
|
289
|
+
'Create a new ktx project here: ktx setup',
|
|
234
290
|
'',
|
|
235
291
|
].join('\n');
|
|
236
292
|
}
|
|
237
293
|
const lines = [
|
|
238
|
-
`
|
|
294
|
+
`ktx project: ${status.project.path}`,
|
|
239
295
|
`Project ready: ${formatReady(status.project.ready)}`,
|
|
240
296
|
`LLM ready: ${formatReady(status.llm.ready)}${status.llm.model ? ` (${status.llm.model})` : ''}`,
|
|
241
297
|
`Embeddings ready: ${formatReady(status.embeddings.ready)}${status.embeddings.model ? ` (${status.embeddings.model})` : ''}`,
|
|
@@ -246,7 +302,7 @@ export function formatKtxSetupStatus(status) {
|
|
|
246
302
|
`Runtime ready: ${formatReady(status.runtime.ready)}${status.runtime.features.length > 0 ? ` (${status.runtime.features.join(', ')})` : ''}`,
|
|
247
303
|
]
|
|
248
304
|
: []),
|
|
249
|
-
`
|
|
305
|
+
`ktx context built: ${formatContextBuilt(status.context)}`,
|
|
250
306
|
`Agent integration ready: ${formatReady(status.agents.some((agent) => agent.ready))}${status.agents.length > 0 ? ` (${status.agents.map((agent) => `${agent.target}:${agent.scope}`).join(', ')})` : ''}`,
|
|
251
307
|
];
|
|
252
308
|
if (!status.context.ready && status.context.status === 'failed' && status.context.detail) {
|
|
@@ -271,7 +327,7 @@ export function formatKtxSetupCompletionSummary(status, options = {}) {
|
|
|
271
327
|
lines.push('', 'REQUIRED BEFORE USING AGENTS', '', ...agentNextActions.split('\n').map((line) => (line ? ` ${line}` : '')));
|
|
272
328
|
}
|
|
273
329
|
lines.push('', agentNextActions ? 'After that, try' : 'Try it');
|
|
274
|
-
lines.push(' Ask your agent: "Use
|
|
330
|
+
lines.push(' Ask your agent: "Use ktx to show me the available tables."');
|
|
275
331
|
return lines.join('\n');
|
|
276
332
|
}
|
|
277
333
|
function setupStatusReady(status) {
|
|
@@ -301,7 +357,7 @@ function setupRuntimeInstallPolicy(args) {
|
|
|
301
357
|
}
|
|
302
358
|
async function commitSetupConfigChanges(projectDir) {
|
|
303
359
|
const project = await loadKtxProject({ projectDir });
|
|
304
|
-
await project.git.commitFile('ktx.yaml', 'setup: update
|
|
360
|
+
await project.git.commitFile('ktx.yaml', 'setup: update ktx project config', 'ktx setup', 'setup@ktx.local');
|
|
305
361
|
}
|
|
306
362
|
export async function runKtxSetup(args, io, deps = {}) {
|
|
307
363
|
try {
|
|
@@ -316,7 +372,7 @@ export async function runKtxSetup(args, io, deps = {}) {
|
|
|
316
372
|
}
|
|
317
373
|
async function runKtxSetupInner(args, io, deps = {}) {
|
|
318
374
|
const setupUi = deps.setupUi ?? createKtxSetupUiAdapter();
|
|
319
|
-
setupUi.intro('
|
|
375
|
+
setupUi.intro('ktx setup', io);
|
|
320
376
|
setupUi.note(KTX_DOCS_URL, '📚 Docs', io);
|
|
321
377
|
let entryAction;
|
|
322
378
|
let projectResult;
|
|
@@ -325,6 +381,10 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
325
381
|
args.inputMode !== 'disabled' &&
|
|
326
382
|
!args.agents &&
|
|
327
383
|
(io.stdout.isTTY === true || deps.entryMenuDeps?.prompts !== undefined);
|
|
384
|
+
// A prompt is only possible when input is enabled AND a TTY is attached. A
|
|
385
|
+
// piped/CI `ktx setup` without `--no-input` is still `inputMode: 'auto'` but
|
|
386
|
+
// cannot prompt, so its `missing-input` is an automation error, not an abort.
|
|
387
|
+
const interactive = args.inputMode !== 'disabled' && io.stdout.isTTY === true;
|
|
328
388
|
setupLoop: while (true) {
|
|
329
389
|
entryAction = undefined;
|
|
330
390
|
if (canShowEntryMenu) {
|
|
@@ -363,7 +423,13 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
363
423
|
continue;
|
|
364
424
|
}
|
|
365
425
|
if (projectResult.status !== 'ready') {
|
|
366
|
-
|
|
426
|
+
const terminal = setupTerminalOutcome({
|
|
427
|
+
status: projectResult.status,
|
|
428
|
+
step: 'project',
|
|
429
|
+
interactive,
|
|
430
|
+
});
|
|
431
|
+
await annotateSetupCommandOutcome(terminal.annotation);
|
|
432
|
+
return terminal.exitCode;
|
|
367
433
|
}
|
|
368
434
|
const agentsRequested = args.agents || entryAction === 'agents';
|
|
369
435
|
const currentStatus = await readKtxSetupStatus(projectResult.projectDir, { cliVersion: args.cliVersion });
|
|
@@ -576,11 +642,15 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
576
642
|
cliVersion: args.cliVersion,
|
|
577
643
|
...(stepResult.errorDetail ? { errorDetail: stepResult.errorDetail } : {}),
|
|
578
644
|
});
|
|
579
|
-
if (stepResult.status === 'failed') {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
645
|
+
if (stepResult.status === 'failed' || stepResult.status === 'missing-input') {
|
|
646
|
+
const terminal = setupTerminalOutcome({
|
|
647
|
+
status: stepResult.status,
|
|
648
|
+
step,
|
|
649
|
+
interactive,
|
|
650
|
+
...(stepResult.errorDetail ? { errorDetail: stepResult.errorDetail } : {}),
|
|
651
|
+
});
|
|
652
|
+
await annotateSetupCommandOutcome(terminal.annotation);
|
|
653
|
+
return terminal.exitCode;
|
|
584
654
|
}
|
|
585
655
|
if (stepResult.status === 'back') {
|
|
586
656
|
const previousIndex = previousNavigableStepIndex(stepIndex);
|
|
@@ -614,7 +684,7 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
614
684
|
const focusedOnAgents = args.agents || entryAction === 'agents';
|
|
615
685
|
if (!focusedOnAgents) {
|
|
616
686
|
if (shouldPrintConciseReadySummary(status)) {
|
|
617
|
-
setupUi.note(formatKtxSetupCompletionSummary(status, { agentNextActions }), agentNextActions ? 'Finish
|
|
687
|
+
setupUi.note(formatKtxSetupCompletionSummary(status, { agentNextActions }), agentNextActions ? 'Finish ktx agent setup' : 'ktx project ready', io, {
|
|
618
688
|
format: (line) => line,
|
|
619
689
|
});
|
|
620
690
|
}
|
|
@@ -630,5 +700,6 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
630
700
|
}).join('\n'), 'What you can do next', io);
|
|
631
701
|
}
|
|
632
702
|
}
|
|
703
|
+
setupUi.note(SLACK_SETUP_NOTE.body, SLACK_SETUP_NOTE.title, io);
|
|
633
704
|
return 0;
|
|
634
705
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ktx-analytics
|
|
3
|
-
description: Use when answering a question that needs data from a
|
|
3
|
+
description: Use when answering a question that needs data from a ktx-connected database - investigating, analyzing, "how many", "show me", "what's the breakdown of", finding records by value, exploring tables, comparing periods, explaining metrics, or any data-analysis request. Triggers even when the user does not say "analytics"; if the answer requires querying a configured ktx connection, this skill applies.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# ktx Analytics Workflow
|
|
7
7
|
|
|
8
|
-
You have access to
|
|
8
|
+
You have access to ktx MCP tools for data discovery, semantic-layer analysis, raw read-only SQL, wiki context, and memory ingest. Follow this workflow.
|
|
9
9
|
|
|
10
10
|
<workflow>
|
|
11
11
|
1. **Discover** - call `discover_data` first to see what exists across wiki pages, semantic-layer sources, metrics, dimensions, raw tables, and columns. Returns refs only.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dbt_ingest
|
|
3
|
-
description: Map dbt `schema.yml` / `properties.yml` models and sources into
|
|
3
|
+
description: Map dbt `schema.yml` / `properties.yml` models and sources into ktx semantic-layer overlays and column notes. Covers `sources:` vs `models:`, column `data_tests` (not_null, unique, accepted_values, relationships), and how bundle-time writes complement manifest backfill from git sync. Load when the WorkUnit's `skillNames` includes `dbt_ingest` or when raw files are dbt YAML under `models/` / `sources/`.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# dbt →
|
|
7
|
+
# dbt → ktx (bundle ingest)
|
|
8
8
|
|
|
9
9
|
Use this skill for **uploaded** dbt projects (`dbt_project.yml` at stage root, `models/**`, `sources/**`, `schema.yml`). There is **no** `fetch()` in v1 - scheduled `dbt parse` / `manifest.json` pulls are out of scope; host-provided dbt sync may still backfill structured test metadata into `_schema` on the next sync.
|
|
10
10
|
|
|
11
11
|
## Mapping (models / sources → SL)
|
|
12
12
|
|
|
13
|
-
| dbt |
|
|
13
|
+
| dbt | ktx | Notes |
|
|
14
14
|
|-----|--------|--------|
|
|
15
15
|
| `models:` entry with `columns:` | **Overlay** on the manifest table with the same name (after `discover_data` / `entity_details`) | One SL source per physical table; model name may differ from DB name - resolve with `read_raw_file` + warehouse context. |
|
|
16
16
|
| `sources:` → `tables:` | Same as models; use `identifier` when present instead of logical `name`. | Schema + name must match how the connection sees tables. |
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: looker_ingest
|
|
3
|
-
description: Extract durable
|
|
3
|
+
description: Extract durable ktx knowledge and semantic-layer contribution proposals from staged Looker runtime dashboard, Look, and explore JSON. Load for WorkUnits whose raw files are under explores/, dashboards/, or looks/.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Looker Runtime Ingest
|
|
8
8
|
|
|
9
|
-
Looker runtime ingest turns API-staged dashboards, Looks, and explores into durable
|
|
9
|
+
Looker runtime ingest turns API-staged dashboards, Looks, and explores into durable ktx memory. Runtime entities are evidence. They are not themselves the final knowledge shape.
|
|
10
10
|
|
|
11
11
|
## Required Workflow
|
|
12
12
|
|
|
@@ -103,7 +103,7 @@ The staged explore file carries warehouse target fields populated before the WU
|
|
|
103
103
|
- `rawSqlTableName`: Looker's verbatim `sql_table_name`. Keep it as provenance only.
|
|
104
104
|
- `targetTable`: the parsed target-table union. Use this as the sole branch condition.
|
|
105
105
|
|
|
106
|
-
When `targetTable.ok === true`, the explore has a complete
|
|
106
|
+
When `targetTable.ok === true`, the explore has a complete ktx backing target. Before writing:
|
|
107
107
|
|
|
108
108
|
1. Use `targetTable.catalog`, `targetTable.schema`, and `targetTable.name` for `source_tables` preflight matching through `sl_discover` or `sl_read_source`.
|
|
109
109
|
2. Use Looker field `sql`, labels, descriptions, and type metadata to derive source columns, measures, segments, joins, and grain.
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lookml_ingest
|
|
3
|
-
description: Map a LookML view/model/explore into
|
|
3
|
+
description: Map a LookML view/model/explore into ktx semantic layer sources. Covers the LookML to ktx primitive table, provenance tagging, and three worked examples (overlay, standalone from derived_table, standalone with sql_always_where). Load when the turn contains `.lkml` content.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# LookML to
|
|
7
|
+
# LookML to ktx Semantic Layer
|
|
8
8
|
|
|
9
9
|
LookML views map to SL sources, `measure:` to measures, `explore: { join: }` to the join graph. This skill lays out the mapping and the three capture shapes.
|
|
10
10
|
|
|
11
11
|
## Mapping table
|
|
12
12
|
|
|
13
|
-
| LookML |
|
|
13
|
+
| LookML | ktx form | Notes |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| `view: X { sql_table_name: …; measure:/dimension:/join: }` | **Overlay**
|
|
15
|
+
| `view: X { sql_table_name: …; measure:/dimension:/join: }` | **Overlay** named `X` with `measures`, computed-only `columns`, `column_overrides`, `joins`, `segments` | Manifest-backed; inherit grain/columns |
|
|
16
16
|
| `view: X { derived_table: { sql: … } }` | **Standalone** with top-level `sql:`, explicit `grain:` + `columns:` | No manifest entry exists |
|
|
17
17
|
| `view: X { sql_always_where: <p> }` | **Standalone** with `sql: SELECT * FROM <base> WHERE <p>` | Enforcement, not opt-in |
|
|
18
18
|
| `explore: { join: Y { sql_on: …; relationship: … } }` | `joins:` entry `{ to: Y, on: "<local> = Y.<col>", relationship: … }` | On the overlay or standalone |
|
|
@@ -23,7 +23,7 @@ Type map: `date`/`datetime`/`timestamp` → `time`; `yesno` → `boolean`; `numb
|
|
|
23
23
|
|
|
24
24
|
## Decision rules
|
|
25
25
|
|
|
26
|
-
LookML writes target the run connection directly. Unlike Looker runtime ingestion, the LookML adapter is configured on the warehouse
|
|
26
|
+
LookML writes target the run connection directly. Unlike Looker runtime ingestion, the LookML adapter is configured on the warehouse ktx connection, so do not look for `targetWarehouseConnectionId` and do not route through a mapping array.
|
|
27
27
|
|
|
28
28
|
Before any SL write, inspect the WorkUnit notes.
|
|
29
29
|
|
|
@@ -94,7 +94,7 @@ SL source, `tables:` frontmatter, `sl_refs`, or `emit_unmapped_fallback`:
|
|
|
94
94
|
schema or dataset, and table from the WorkUnit evidence.
|
|
95
95
|
3. Use only those names in `sql:`, `columns:`, and `grain:`. Map each `dimension_group` to ONE `{ name: <physical_col>, type: time, role: time }` entry - never one per timeframe.
|
|
96
96
|
|
|
97
|
-
| LookML input |
|
|
97
|
+
| LookML input | ktx `columns:` entry |
|
|
98
98
|
|---|---|
|
|
99
99
|
| `dimension_group: month { type: time; timeframes: [month]; sql: ${TABLE}.month_date ;; }` | `{ name: month_date, type: time, role: time }` |
|
|
100
100
|
| `dimension_group: date { type: time; timeframes: [raw, date, week, month]; sql: ${TABLE}.date ;; }` | `{ name: date, type: time, role: time }` - single entry, NOT `date_raw`/`date_date`/`date_week` |
|
|
@@ -132,7 +132,7 @@ explore: fct_labs {
|
|
|
132
132
|
}
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
ktx overlay at `<connId>/fct_labs.yaml`:
|
|
136
136
|
|
|
137
137
|
```yaml
|
|
138
138
|
name: fct_labs
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: metabase_ingest
|
|
3
|
-
description: Convert Metabase questions, models, and metrics into
|
|
3
|
+
description: Convert Metabase questions, models, and metrics into ktx Semantic Layer source definitions. Covers result-metadata to KSL column type mapping, FK/PK detection, near-duplicate deduplication, pre-aggregation decomposition, join-graph connectivity, and how to react to priorProvenance from earlier ingest syncs. Load when the WorkUnit contains `cards/<id>.json` files under a Metabase bundle.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Metabase to
|
|
7
|
+
# Metabase to ktx Semantic Layer
|
|
8
8
|
|
|
9
|
-
Each WorkUnit represents one Metabase collection's cards for one Metabase database (mapped to exactly one
|
|
9
|
+
Each WorkUnit represents one Metabase collection's cards for one Metabase database (mapped to exactly one ktx connection). Every `cards/<id>.json` file carries the resolved SQL, result_metadata, card type, collection path, and referenced-card ids. The WU's `sync-config.json` tells you which sync mode is active and which selections apply. `databases/<id>.json` tells you the target ktx connection.
|
|
10
10
|
|
|
11
11
|
## Context format
|
|
12
12
|
|
|
@@ -100,7 +100,7 @@ measures:
|
|
|
100
100
|
|
|
101
101
|
Overlay shape: `name:` plus any of `measures:`, `segments:`, `descriptions:`, `joins:`, `disable_joins:`, `exclude_columns:`, `column_overrides:`, or computed-only `columns:` entries with `expr` + `type`. Never include `sql:`, `table:`, `grain:`, or base-table `columns:` on a manifest-backed name — those would shadow the manifest's schema and drop its joins. Use `column_overrides:` for inherited column descriptions. Overlay `joins:` are merged additively with the manifest's joins (deduped by `to` + `on`); use `disable_joins: ["<on-clause>"]` to suppress a specific manifest join. After the overlay exists, use `sl_edit_source` for further tweaks. See `sl_capture` skill for the canonical overlay rule.
|
|
102
102
|
|
|
103
|
-
**Join discovery:** When your card's SQL references warehouse tables (e.g. in `FROM` or `JOIN` clauses), call `sl_discover({ query: '<table>' })` before writing. The matching manifest entry's `name` is the value you use in `joins: [- to: <name>]` only when the card output exposes a local key that matches the target source grain (for example `account_id = mart_account_segments.account_id`). Do not declare a
|
|
103
|
+
**Join discovery:** When your card's SQL references warehouse tables (e.g. in `FROM` or `JOIN` clauses), call `sl_discover({ query: '<table>' })` before writing. The matching manifest entry's `name` is the value you use in `joins: [- to: <name>]` only when the card output exposes a local key that matches the target source grain (for example `account_id = mart_account_segments.account_id`). Do not declare a ktx join just because the card SQL joins that table internally. If the output only exposes display fields such as `account_name`, keep the SQL source self-contained or project the key before adding the join. Use `many_to_one` for FK-to-dimension joins, `one_to_many` for the reverse.
|
|
104
104
|
|
|
105
105
|
**Hard rule on join columns (prevents broken joins):** For every join you declare, the local column on the left of `on:` MUST be (a) present in your source's projected output and (b) a key/ID column, never a display value. If the natural FK isn't in your SELECT, add it to SELECT before declaring the join. Joining `account_name = mart_account_segments.account_id` is always wrong - names are not identifiers and the equality produces zero matches. The validator rejects this with a "display value to identifier" error; the tool will refuse to save it. Add `account_id` to your SELECT and join on `account_id = mart_account_segments.account_id`, or omit the join entirely.
|
|
106
106
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: metricflow_ingest
|
|
3
|
-
description: Map a MetricFlow semantic_model or metric into
|
|
3
|
+
description: Map a MetricFlow semantic_model or metric into ktx semantic layer sources. Covers the MetricFlow to ktx primitive table, `extends:` inheritance flattening, metric-type handling (simple / derived / ratio / cumulative / conversion), `model: ref('x')` resolution, and four worked examples. Load when the turn contains `.yml`/`.yaml` files with top-level `semantic_models:` or `metrics:`.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# MetricFlow to
|
|
7
|
+
# MetricFlow to ktx Semantic Layer
|
|
8
8
|
|
|
9
|
-
A MetricFlow `semantic_model` maps to an SL source; MetricFlow `measures` map to
|
|
9
|
+
A MetricFlow `semantic_model` maps to an SL source; MetricFlow `measures` map to ktx measures; MetricFlow `entities` map to ktx `joins`; MetricFlow `metrics` (top-level) map to ktx measures OR to cross-model derived measures. Files in one WorkUnit are ALWAYS part of the same logical entity (a connected component, possibly spanning `extends:` + cross-model metric refs). Flatten inheritance and cross-file references at write time.
|
|
10
10
|
|
|
11
11
|
## Mapping table
|
|
12
12
|
|
|
13
|
-
| MetricFlow |
|
|
13
|
+
| MetricFlow | ktx form | Notes |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| `semantic_model: X { model: ref('t') }` with measures + dimensions | **Overlay**
|
|
16
|
-
| `semantic_model: X { model: source('s','t') }` | **Overlay**
|
|
15
|
+
| `semantic_model: X { model: ref('t') }` with measures + dimensions | **Overlay** named `X` with `measures`, computed-only `columns`, `column_overrides`, `joins` | The `model:` ref resolves to a manifest table. |
|
|
16
|
+
| `semantic_model: X { model: source('s','t') }` | **Overlay** named `X` over table `t`. | Same shape; `source()` still resolves to a physical table. |
|
|
17
17
|
| `semantic_model: X { model: <literal> }` with no manifest entry | **Standalone** with explicit `sql:`, `grain:`, `columns:` | Happens when the dbt manifest isn't available. |
|
|
18
18
|
| `semantic_model: Y { extends: X }` | **Merge** Y's measures/dimensions/entities into X's overlay, or write a single overlay named for the most-derived child (Y) containing both X's and Y's primitives | Do not emit a second overlay for X - flatten. |
|
|
19
19
|
| `measures: [{ name, agg, expr }]` | `measures: [{ name, expr: "<agg>(<expr>)" }]` | Aggregation inlined. `agg: count_distinct` → `count(distinct ...)`. |
|
|
@@ -23,11 +23,11 @@ A MetricFlow `semantic_model` maps to an SL source; MetricFlow `measures` map to
|
|
|
23
23
|
| `metrics: [{ type: simple, filter: <jinja> }]` | **New measure** on the same source, with the filter translated to SQL and attached via `filter:` | Translate Jinja `{{ Dimension('x__y') }}` to the column name `y`. |
|
|
24
24
|
| `metrics: [{ type: derived, type_params: { expr, metrics } }]` | **Derived measure** on whichever source owns the referenced measures, with `expr:` referencing measure names | If the metric spans models, still write it once on the source owning the "primary" measure (the one the agent judges most central). Mention the cross-model chain in the description. |
|
|
25
25
|
| `metrics: [{ type: ratio, type_params: { numerator, denominator } }]` | Same as derived; `expr: "numerator / NULLIF(denominator, 0)"` if no explicit expr | Safe-division by default. |
|
|
26
|
-
| `metrics: [{ type: cumulative, type_params: { window, grain_to_date } }]` | **Standalone** source with a window-function SQL; reference the resulting column as a normal measure |
|
|
27
|
-
| `metrics: [{ type: conversion }]` | **Flag for human** - do NOT write. Emit a wiki note describing the intended semantics. | No
|
|
26
|
+
| `metrics: [{ type: cumulative, type_params: { window, grain_to_date } }]` | **Standalone** source with a window-function SQL; reference the resulting column as a normal measure | ktx SL has no first-class cumulative primitive (spec Non-goals). |
|
|
27
|
+
| `metrics: [{ type: conversion }]` | **Flag for human** - do NOT write. Emit a wiki note describing the intended semantics. | No ktx equivalent in v1. |
|
|
28
28
|
| Metric not mappable | Wiki page `<metric_name>-definition.md` with the full YAML body quoted | Capture the intent even if we can't emit SL. |
|
|
29
29
|
|
|
30
|
-
Type map: MetricFlow `time` to
|
|
30
|
+
Type map: MetricFlow `time` to ktx `time`; `categorical` to `string`; `number` to `number`; `boolean` to `boolean`. Follow `expr` over `name` when both differ - `expr` is the physical column.
|
|
31
31
|
|
|
32
32
|
Verify each MetricFlow model source table with entity_details before producing
|
|
33
33
|
the corresponding sl_write_source.
|
|
@@ -92,7 +92,7 @@ After every `sl_write_source`, call `sl_validate`. The warehouse will reject inv
|
|
|
92
92
|
|
|
93
93
|
## Cumulative metrics - sql-standalone fallback
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
ktx SL has no first-class `window:` or `grain_to_date:` primitive in v1 (spec Non-goals). Translate a MetricFlow cumulative metric to a standalone SL source with a window-function SQL:
|
|
96
96
|
|
|
97
97
|
```yaml
|
|
98
98
|
# MetricFlow input:
|
|
@@ -105,7 +105,7 @@ metrics:
|
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
```yaml
|
|
108
|
-
#
|
|
108
|
+
# ktx standalone output:
|
|
109
109
|
name: cum_revenue_7d
|
|
110
110
|
source_type: sql
|
|
111
111
|
sql: |
|
|
@@ -143,7 +143,7 @@ Do NOT emit SL for this. Instead:
|
|
|
143
143
|
- Write a wiki page at `wiki/global/<metric_name>-intent.md` quoting the full YAML body and a one-line explanation of the intended semantics (base event → conversion event within window).
|
|
144
144
|
- Call `emit_unmapped_fallback` with `rawPath` set to the MetricFlow file path, `reason: "conversion_metric_unsupported"`, and `fallback: "flagged"`.
|
|
145
145
|
|
|
146
|
-
When
|
|
146
|
+
When ktx SL gains conversion primitives, re-ingesting will find the prior wiki note (via `priorProvenance`) and replace it with an SL source.
|
|
147
147
|
|
|
148
148
|
## Provenance markers
|
|
149
149
|
|
|
@@ -174,7 +174,7 @@ semantic_models:
|
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
```yaml
|
|
177
|
-
#
|
|
177
|
+
# ktx overlay at <connId>/orders.yaml:
|
|
178
178
|
# <!-- from: raw-sources/.../models/orders.yml#L1-10 -->
|
|
179
179
|
name: orders
|
|
180
180
|
descriptions:
|
|
@@ -217,7 +217,7 @@ metrics:
|
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
```yaml
|
|
220
|
-
#
|
|
220
|
+
# ktx overlay at <connId>/orders_ext.yaml (one file; inheritance flattened):
|
|
221
221
|
# <!-- from: raw-sources/.../models/orders.yml#L1-10 -->
|
|
222
222
|
# <!-- from: raw-sources/.../models/orders_ext.yml#L1-8 -->
|
|
223
223
|
# <!-- from: raw-sources/.../metrics/orders_final.yml#L1-10 -->
|
|
@@ -256,7 +256,7 @@ metrics:
|
|
|
256
256
|
metrics: [{name: revenue}, {name: cost}]
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
-
Because the WorkUnit bundles all three files (cross-component union via the metric), write the derived measure on ONE of the two sources - pick the source whose domain "owns" the metric (here, `sales` - margin is inherently a sales metric). Cross-source references aren't native in
|
|
259
|
+
Because the WorkUnit bundles all three files (cross-component union via the metric), write the derived measure on ONE of the two sources - pick the source whose domain "owns" the metric (here, `sales` - margin is inherently a sales metric). Cross-source references aren't native in ktx SL; treat the metric's operands as already-resolvable in the target source's query context OR emit a standalone SQL that joins the two tables:
|
|
260
260
|
|
|
261
261
|
```yaml
|
|
262
262
|
# <connId>/sales.yaml
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: notion_synthesize
|
|
3
|
-
description: Synthesize durable
|
|
3
|
+
description: Synthesize durable ktx wiki pages and semantic-layer sources from staged Notion pages, databases, data-source rows, and clustered Notion evidence. Load when a WorkUnit contains Notion raw files or Notion evidence chunks.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
package/dist/skills/sl/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sl
|
|
3
|
-
description:
|
|
3
|
+
description: ktx's semantic layer - a structured catalog of sources (tables/views), measures, joins, and segments expressed as YAML. Covers the schema and how to query it via `sl_query`. Use when the task involves querying pre-defined metrics (ARR, churn, retention, LTV, MAU) or reading SL source YAML to understand the catalog. Capture is handled by the `sl_capture` skill (memory-agent only).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Semantic Layer
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
ktx's semantic layer (SL) is a structured catalog. Each **source** represents a table, a SQL view, or an overlay that enriches a manifest-backed table with measures, computed columns, joins, and named segments. The catalog is the single source of truth for reusable business metrics.
|
|
9
9
|
|
|
10
10
|
This skill covers two parts:
|
|
11
11
|
- **Part 1** - Schema reference (what an SL source looks like).
|
|
@@ -21,7 +21,7 @@ skills must verify warehouse identifiers with `discover_data`,
|
|
|
21
21
|
|
|
22
22
|
## Part 1 - Schema reference
|
|
23
23
|
|
|
24
|
-
An SL source is a YAML file
|
|
24
|
+
An SL source is a YAML file under `semantic-layer/<connectionId>/`. The file's `name:` field is the source's identity — it mirrors the warehouse identifier verbatim (e.g. Snowflake's uppercase `SIGNED_UP`); the filename is only a derived label. Always address sources by name through the `sl_*` tools, never by file path. There are three flavors:
|
|
25
25
|
|
|
26
26
|
### Overlay sources
|
|
27
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sl_capture
|
|
3
|
-
description: How to capture new reusable patterns into
|
|
3
|
+
description: How to capture new reusable patterns into ktx's semantic layer - when a measure, segment, or join belongs in the catalog and how to write it generically so it stays small and useful over time. Loaded by the post-turn memory-agent only. The research agent does not write to the SL.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wiki_capture
|
|
3
|
-
description:
|
|
3
|
+
description: ktx's knowledge base - wiki pages for durable, reusable business knowledge. Covers capture workflow for user preferences, metric definitions, organizational conventions, and cross-references between wiki pages and semantic-layer sources. Loaded by the post-turn memory-agent only. The research agent reads wiki via `wiki_read`/`wiki_search` but does not write it.
|
|
4
4
|
callers: [memory_agent]
|
|
5
5
|
---
|
|
6
6
|
|
package/dist/source-mapping.js
CHANGED
|
@@ -23,7 +23,7 @@ async function createDefaultLookerClient(project, connectionId) {
|
|
|
23
23
|
return lookerCredentialsFromLocalConnection(lookerConnectionId, project.config.connections[lookerConnectionId]);
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
|
-
return factory.
|
|
26
|
+
return factory.createLookerClient(connectionId);
|
|
27
27
|
}
|
|
28
28
|
function isLookerConnection(project, connectionId) {
|
|
29
29
|
return String(project.config.connections[connectionId]?.driver ?? '').toLowerCase() === 'looker';
|
package/dist/startup-profile.js
CHANGED
|
@@ -28,7 +28,7 @@ export function installStartupProfileReporter() {
|
|
|
28
28
|
}
|
|
29
29
|
process.once('beforeExit', () => {
|
|
30
30
|
const total = now();
|
|
31
|
-
process.stderr.write('\
|
|
31
|
+
process.stderr.write('\nktx startup profile\n');
|
|
32
32
|
for (const event of events) {
|
|
33
33
|
const elapsed = event.at.toFixed(1).padStart(7);
|
|
34
34
|
if (event.duration === undefined) {
|
package/dist/status-project.d.ts
CHANGED
|
@@ -40,7 +40,6 @@ interface PipelineStatus {
|
|
|
40
40
|
interface StorageStatus {
|
|
41
41
|
state: string;
|
|
42
42
|
search: string;
|
|
43
|
-
gitAutoCommit: boolean;
|
|
44
43
|
gitAuthor: string;
|
|
45
44
|
}
|
|
46
45
|
interface ConfigStatus {
|
|
@@ -133,7 +132,6 @@ export interface ProjectStatus {
|
|
|
133
132
|
maxConcurrency: number;
|
|
134
133
|
failureMode: string;
|
|
135
134
|
};
|
|
136
|
-
memoryAutoCommit: boolean;
|
|
137
135
|
relationshipsDetail?: {
|
|
138
136
|
acceptThreshold: number;
|
|
139
137
|
reviewThreshold: number;
|
package/dist/status-project.js
CHANGED
|
@@ -384,7 +384,6 @@ function buildStorageStatus(config) {
|
|
|
384
384
|
return {
|
|
385
385
|
state: config.storage.state,
|
|
386
386
|
search: config.storage.search,
|
|
387
|
-
gitAutoCommit: config.storage.git.auto_commit,
|
|
388
387
|
gitAuthor: config.storage.git.author,
|
|
389
388
|
};
|
|
390
389
|
}
|
|
@@ -498,9 +497,10 @@ function buildConfigStatus(issues) {
|
|
|
498
497
|
if (list.length === 0) {
|
|
499
498
|
return { status: 'ok', detail: 'ktx.yaml schema valid', issues: [] };
|
|
500
499
|
}
|
|
500
|
+
// Error-severity issues never reach here — the doctor exits on them first.
|
|
501
501
|
return {
|
|
502
502
|
status: 'warn',
|
|
503
|
-
detail:
|
|
503
|
+
detail: `ktx.yaml schema valid · ${list.length} ignored field${list.length === 1 ? '' : 's'}`,
|
|
504
504
|
issues: list,
|
|
505
505
|
};
|
|
506
506
|
}
|
|
@@ -713,7 +713,6 @@ export async function buildProjectStatus(project, options = {}) {
|
|
|
713
713
|
maxConcurrency: config.ingest.workUnits.maxConcurrency,
|
|
714
714
|
failureMode: config.ingest.workUnits.failureMode,
|
|
715
715
|
},
|
|
716
|
-
memoryAutoCommit: config.memory.auto_commit,
|
|
717
716
|
relationshipsDetail: {
|
|
718
717
|
acceptThreshold: config.scan.relationships.acceptThreshold,
|
|
719
718
|
reviewThreshold: config.scan.relationships.reviewThreshold,
|
|
@@ -840,7 +839,7 @@ export function renderProjectStatus(status, options = {}) {
|
|
|
840
839
|
const sym = (level) => color(level, SYMBOL[level]);
|
|
841
840
|
const lines = [];
|
|
842
841
|
const dirStr = abbreviateHome(status.projectDir, env);
|
|
843
|
-
lines.push(`${bold('
|
|
842
|
+
lines.push(`${bold('ktx status')} ${dim('·')} ${status.projectName} ${dim(`(${dirStr})`)}`);
|
|
844
843
|
lines.push('');
|
|
845
844
|
const labelPad = 'Connections'.length;
|
|
846
845
|
const label = (text) => text.padEnd(labelPad);
|
|
@@ -960,8 +959,7 @@ export function renderProjectStatus(status, options = {}) {
|
|
|
960
959
|
lines.push(` ${bold('Relationships')} ${dim(`accept=${r.acceptThreshold}, review=${r.reviewThreshold}, maxLlmTables=${r.maxLlmTablesPerBatch}, concurrency=${r.validationConcurrency}`)}`);
|
|
961
960
|
}
|
|
962
961
|
lines.push(` ${bold('Agent')} ${dim(`max_iterations=${status.pipeline.agentMaxIterations}, tools=${status.pipeline.agentTools.join(', ') || '(none)'}`)}`);
|
|
963
|
-
lines.push(` ${bold('
|
|
964
|
-
lines.push(` ${bold('Git')} ${dim(`auto_commit=${status.storage.gitAutoCommit}, author=${status.storage.gitAuthor}`)}`);
|
|
962
|
+
lines.push(` ${bold('Git')} ${dim(`author=${status.storage.gitAuthor}`)}`);
|
|
965
963
|
lines.push('');
|
|
966
964
|
}
|
|
967
965
|
// Verdict + next steps
|
|
@@ -6,6 +6,9 @@ interface CommandSpan {
|
|
|
6
6
|
hasProject: boolean;
|
|
7
7
|
attachProjectGroup: boolean;
|
|
8
8
|
startedAt: number;
|
|
9
|
+
annotatedOutcome?: CommandOutcome;
|
|
10
|
+
annotatedErrorClass?: string;
|
|
11
|
+
annotatedErrorDetail?: string;
|
|
9
12
|
}
|
|
10
13
|
export interface CompletedCommandSpan {
|
|
11
14
|
commandPath: string[];
|
|
@@ -19,6 +22,27 @@ export interface CompletedCommandSpan {
|
|
|
19
22
|
projectGroupAttached: boolean;
|
|
20
23
|
}
|
|
21
24
|
export declare function beginCommandSpan(input: CommandSpan): void;
|
|
25
|
+
/**
|
|
26
|
+
* Let a command action record the true outcome and reason on the active span.
|
|
27
|
+
*
|
|
28
|
+
* The Commander wrapper can only derive an outcome from a thrown error or the
|
|
29
|
+
* process exit code, so a command that exits non-zero *without throwing* (e.g.
|
|
30
|
+
* `ktx setup` when the user abandons the wizard) lands as `outcome: 'error'`
|
|
31
|
+
* with no `errorClass`/`errorDetail` — an unactionable blank in the dashboard.
|
|
32
|
+
* The action is the decision-maker: it can mark the run `aborted`, or attach a
|
|
33
|
+
* scrubbed reason so the next occurrence is self-diagnosing. A later thrown
|
|
34
|
+
* error still wins (see {@link completeCommandSpan}), since that is the most
|
|
35
|
+
* authoritative signal and also feeds the `$exception` stream. No-ops when no
|
|
36
|
+
* span is active so call sites stay safe in tests and bare-help paths.
|
|
37
|
+
*
|
|
38
|
+
* Values are emitted verbatim and must already satisfy the telemetry privacy
|
|
39
|
+
* rules — pass synthetic or already-scrubbed strings, never raw user input.
|
|
40
|
+
*/
|
|
41
|
+
export declare function annotateCommandOutcome(input: {
|
|
42
|
+
outcome?: CommandOutcome;
|
|
43
|
+
errorClass?: string;
|
|
44
|
+
errorDetail?: string;
|
|
45
|
+
}): void;
|
|
22
46
|
export declare function completeCommandSpan(input: {
|
|
23
47
|
completedAt: number;
|
|
24
48
|
outcome: CommandOutcome;
|