@kaelio/ktx 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/kaelio_ktx-0.13.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 +3 -3
- package/dist/cli-runtime.js +2 -2
- 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 +19 -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/connection.js +15 -3
- package/dist/connectors/bigquery/connector.js +1 -14
- package/dist/connectors/clickhouse/connector.js +2 -16
- package/dist/connectors/duckdb/federated-attach.d.ts +7 -0
- package/dist/connectors/duckdb/federated-attach.js +86 -0
- package/dist/connectors/duckdb/federated-executor.d.ts +5 -0
- package/dist/connectors/duckdb/federated-executor.js +59 -0
- package/dist/connectors/mysql/connector.js +2 -16
- package/dist/connectors/postgres/connector.js +1 -14
- package/dist/connectors/shared/string-reference.d.ts +6 -0
- package/dist/connectors/shared/string-reference.js +19 -0
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/snowflake/connector.js +1 -14
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +4 -17
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/federation.d.ts +33 -0
- package/dist/context/connections/federation.js +51 -0
- package/dist/context/connections/local-warehouse-descriptor.d.ts +2 -0
- package/dist/context/connections/project-sql-executor.d.ts +18 -0
- package/dist/context/connections/project-sql-executor.js +39 -0
- package/dist/context/connections/query-executor.d.ts +2 -2
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +119 -4
- package/dist/context/connections/resolve-connection.d.ts +12 -0
- package/dist/context/connections/resolve-connection.js +37 -0
- package/dist/context/core/git-env.d.ts +4 -0
- package/dist/context/core/git-env.js +5 -1
- package/dist/context/core/git.service.d.ts +23 -0
- package/dist/context/core/git.service.js +71 -8
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- package/dist/context/ingest/adapters/live-database/manifest.d.ts +3 -0
- package/dist/context/ingest/adapters/live-database/manifest.js +19 -11
- 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 +19 -3
- 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 +33 -8
- package/dist/context/mcp/local-project-ports.js +63 -89
- package/dist/context/mcp/types.d.ts +2 -0
- 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 +33 -4
- 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 +31 -44
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +71 -70
- 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 +48 -0
- package/dist/context/sl/source-files.js +138 -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-query-executor.d.ts +2 -0
- package/dist/ingest-query-executor.js +8 -22
- 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 +8 -0
- package/dist/io/tty.js +16 -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.d.ts +21 -15
- package/dist/setup-agents.js +143 -66
- 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.d.ts +3 -0
- package/dist/setup-databases.js +47 -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 -1
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +28 -12
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +14 -13
- 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/sql.d.ts +2 -0
- package/dist/sql.js +35 -53
- 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/events.d.ts +3 -2
- package/dist/telemetry/events.js +11 -1
- package/dist/telemetry/exception.js +14 -0
- 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 +2 -1
- package/assets/python/kaelio_ktx-0.11.0-py3-none-any.whl +0 -0
package/dist/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ function writeJson(io, value) {
|
|
|
5
5
|
}
|
|
6
6
|
function writeInstallResult(io, result) {
|
|
7
7
|
const verb = result.status === 'ready' ? 'Using existing' : 'Installed';
|
|
8
|
-
io.stdout.write(`${verb}
|
|
8
|
+
io.stdout.write(`${verb} ktx Python runtime\n`);
|
|
9
9
|
io.stdout.write(`version: ${result.manifest.cliVersion}\n`);
|
|
10
10
|
io.stdout.write(`features: ${result.manifest.features.join(', ')}\n`);
|
|
11
11
|
io.stdout.write(`python: ${result.manifest.python.executable}\n`);
|
|
@@ -15,7 +15,7 @@ function writeInstallResult(io, result) {
|
|
|
15
15
|
}
|
|
16
16
|
function writeDaemonStart(io, result) {
|
|
17
17
|
const verb = result.status === 'reused' ? 'Using existing' : 'Started';
|
|
18
|
-
io.stdout.write(`${verb}
|
|
18
|
+
io.stdout.write(`${verb} ktx daemon\n`);
|
|
19
19
|
io.stdout.write(`url: ${result.baseUrl}\n`);
|
|
20
20
|
io.stdout.write(`pid: ${result.state.pid}\n`);
|
|
21
21
|
io.stdout.write(`version: ${result.state.version}\n`);
|
|
@@ -26,10 +26,10 @@ function writeDaemonStart(io, result) {
|
|
|
26
26
|
}
|
|
27
27
|
function writeDaemonStop(io, result) {
|
|
28
28
|
if (result.status === 'already-stopped') {
|
|
29
|
-
io.stdout.write('
|
|
29
|
+
io.stdout.write('ktx daemon already stopped\n');
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
io.stdout.write('Stopped
|
|
32
|
+
io.stdout.write('Stopped ktx daemon\n');
|
|
33
33
|
io.stdout.write(`pid: ${result.state?.pid ?? 'unknown'}\n`);
|
|
34
34
|
io.stdout.write(`state: ${result.layout.daemonStatePath}\n`);
|
|
35
35
|
}
|
|
@@ -42,11 +42,11 @@ function writeDaemonStopAll(io, result) {
|
|
|
42
42
|
result.stale.length === 0 &&
|
|
43
43
|
result.failed.length === 0 &&
|
|
44
44
|
result.scanErrors.length === 0) {
|
|
45
|
-
io.stdout.write('No
|
|
45
|
+
io.stdout.write('No ktx daemons found\n');
|
|
46
46
|
return 0;
|
|
47
47
|
}
|
|
48
48
|
if (failed === 0) {
|
|
49
|
-
io.stdout.write(`Stopped ${result.stopped.length}
|
|
49
|
+
io.stdout.write(`Stopped ${result.stopped.length} ktx daemons\n`);
|
|
50
50
|
if (result.stale.length > 0) {
|
|
51
51
|
io.stdout.write(`Cleaned ${result.stale.length} stale daemon states\n`);
|
|
52
52
|
}
|
|
@@ -58,7 +58,7 @@ function writeDaemonStopAll(io, result) {
|
|
|
58
58
|
}
|
|
59
59
|
return 0;
|
|
60
60
|
}
|
|
61
|
-
io.stderr.write(`Stopped ${result.stopped.length}
|
|
61
|
+
io.stderr.write(`Stopped ${result.stopped.length} ktx daemons; failed ${result.failed.length}${result.stale.length > 0 ? `; cleaned stale ${result.stale.length}` : ''}\n`);
|
|
62
62
|
for (const entry of result.failed) {
|
|
63
63
|
io.stderr.write(`pid: ${entry.pid} source: ${entry.source}${entry.url ? ` url: ${entry.url}` : ''}${entry.health ? ` health: ${entry.health}` : ''} detail: ${entry.detail}\n`);
|
|
64
64
|
}
|
|
@@ -68,7 +68,7 @@ function writeDaemonStopAll(io, result) {
|
|
|
68
68
|
return 1;
|
|
69
69
|
}
|
|
70
70
|
function writeStatus(io, status) {
|
|
71
|
-
io.stdout.write('
|
|
71
|
+
io.stdout.write('ktx Python runtime\n');
|
|
72
72
|
io.stdout.write(`status: ${status.kind}\n`);
|
|
73
73
|
io.stdout.write(`detail: ${status.detail}\n`);
|
|
74
74
|
io.stdout.write(`runtime root: ${status.layout.runtimeRoot}\n`);
|
|
@@ -80,7 +80,7 @@ function writeStatus(io, status) {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
function writeRuntimeChecks(io, checks) {
|
|
83
|
-
io.stdout.write('
|
|
83
|
+
io.stdout.write('ktx Python runtime checks\n');
|
|
84
84
|
for (const check of checks) {
|
|
85
85
|
io.stdout.write(`${check.status.toUpperCase()} ${check.label}: ${check.detail}\n`);
|
|
86
86
|
if (check.fix) {
|
package/dist/scan.js
CHANGED
|
@@ -206,7 +206,7 @@ function writeHumanReportBody(report, io) {
|
|
|
206
206
|
}
|
|
207
207
|
function writeRunSummary(report, projectDir, io) {
|
|
208
208
|
const styled = shouldUseStyledOutput(io);
|
|
209
|
-
io.stdout.write(`${styled ? green('✓') : ''}${styled ? ' ' : ''}
|
|
209
|
+
io.stdout.write(`${styled ? green('✓') : ''}${styled ? ' ' : ''}ktx scan completed\n`);
|
|
210
210
|
io.stdout.write('Status: done\n');
|
|
211
211
|
writeHumanReportBody(report, io);
|
|
212
212
|
const projectDirArg = quoteCliArg(projectDir);
|
package/dist/setup-agents.d.ts
CHANGED
|
@@ -14,15 +14,24 @@ export interface KtxSetupAgentsArgs {
|
|
|
14
14
|
mode: KtxAgentInstallMode;
|
|
15
15
|
skipAgents: boolean;
|
|
16
16
|
showNextActions?: boolean;
|
|
17
|
+
installRoot?: string;
|
|
18
|
+
cwd?: string;
|
|
17
19
|
}
|
|
20
|
+
/** The directory project-scoped agent files land in; equals projectDir unless an install root is chosen. */
|
|
21
|
+
interface KtxAgentInstall {
|
|
22
|
+
target: KtxAgentTarget;
|
|
23
|
+
scope: KtxAgentScope;
|
|
24
|
+
mode: KtxAgentInstallMode;
|
|
25
|
+
installRoot: string;
|
|
26
|
+
}
|
|
27
|
+
/** Install shape for formatting helpers; installRoot falls back to projectDir when absent. */
|
|
28
|
+
type KtxAgentInstallLike = Omit<KtxAgentInstall, 'installRoot'> & {
|
|
29
|
+
installRoot?: string;
|
|
30
|
+
};
|
|
18
31
|
export type KtxSetupAgentsResult = {
|
|
19
32
|
status: 'ready';
|
|
20
33
|
projectDir: string;
|
|
21
|
-
installs:
|
|
22
|
-
target: KtxAgentTarget;
|
|
23
|
-
scope: KtxAgentScope;
|
|
24
|
-
mode: KtxAgentInstallMode;
|
|
25
|
-
}>;
|
|
34
|
+
installs: KtxAgentInstall[];
|
|
26
35
|
nextActions?: string;
|
|
27
36
|
} | {
|
|
28
37
|
status: 'skipped';
|
|
@@ -41,11 +50,7 @@ export interface KtxAgentInstallManifest {
|
|
|
41
50
|
version: 1;
|
|
42
51
|
projectDir: string;
|
|
43
52
|
installedAt: string;
|
|
44
|
-
installs:
|
|
45
|
-
target: KtxAgentTarget;
|
|
46
|
-
scope: KtxAgentScope;
|
|
47
|
-
mode: KtxAgentInstallMode;
|
|
48
|
-
}>;
|
|
53
|
+
installs: KtxAgentInstall[];
|
|
49
54
|
entries: Array<{
|
|
50
55
|
kind: 'file';
|
|
51
56
|
path: string;
|
|
@@ -65,6 +70,7 @@ export declare function plannedKtxAgentFiles(input: {
|
|
|
65
70
|
target: KtxAgentTarget;
|
|
66
71
|
scope: KtxAgentScope;
|
|
67
72
|
mode: KtxAgentInstallMode;
|
|
73
|
+
installRoot?: string;
|
|
68
74
|
}): InstallEntry[];
|
|
69
75
|
export declare function readKtxAgentInstallManifest(projectDir: string): Promise<KtxAgentInstallManifest | null>;
|
|
70
76
|
/** @internal */
|
|
@@ -79,6 +85,10 @@ interface KtxSetupAgentsPromptAdapter {
|
|
|
79
85
|
options: KtxSetupPromptOption[];
|
|
80
86
|
required?: boolean;
|
|
81
87
|
}): Promise<string[]>;
|
|
88
|
+
text(options: {
|
|
89
|
+
message: string;
|
|
90
|
+
placeholder?: string;
|
|
91
|
+
}): Promise<string | undefined>;
|
|
82
92
|
cancel(message: string): void;
|
|
83
93
|
}
|
|
84
94
|
export interface KtxSetupAgentsDeps {
|
|
@@ -91,10 +101,6 @@ export interface InstallSummaryEntry {
|
|
|
91
101
|
lines: string[];
|
|
92
102
|
}
|
|
93
103
|
/** @internal */
|
|
94
|
-
export declare function formatInstallSummaryLines(installs:
|
|
95
|
-
target: KtxAgentTarget;
|
|
96
|
-
scope: KtxAgentScope;
|
|
97
|
-
mode: KtxAgentInstallMode;
|
|
98
|
-
}>, entries: InstallEntry[], projectDir: string): InstallSummaryEntry[];
|
|
104
|
+
export declare function formatInstallSummaryLines(installs: KtxAgentInstallLike[], entries: InstallEntry[], projectDir: string): InstallSummaryEntry[];
|
|
99
105
|
export declare function runKtxSetupAgentsStep(args: KtxSetupAgentsArgs, io: KtxCliIo, deps?: KtxSetupAgentsDeps): Promise<KtxSetupAgentsResult>;
|
|
100
106
|
export {};
|
package/dist/setup-agents.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { styleText } from 'node:util';
|
|
@@ -12,14 +12,8 @@ import { errorMessage, writePrefixedLines } from './clack.js';
|
|
|
12
12
|
import { isWritableTtyOutput } from './io/tty.js';
|
|
13
13
|
import { createKtxSetupPromptAdapter, createKtxSetupUiAdapter, } from './setup-prompts.js';
|
|
14
14
|
import { readKtxMcpDaemonStatus } from './managed-mcp-daemon.js';
|
|
15
|
+
import { withMultiselectNavigation } from './prompt-navigation.js';
|
|
15
16
|
const MCP_DAEMON_REQUIRED_NOTICE = 'mcp-daemon-required';
|
|
16
|
-
function writeSetupInfo(io, message) {
|
|
17
|
-
if (isWritableTtyOutput(io.stdout)) {
|
|
18
|
-
log.info(message, { output: io.stdout });
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
io.stdout.write(`${message}\n`);
|
|
22
|
-
}
|
|
23
17
|
function writeSetupStep(io, message) {
|
|
24
18
|
if (isWritableTtyOutput(io.stdout)) {
|
|
25
19
|
log.step(message, { output: io.stdout });
|
|
@@ -148,7 +142,7 @@ function codexSnippet(endpoint) {
|
|
|
148
142
|
if (endpoint.tokenAuth) {
|
|
149
143
|
return [
|
|
150
144
|
'Codex MCP config does not currently document HTTP headers.',
|
|
151
|
-
'Run
|
|
145
|
+
'Run ktx on loopback without token auth for Codex, or configure headers after Codex documents support.',
|
|
152
146
|
].join('\n');
|
|
153
147
|
}
|
|
154
148
|
return [`[mcp_servers.ktx]`, `url = "${endpoint.url}"`].join('\n');
|
|
@@ -172,7 +166,7 @@ function universalMcpSnippet(endpoint) {
|
|
|
172
166
|
...(endpoint.tokenAuth ? ['Header: Authorization: Bearer ${KTX_MCP_TOKEN}'] : []),
|
|
173
167
|
].join('\n');
|
|
174
168
|
}
|
|
175
|
-
function claudeConfigPath(projectDir, scope) {
|
|
169
|
+
function claudeConfigPath(projectDir, installRoot, scope) {
|
|
176
170
|
const home = process.env.HOME ?? '';
|
|
177
171
|
if (scope === 'global') {
|
|
178
172
|
return { path: join(home, '.claude.json'), jsonPath: ['mcpServers', 'ktx'] };
|
|
@@ -180,12 +174,12 @@ function claudeConfigPath(projectDir, scope) {
|
|
|
180
174
|
if (scope === 'local') {
|
|
181
175
|
return { path: join(home, '.claude.json'), jsonPath: ['projects', resolve(projectDir), 'mcpServers', 'ktx'] };
|
|
182
176
|
}
|
|
183
|
-
return { path: join(resolve(
|
|
177
|
+
return { path: join(resolve(installRoot), '.mcp.json'), jsonPath: ['mcpServers', 'ktx'] };
|
|
184
178
|
}
|
|
185
|
-
function cursorConfigPath(
|
|
179
|
+
function cursorConfigPath(installRoot, scope) {
|
|
186
180
|
const home = process.env.HOME ?? '';
|
|
187
181
|
return {
|
|
188
|
-
path: scope === 'global' ? join(home, '.cursor/mcp.json') : join(resolve(
|
|
182
|
+
path: scope === 'global' ? join(home, '.cursor/mcp.json') : join(resolve(installRoot), '.cursor/mcp.json'),
|
|
189
183
|
jsonPath: ['mcpServers', 'ktx'],
|
|
190
184
|
};
|
|
191
185
|
}
|
|
@@ -232,12 +226,12 @@ async function installMcpClientConfig(input) {
|
|
|
232
226
|
notices.push(MCP_DAEMON_REQUIRED_NOTICE);
|
|
233
227
|
}
|
|
234
228
|
if (input.target === 'claude-code') {
|
|
235
|
-
const config = claudeConfigPath(input.projectDir, input.scope);
|
|
229
|
+
const config = claudeConfigPath(input.projectDir, input.installRoot, input.scope);
|
|
236
230
|
await writeJsonKey(config.path, config.jsonPath, claudeMcpEntry(endpoint));
|
|
237
231
|
entries.push({ kind: 'json-key', path: config.path, jsonPath: config.jsonPath });
|
|
238
232
|
}
|
|
239
233
|
else if (input.target === 'cursor') {
|
|
240
|
-
const config = cursorConfigPath(input.
|
|
234
|
+
const config = cursorConfigPath(input.installRoot, input.scope);
|
|
241
235
|
await writeJsonKey(config.path, config.jsonPath, cursorMcpEntry(endpoint));
|
|
242
236
|
entries.push({ kind: 'json-key', path: config.path, jsonPath: config.jsonPath });
|
|
243
237
|
}
|
|
@@ -247,7 +241,7 @@ async function installMcpClientConfig(input) {
|
|
|
247
241
|
else if (input.target === 'opencode') {
|
|
248
242
|
const path = input.scope === 'global'
|
|
249
243
|
? '~/.config/opencode/opencode.json'
|
|
250
|
-
: relative(input.
|
|
244
|
+
: relative(input.installRoot, join(input.installRoot, 'opencode.json'));
|
|
251
245
|
snippets.push(`Add this OpenCode MCP snippet to ${path}:\n${opencodeSnippet(endpoint)}`);
|
|
252
246
|
}
|
|
253
247
|
else if (input.target === 'universal') {
|
|
@@ -257,7 +251,7 @@ async function installMcpClientConfig(input) {
|
|
|
257
251
|
}
|
|
258
252
|
function plannedMcpJsonEntries(input) {
|
|
259
253
|
if (input.target === 'claude-code') {
|
|
260
|
-
const config = claudeConfigPath(input.projectDir, input.scope);
|
|
254
|
+
const config = claudeConfigPath(input.projectDir, input.installRoot, input.scope);
|
|
261
255
|
return [{ kind: 'json-key', path: config.path, jsonPath: config.jsonPath }];
|
|
262
256
|
}
|
|
263
257
|
if (input.target === 'claude-desktop') {
|
|
@@ -265,7 +259,7 @@ function plannedMcpJsonEntries(input) {
|
|
|
265
259
|
return [{ kind: 'json-key', path: config.path, jsonPath: config.jsonPath }];
|
|
266
260
|
}
|
|
267
261
|
if (input.target === 'cursor') {
|
|
268
|
-
const config = cursorConfigPath(input.
|
|
262
|
+
const config = cursorConfigPath(input.installRoot, input.scope);
|
|
269
263
|
return [{ kind: 'json-key', path: config.path, jsonPath: config.jsonPath }];
|
|
270
264
|
}
|
|
271
265
|
return [];
|
|
@@ -330,7 +324,7 @@ export function plannedKtxAgentFiles(input) {
|
|
|
330
324
|
}
|
|
331
325
|
throw new Error(`Global ${input.target} installation is not supported; omit --global.`);
|
|
332
326
|
}
|
|
333
|
-
const root = resolve(input.projectDir);
|
|
327
|
+
const root = resolve(input.installRoot ?? input.projectDir);
|
|
334
328
|
const analyticsEntries = {
|
|
335
329
|
'claude-code': [
|
|
336
330
|
{ kind: 'file', path: join(root, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
@@ -406,16 +400,16 @@ function cliInstructionContent(input) {
|
|
|
406
400
|
return [
|
|
407
401
|
'---',
|
|
408
402
|
'name: ktx',
|
|
409
|
-
'description: Use local
|
|
403
|
+
'description: Use local ktx semantic context and wiki knowledge for this project.',
|
|
410
404
|
'---',
|
|
411
405
|
'',
|
|
412
|
-
'#
|
|
406
|
+
'# ktx Local Context',
|
|
413
407
|
'',
|
|
414
|
-
'This is an admin/developer CLI helper. End-user data agents should use the
|
|
408
|
+
'This is an admin/developer CLI helper. End-user data agents should use the ktx MCP tools when available.',
|
|
415
409
|
'',
|
|
416
410
|
`Use this project with \`--project-dir ${input.projectDir}\`.`,
|
|
417
|
-
'Commands are pinned to the local
|
|
418
|
-
'If the CLI path no longer exists after moving this checkout or reinstalling
|
|
411
|
+
'Commands are pinned to the local ktx CLI path that created this file, so agents do not need `ktx` in PATH.',
|
|
412
|
+
'If the CLI path no longer exists after moving this checkout or reinstalling ktx, rerun `ktx setup --agents`.',
|
|
419
413
|
'',
|
|
420
414
|
'Agents must not print secrets, credential references, environment variable values, or file contents from ' +
|
|
421
415
|
'`.ktx/secrets`.',
|
|
@@ -508,7 +502,8 @@ function entryKey(entry) {
|
|
|
508
502
|
function mergeManifest(projectDir, existing, installs, entries) {
|
|
509
503
|
const installMap = new Map();
|
|
510
504
|
for (const install of [...(existing?.installs ?? []), ...installs]) {
|
|
511
|
-
|
|
505
|
+
const installRoot = install.installRoot ?? resolve(projectDir);
|
|
506
|
+
installMap.set(`${install.target}:${install.scope}:${install.mode}:${installRoot}`, { ...install, installRoot });
|
|
512
507
|
}
|
|
513
508
|
const entryMap = new Map();
|
|
514
509
|
for (const entry of [...(existing?.entries ?? []), ...entries]) {
|
|
@@ -526,7 +521,7 @@ function mergeManifest(projectDir, existing, installs, entries) {
|
|
|
526
521
|
export async function removeKtxAgentInstall(projectDir, io) {
|
|
527
522
|
const manifest = await readKtxAgentInstallManifest(projectDir);
|
|
528
523
|
if (!manifest) {
|
|
529
|
-
io.stdout.write('No
|
|
524
|
+
io.stdout.write('No ktx agent installation manifest found.\n');
|
|
530
525
|
return 0;
|
|
531
526
|
}
|
|
532
527
|
for (const entry of manifest.entries) {
|
|
@@ -536,7 +531,7 @@ export async function removeKtxAgentInstall(projectDir, io) {
|
|
|
536
531
|
await removeJsonKey(entry.path, entry.jsonPath).catch(() => undefined);
|
|
537
532
|
}
|
|
538
533
|
await rm(agentInstallManifestPath(projectDir), { force: true });
|
|
539
|
-
io.stdout.write('Removed
|
|
534
|
+
io.stdout.write('Removed ktx agent integration files from manifest.\n');
|
|
540
535
|
return 0;
|
|
541
536
|
}
|
|
542
537
|
function createPromptAdapter() {
|
|
@@ -617,18 +612,31 @@ function formatInlinePath(path) {
|
|
|
617
612
|
}
|
|
618
613
|
return path;
|
|
619
614
|
}
|
|
615
|
+
function installSummaryTitle(install, projectDir) {
|
|
616
|
+
const name = targetDisplayName(install.target);
|
|
617
|
+
if (install.scope !== 'project') {
|
|
618
|
+
return `${name} · ${scopeDisplayName(install.scope)}`;
|
|
619
|
+
}
|
|
620
|
+
const installRoot = resolve(install.installRoot ?? projectDir);
|
|
621
|
+
if (installRoot === resolve(projectDir)) {
|
|
622
|
+
return `${name} · ${scopeDisplayName('project')}`;
|
|
623
|
+
}
|
|
624
|
+
return `${name} · ${formatInlinePath(installRoot)}`;
|
|
625
|
+
}
|
|
620
626
|
/** @internal */
|
|
621
627
|
export function formatInstallSummaryLines(installs, entries, projectDir) {
|
|
622
628
|
const entriesByTarget = new Map();
|
|
623
629
|
for (const install of installs) {
|
|
624
|
-
const
|
|
630
|
+
const installRoot = install.installRoot ?? projectDir;
|
|
631
|
+
const plannedFilePaths = new Set(plannedKtxAgentFiles({ projectDir, ...install, installRoot })
|
|
625
632
|
.filter((entry) => entry.kind === 'file')
|
|
626
633
|
.map((entry) => entry.path));
|
|
627
634
|
entriesByTarget.set(install.target, entries.filter((entry) => entry.kind === 'file' && plannedFilePaths.has(entry.path)));
|
|
628
635
|
}
|
|
629
636
|
const mcpEntriesByTarget = new Map();
|
|
630
637
|
for (const install of installs) {
|
|
631
|
-
const
|
|
638
|
+
const installRoot = install.installRoot ?? projectDir;
|
|
639
|
+
const plannedMcpKeys = new Set(plannedMcpJsonEntries({ projectDir, installRoot, target: install.target, scope: install.scope }).map(entryKey));
|
|
632
640
|
mcpEntriesByTarget.set(install.target, entries.filter((entry) => entry.kind === 'json-key' && plannedMcpKeys.has(entryKey(entry))));
|
|
633
641
|
}
|
|
634
642
|
return installs.map((install) => {
|
|
@@ -669,7 +677,7 @@ export function formatInstallSummaryLines(installs, entries, projectDir) {
|
|
|
669
677
|
lines.push(`${guidanceInstallLine(install.target)}.`);
|
|
670
678
|
}
|
|
671
679
|
return {
|
|
672
|
-
title:
|
|
680
|
+
title: installSummaryTitle(install, projectDir),
|
|
673
681
|
lines,
|
|
674
682
|
};
|
|
675
683
|
});
|
|
@@ -732,6 +740,9 @@ function manualActionFromSnippet(snippet) {
|
|
|
732
740
|
body,
|
|
733
741
|
};
|
|
734
742
|
}
|
|
743
|
+
function openFromDirectoryLabel(installRoot, projectDir) {
|
|
744
|
+
return resolve(installRoot) === resolve(projectDir) ? 'the ktx project directory' : 'the install directory';
|
|
745
|
+
}
|
|
735
746
|
function formatAgentNextActions(input) {
|
|
736
747
|
const projectDir = resolve(input.projectDir);
|
|
737
748
|
const lines = [];
|
|
@@ -768,10 +779,11 @@ function formatAgentNextActions(input) {
|
|
|
768
779
|
if (claudeCodeInstall) {
|
|
769
780
|
lines.push(`${step}. Open Claude Code`);
|
|
770
781
|
if (claudeCodeInstall.scope === 'project') {
|
|
771
|
-
|
|
782
|
+
const installRoot = resolve(claudeCodeInstall.installRoot ?? projectDir);
|
|
783
|
+
lines.push(` Open Claude Code from ${openFromDirectoryLabel(installRoot, projectDir)}:`);
|
|
772
784
|
lines.push('');
|
|
773
785
|
lines.push(' RUN:');
|
|
774
|
-
lines.push(` cd ${shellScriptQuote(
|
|
786
|
+
lines.push(` cd ${shellScriptQuote(installRoot)}`);
|
|
775
787
|
lines.push(' claude');
|
|
776
788
|
}
|
|
777
789
|
else {
|
|
@@ -785,10 +797,11 @@ function formatAgentNextActions(input) {
|
|
|
785
797
|
if (cursorInstall) {
|
|
786
798
|
lines.push(`${step}. Open Cursor`);
|
|
787
799
|
if (cursorInstall.scope === 'project') {
|
|
788
|
-
|
|
800
|
+
const installRoot = resolve(cursorInstall.installRoot ?? projectDir);
|
|
801
|
+
lines.push(` Open Cursor from ${openFromDirectoryLabel(installRoot, projectDir)}:`);
|
|
789
802
|
lines.push('');
|
|
790
803
|
lines.push(' OPEN:');
|
|
791
|
-
lines.push(` ${
|
|
804
|
+
lines.push(` ${installRoot}`);
|
|
792
805
|
}
|
|
793
806
|
else {
|
|
794
807
|
lines.push(' Open Cursor.');
|
|
@@ -798,7 +811,7 @@ function formatAgentNextActions(input) {
|
|
|
798
811
|
}
|
|
799
812
|
if (input.installs.some((install) => install.target === 'claude-desktop')) {
|
|
800
813
|
lines.push(`${step}. Restart Claude Desktop`);
|
|
801
|
-
lines.push(' Claude Desktop loads
|
|
814
|
+
lines.push(' Claude Desktop loads ktx MCP after restart.');
|
|
802
815
|
pushBlankLine(lines);
|
|
803
816
|
step += 1;
|
|
804
817
|
const skillBundlePaths = claudeDesktopSkillBundlePathsForInstalls(projectDir, input.installs);
|
|
@@ -809,7 +822,7 @@ function formatAgentNextActions(input) {
|
|
|
809
822
|
for (const path of skillBundlePaths) {
|
|
810
823
|
lines.push(` ${path}`);
|
|
811
824
|
}
|
|
812
|
-
lines.push(' Toggle the uploaded
|
|
825
|
+
lines.push(' Toggle the uploaded ktx skills on.');
|
|
813
826
|
pushBlankLine(lines);
|
|
814
827
|
step += 1;
|
|
815
828
|
}
|
|
@@ -850,6 +863,70 @@ async function markAgentsComplete(projectDir) {
|
|
|
850
863
|
await writeFile(project.configPath, serializeKtxProjectConfig(project.config), 'utf-8');
|
|
851
864
|
await markKtxSetupStateStepComplete(projectDir, 'agents');
|
|
852
865
|
}
|
|
866
|
+
// A typed path never passes through a shell, so expand a leading ~ here; HOME
|
|
867
|
+
// matches formatInlinePath so the ~/… hints shown in the menu round-trip.
|
|
868
|
+
function resolveTypedInstallDir(cwd, raw) {
|
|
869
|
+
const home = process.env.HOME;
|
|
870
|
+
if (home && (raw === '~' || raw.startsWith('~/'))) {
|
|
871
|
+
return resolve(home, raw.slice(2));
|
|
872
|
+
}
|
|
873
|
+
return resolve(cwd, raw);
|
|
874
|
+
}
|
|
875
|
+
async function ensureInstallDir(resolvedPath) {
|
|
876
|
+
if (existsSync(resolvedPath)) {
|
|
877
|
+
if (!(await stat(resolvedPath)).isDirectory()) {
|
|
878
|
+
throw new Error(`Install directory path is a file, not a directory: ${resolvedPath}`);
|
|
879
|
+
}
|
|
880
|
+
return resolvedPath;
|
|
881
|
+
}
|
|
882
|
+
await mkdir(resolvedPath, { recursive: true });
|
|
883
|
+
return resolvedPath;
|
|
884
|
+
}
|
|
885
|
+
async function promptInstallDirectory(input) {
|
|
886
|
+
const { prompts, io, cwd, projectRoot, scopeTargets } = input;
|
|
887
|
+
const options = [
|
|
888
|
+
{ value: 'project', label: 'ktx project directory', hint: formatInlinePath(projectRoot) },
|
|
889
|
+
...(cwd !== projectRoot
|
|
890
|
+
? [{ value: 'current', label: 'Current directory', hint: formatInlinePath(cwd) }]
|
|
891
|
+
: []),
|
|
892
|
+
{ value: 'custom', label: 'Custom directory…', hint: 'Enter a path' },
|
|
893
|
+
...(scopeTargets.every(targetSupportsGlobalScope)
|
|
894
|
+
? [
|
|
895
|
+
{
|
|
896
|
+
value: 'global',
|
|
897
|
+
label: 'Global scope (user config)',
|
|
898
|
+
hint: 'Agents can load this ktx project from any working directory.',
|
|
899
|
+
},
|
|
900
|
+
]
|
|
901
|
+
: []),
|
|
902
|
+
];
|
|
903
|
+
const choice = await prompts.select({
|
|
904
|
+
message: `Where should ktx install agent config?\n\nktx project: ${projectRoot}`,
|
|
905
|
+
options,
|
|
906
|
+
});
|
|
907
|
+
if (choice === 'back')
|
|
908
|
+
return 'back';
|
|
909
|
+
if (choice === 'global')
|
|
910
|
+
return { scope: 'global', installRoot: projectRoot };
|
|
911
|
+
if (choice === 'current')
|
|
912
|
+
return { scope: 'project', installRoot: cwd };
|
|
913
|
+
if (choice === 'project')
|
|
914
|
+
return { scope: 'project', installRoot: projectRoot };
|
|
915
|
+
while (true) {
|
|
916
|
+
const typed = await prompts.text({ message: 'Enter the directory to install agent config into' });
|
|
917
|
+
if (typed === undefined)
|
|
918
|
+
return 'back';
|
|
919
|
+
const trimmed = typed.trim();
|
|
920
|
+
if (trimmed === '')
|
|
921
|
+
continue;
|
|
922
|
+
try {
|
|
923
|
+
return { scope: 'project', installRoot: await ensureInstallDir(resolveTypedInstallDir(cwd, trimmed)) };
|
|
924
|
+
}
|
|
925
|
+
catch (error) {
|
|
926
|
+
io.stderr.write(`${errorMessage(error)}\n`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
853
930
|
export async function runKtxSetupAgentsStep(args, io, deps = {}) {
|
|
854
931
|
if (args.skipAgents) {
|
|
855
932
|
io.stdout.write('│ Agent integration skipped.\n');
|
|
@@ -859,22 +936,19 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
|
|
|
859
936
|
return { status: 'skipped', projectDir: args.projectDir };
|
|
860
937
|
}
|
|
861
938
|
const prompts = deps.prompts ?? createPromptAdapter();
|
|
862
|
-
if (args.inputMode === 'auto' && args.target === undefined) {
|
|
863
|
-
writeSetupInfo(io, 'Space to select, Enter to confirm, Esc to go back.');
|
|
864
|
-
}
|
|
865
939
|
const mode = args.inputMode === 'disabled'
|
|
866
940
|
? args.mode
|
|
867
941
|
: (await prompts.select({
|
|
868
|
-
message: 'What should agents be allowed to do with this
|
|
942
|
+
message: 'What should agents be allowed to do with this ktx project?',
|
|
869
943
|
options: [
|
|
870
944
|
{
|
|
871
945
|
value: 'mcp',
|
|
872
|
-
label: 'Ask data questions with
|
|
946
|
+
label: 'Ask data questions with ktx MCP',
|
|
873
947
|
hint: 'Installs the MCP connection and analytics workflow skill. Best for normal use.',
|
|
874
948
|
},
|
|
875
949
|
{
|
|
876
950
|
value: 'mcp-cli',
|
|
877
|
-
label: 'Ask data questions + manage
|
|
951
|
+
label: 'Ask data questions + manage ktx with CLI commands',
|
|
878
952
|
hint: 'Adds an admin CLI skill so agents can run ktx status, sl, wiki, and setup commands.',
|
|
879
953
|
},
|
|
880
954
|
{
|
|
@@ -895,7 +969,7 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
|
|
|
895
969
|
: args.inputMode === 'disabled'
|
|
896
970
|
? []
|
|
897
971
|
: (await prompts.multiselect({
|
|
898
|
-
message: 'Which agent targets should
|
|
972
|
+
message: withMultiselectNavigation('Which agent targets should ktx install?'),
|
|
899
973
|
options: [
|
|
900
974
|
{ value: 'claude-code', label: 'Claude Code' },
|
|
901
975
|
{ value: 'claude-desktop', label: 'Claude Desktop' },
|
|
@@ -914,30 +988,32 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
|
|
|
914
988
|
: 'Missing agent target: pass --target or use interactive setup.\n');
|
|
915
989
|
return { status: 'missing-input', projectDir: args.projectDir };
|
|
916
990
|
}
|
|
991
|
+
const cwd = resolve(args.cwd ?? process.cwd());
|
|
992
|
+
const projectRoot = resolve(args.projectDir);
|
|
917
993
|
const scopeTargets = targets.filter((target) => target !== 'claude-desktop');
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
return {
|
|
940
|
-
|
|
994
|
+
let selectedScope = args.scope;
|
|
995
|
+
let installRoot = projectRoot;
|
|
996
|
+
if (args.installRoot !== undefined) {
|
|
997
|
+
try {
|
|
998
|
+
installRoot = await ensureInstallDir(resolveTypedInstallDir(cwd, args.installRoot));
|
|
999
|
+
}
|
|
1000
|
+
catch (error) {
|
|
1001
|
+
writePrefixedLines((chunk) => io.stderr.write(chunk), errorMessage(error));
|
|
1002
|
+
return { status: 'failed', projectDir: args.projectDir };
|
|
1003
|
+
}
|
|
1004
|
+
selectedScope = 'project';
|
|
1005
|
+
}
|
|
1006
|
+
else if (args.inputMode !== 'disabled' && args.scope === 'project' && scopeTargets.length > 0) {
|
|
1007
|
+
const decision = await promptInstallDirectory({ prompts, io, cwd, projectRoot, scopeTargets });
|
|
1008
|
+
if (decision === 'back')
|
|
1009
|
+
return { status: 'back', projectDir: args.projectDir };
|
|
1010
|
+
selectedScope = decision.scope;
|
|
1011
|
+
installRoot = decision.installRoot;
|
|
1012
|
+
}
|
|
1013
|
+
const installs = targets.map((target) => {
|
|
1014
|
+
const scope = effectiveInstallScope(target, selectedScope);
|
|
1015
|
+
return { target, scope, mode, installRoot: scope === 'project' ? installRoot : projectRoot };
|
|
1016
|
+
});
|
|
941
1017
|
const entries = [];
|
|
942
1018
|
const snippets = [];
|
|
943
1019
|
const notices = new Set();
|
|
@@ -947,6 +1023,7 @@ export async function runKtxSetupAgentsStep(args, io, deps = {}) {
|
|
|
947
1023
|
entries.push(...targetEntries);
|
|
948
1024
|
const mcpResult = await installMcpClientConfig({
|
|
949
1025
|
projectDir: args.projectDir,
|
|
1026
|
+
installRoot: install.installRoot,
|
|
950
1027
|
target: install.target,
|
|
951
1028
|
scope: install.scope,
|
|
952
1029
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ktx setup` intro banner: a lowercase ktx wordmark drawn with
|
|
3
|
+
* half-block glyphs over the brand-orange gradient, followed by the product
|
|
4
|
+
* tagline. Rendering is a pure function of the options so output stays
|
|
5
|
+
* deterministic in tests.
|
|
6
|
+
*/
|
|
7
|
+
export interface KtxSetupBannerOptions {
|
|
8
|
+
/** Terminal width in columns. */
|
|
9
|
+
columns: number;
|
|
10
|
+
/** Color depth in bits, as reported by `tty.WriteStream#getColorDepth`; 1 disables color. */
|
|
11
|
+
colorDepth: number;
|
|
12
|
+
/** Whether the terminal renders Unicode block glyphs. */
|
|
13
|
+
unicode: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the banner block ending right above the clack intro line, or an
|
|
17
|
+
* empty string when the terminal cannot display it (no Unicode support or
|
|
18
|
+
* too narrow).
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderKtxSetupBanner(options: KtxSetupBannerOptions): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ktx setup` intro banner: a lowercase ktx wordmark drawn with
|
|
3
|
+
* half-block glyphs over the brand-orange gradient, followed by the product
|
|
4
|
+
* tagline. Rendering is a pure function of the options so output stays
|
|
5
|
+
* deterministic in tests.
|
|
6
|
+
*/
|
|
7
|
+
const WORDMARK = [
|
|
8
|
+
{ art: '███ ███', rgb: [253, 186, 116], ansi256: 215 },
|
|
9
|
+
{ art: '███ ▄██▀ ███████ ▀██▄ ▄██▀', rgb: [251, 146, 60], ansi256: 214 },
|
|
10
|
+
{ art: '███▄██▀ ███ ▀████▀', rgb: [249, 115, 22], ansi256: 208 },
|
|
11
|
+
{ art: '███▀██▄ ███ ▄████▄', rgb: [234, 88, 12], ansi256: 202 },
|
|
12
|
+
{ art: '███ ▀██▄ ███ ▄██▀ ▀██▄', rgb: [194, 65, 12], ansi256: 166 },
|
|
13
|
+
];
|
|
14
|
+
const TAGLINE = 'context layer for data agents';
|
|
15
|
+
const INDENT = ' ';
|
|
16
|
+
const BANNER_WIDTH = Math.max(...WORDMARK.map((row) => row.art.length), TAGLINE.length) + INDENT.length;
|
|
17
|
+
/**
|
|
18
|
+
* Returns the banner block ending right above the clack intro line, or an
|
|
19
|
+
* empty string when the terminal cannot display it (no Unicode support or
|
|
20
|
+
* too narrow).
|
|
21
|
+
*/
|
|
22
|
+
export function renderKtxSetupBanner(options) {
|
|
23
|
+
if (!options.unicode || options.columns < BANNER_WIDTH) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
const art = WORDMARK.map((row) => INDENT + colorizeBannerRow(row, options.colorDepth));
|
|
27
|
+
const tagline = INDENT + (options.colorDepth > 1 ? `\u001b[2m${TAGLINE}\u001b[22m` : TAGLINE);
|
|
28
|
+
return `\n${art.join('\n')}\n\n${tagline}\n\n`;
|
|
29
|
+
}
|
|
30
|
+
function colorizeBannerRow(row, colorDepth) {
|
|
31
|
+
if (colorDepth >= 24) {
|
|
32
|
+
const [r, g, b] = row.rgb;
|
|
33
|
+
return `\u001b[38;2;${r};${g};${b}m${row.art}\u001b[39m`;
|
|
34
|
+
}
|
|
35
|
+
if (colorDepth >= 8) {
|
|
36
|
+
return `\u001b[38;5;${row.ansi256}m${row.art}\u001b[39m`;
|
|
37
|
+
}
|
|
38
|
+
return row.art;
|
|
39
|
+
}
|