2026-05-10 23:12:26 +02:00
|
|
|
|
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
|
|
|
|
import { tmpdir } from 'node:os';
|
|
|
|
|
|
import { join } from 'node:path';
|
test: split cli tests from source tree (#216)
* feat(cli): define full warehouse dialect contract
* test(cli): keep dialect edge tests focused
* fix(cli): stabilize dialect contract foundation
* refactor(connectors): own read-only query preparation
* refactor(connectors): resolve dialects through registry
* refactor(connectors): keep concrete dialect classes internal
* chore(workspace): enforce dialect import boundary
* refactor(cli): resolve relationship dialect at scan boundary
* refactor(cli): use dialect display parsing for entity details
* refactor(cli): use dialect display parsing for warehouse catalog
* refactor(cli): use dialect SQL in relationship workflows
* test(cli): verify solid dialect scan workflow closure
* test: split cli tests from source tree
* refactor(cli): standardize BigQuery scope listing
* feat(sqlite): implement connector scope listing
* test(connectors): cover required table listing
* feat(cli): add warehouse driver registry
* refactor(setup): route scope discovery through driver registry
* refactor(cli): route local query execution through driver registry
* refactor(historic-sql): route dialect support through driver registry
* refactor(cli): test warehouse connections through driver registry
* fix(cli): close driver registry type export gaps
* Improve setup daemon diagnostics
* refactor(setup): centralize rail-prefixed diagnostics + query-history fallback
Extract errorMessage, writePrefixedLines, and flushPrefixedBufferedCommandOutput
into clack.ts so the setup wizard, managed daemons, and embedding/agent steps
share one rail-formatted writer. setup-databases.ts also adds a
"disable query history and retry" option when the schema-context build fails
and query history is the likely culprit, surfaced via a new
failed-query-history-unavailable status.
* fix(cli): carry catalog through the picker so BigQuery/Snowflake/SQL Server scope filters match
The setup picker's KtxTableListEntry was a 2-level { schema, name }, so
qualifiedTableId always wrote db.name into enabled_tables. When BigQuery,
Snowflake, or SQL Server later ran fast ingest, their introspect step filtered
the scope set with scopedTableNames(scope, { catalog: projectId|database, db })
— catalog was non-null on the introspect side but null in the scope refs, so
every entry was rejected, the live-database adapter staged zero table files,
and detect() failed with 'Adapter "live-database" did not recognize fetched
source output'.
Align the picker boundary with the canonical 3-level KtxTableRef:
- Add catalog: string | null to KtxTableListEntry.
- BigQuery/Snowflake/SQL Server listTables populate catalog from the
resolved projectId / database; Postgres/MySQL/ClickHouse/SQLite set null.
- qualifiedTableId emits catalog.schema.name when catalog is non-null
(resolveEnabledTables already accepts the 3-part shape) and
schemasFromEnabledTables now goes through parseDottedTableEntry so it
recovers the schema correctly from both 2-part and 3-part entries.
- Export parseDottedTableEntry from enabled-tables.ts (@internal) for picker
reuse.
Update listTables expectations in all seven connector tests and the setup /
picker test fixtures. Add a picker regression test that covers the
catalog-bearing round-trip (save + refine).
* fix(cli): allow debug telemetry under opt-out env
2026-05-26 08:49:05 +02:00
|
|
|
|
import { readKtxSetupState } from '../src/context/project/setup-config.js';
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
import { strFromU8, unzipSync } from 'fflate';
|
2026-05-10 23:12:26 +02:00
|
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
|
|
import {
|
2026-05-19 15:21:49 +02:00
|
|
|
|
createAgentNextActionsLineFormatter,
|
|
|
|
|
|
formatInstallSummaryLines,
|
2026-05-10 23:51:24 +02:00
|
|
|
|
plannedKtxAgentFiles,
|
|
|
|
|
|
readKtxAgentInstallManifest,
|
|
|
|
|
|
removeKtxAgentInstall,
|
|
|
|
|
|
runKtxSetupAgentsStep,
|
test: split cli tests from source tree (#216)
* feat(cli): define full warehouse dialect contract
* test(cli): keep dialect edge tests focused
* fix(cli): stabilize dialect contract foundation
* refactor(connectors): own read-only query preparation
* refactor(connectors): resolve dialects through registry
* refactor(connectors): keep concrete dialect classes internal
* chore(workspace): enforce dialect import boundary
* refactor(cli): resolve relationship dialect at scan boundary
* refactor(cli): use dialect display parsing for entity details
* refactor(cli): use dialect display parsing for warehouse catalog
* refactor(cli): use dialect SQL in relationship workflows
* test(cli): verify solid dialect scan workflow closure
* test: split cli tests from source tree
* refactor(cli): standardize BigQuery scope listing
* feat(sqlite): implement connector scope listing
* test(connectors): cover required table listing
* feat(cli): add warehouse driver registry
* refactor(setup): route scope discovery through driver registry
* refactor(cli): route local query execution through driver registry
* refactor(historic-sql): route dialect support through driver registry
* refactor(cli): test warehouse connections through driver registry
* fix(cli): close driver registry type export gaps
* Improve setup daemon diagnostics
* refactor(setup): centralize rail-prefixed diagnostics + query-history fallback
Extract errorMessage, writePrefixedLines, and flushPrefixedBufferedCommandOutput
into clack.ts so the setup wizard, managed daemons, and embedding/agent steps
share one rail-formatted writer. setup-databases.ts also adds a
"disable query history and retry" option when the schema-context build fails
and query history is the likely culprit, surfaced via a new
failed-query-history-unavailable status.
* fix(cli): carry catalog through the picker so BigQuery/Snowflake/SQL Server scope filters match
The setup picker's KtxTableListEntry was a 2-level { schema, name }, so
qualifiedTableId always wrote db.name into enabled_tables. When BigQuery,
Snowflake, or SQL Server later ran fast ingest, their introspect step filtered
the scope set with scopedTableNames(scope, { catalog: projectId|database, db })
— catalog was non-null on the introspect side but null in the scope refs, so
every entry was rejected, the live-database adapter staged zero table files,
and detect() failed with 'Adapter "live-database" did not recognize fetched
source output'.
Align the picker boundary with the canonical 3-level KtxTableRef:
- Add catalog: string | null to KtxTableListEntry.
- BigQuery/Snowflake/SQL Server listTables populate catalog from the
resolved projectId / database; Postgres/MySQL/ClickHouse/SQLite set null.
- qualifiedTableId emits catalog.schema.name when catalog is non-null
(resolveEnabledTables already accepts the 3-part shape) and
schemasFromEnabledTables now goes through parseDottedTableEntry so it
recovers the schema correctly from both 2-part and 3-part entries.
- Export parseDottedTableEntry from enabled-tables.ts (@internal) for picker
reuse.
Update listTables expectations in all seven connector tests and the setup /
picker test fixtures. Add a picker regression test that covers the
catalog-bearing round-trip (save + refine).
* fix(cli): allow debug telemetry under opt-out env
2026-05-26 08:49:05 +02:00
|
|
|
|
} from '../src/setup-agents.js';
|
2026-05-10 23:12:26 +02:00
|
|
|
|
|
|
|
|
|
|
function makeIo() {
|
|
|
|
|
|
let stdout = '';
|
|
|
|
|
|
let stderr = '';
|
|
|
|
|
|
return {
|
|
|
|
|
|
io: {
|
|
|
|
|
|
stdout: { write: (chunk: string) => (stdout += chunk) },
|
|
|
|
|
|
stderr: { write: (chunk: string) => (stderr += chunk) },
|
|
|
|
|
|
},
|
|
|
|
|
|
stdout: () => stdout,
|
|
|
|
|
|
stderr: () => stderr,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
async function readZipText(path: string, entry: string): Promise<string> {
|
|
|
|
|
|
const archive = unzipSync(new Uint8Array(await readFile(path)));
|
|
|
|
|
|
const content = archive[entry];
|
|
|
|
|
|
if (!content) throw new Error(`Missing zip entry: ${entry}`);
|
|
|
|
|
|
return strFromU8(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function captureEnvKeys(env: NodeJS.ProcessEnv, keys: readonly string[]): Record<string, string | undefined> {
|
|
|
|
|
|
const snapshot: Record<string, string | undefined> = {};
|
|
|
|
|
|
for (const key of keys) snapshot[key] = env[key];
|
|
|
|
|
|
return snapshot;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function clearEnvKeys(env: NodeJS.ProcessEnv, keys: readonly string[]): void {
|
|
|
|
|
|
for (const key of keys) delete env[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function captureKtxEnv(env: NodeJS.ProcessEnv): Record<string, string | undefined> {
|
|
|
|
|
|
const snapshot: Record<string, string | undefined> = {};
|
|
|
|
|
|
for (const key of Object.keys(env)) {
|
|
|
|
|
|
if (key.startsWith('KTX_')) snapshot[key] = env[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
return snapshot;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function clearKtxEnv(env: NodeJS.ProcessEnv): void {
|
|
|
|
|
|
for (const key of Object.keys(env)) {
|
|
|
|
|
|
if (key.startsWith('KTX_')) delete env[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function restoreEnvKeys(env: NodeJS.ProcessEnv, snapshot: Record<string, string | undefined>): void {
|
|
|
|
|
|
for (const [key, value] of Object.entries(snapshot)) {
|
|
|
|
|
|
if (value === undefined) delete env[key];
|
|
|
|
|
|
else env[key] = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 23:12:26 +02:00
|
|
|
|
describe('setup agents', () => {
|
|
|
|
|
|
let tempDir: string;
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
2026-05-10 23:51:24 +02:00
|
|
|
|
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-'));
|
|
|
|
|
|
await mkdir(join(tempDir, '.ktx', 'agents'), { recursive: true });
|
2026-05-14 17:39:31 +02:00
|
|
|
|
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
|
await rm(tempDir, { recursive: true, force: true });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
it('plans project-scoped MCP analytics files for every target', () => {
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'claude-code', scope: 'project', mode: 'mcp' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'claude-desktop', scope: 'global', mode: 'mcp' })).toEqual([
|
2026-05-19 15:21:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
kind: 'file',
|
|
|
|
|
|
path: join(tempDir, '.ktx/agents/claude/ktx-analytics.zip'),
|
|
|
|
|
|
role: 'claude-desktop-skill-bundle',
|
|
|
|
|
|
},
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'codex', scope: 'project', mode: 'mcp' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'cursor', scope: 'project', mode: 'mcp' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx-analytics.mdc'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'opencode', scope: 'project', mode: 'mcp' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.opencode/commands/ktx-analytics.md'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'universal', scope: 'project', mode: 'mcp' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('plans project-scoped admin CLI files for every target when requested', () => {
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'claude-code', scope: 'project', mode: 'mcp-cli' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
2026-05-10 23:13:17 -07:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx/SKILL.md'), role: 'skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/rules/ktx.md'), role: 'rule' },
|
2026-05-10 23:12:26 +02:00
|
|
|
|
]);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'codex', scope: 'project', mode: 'mcp-cli' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
2026-05-10 23:13:17 -07:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx/SKILL.md'), role: 'skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.codex/instructions/ktx.md'), role: 'rule' },
|
2026-05-10 23:12:26 +02:00
|
|
|
|
]);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'cursor', scope: 'project', mode: 'mcp-cli' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx-analytics.mdc'), role: 'analytics-skill' },
|
2026-05-12 23:51:46 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx.mdc') },
|
2026-05-10 23:12:26 +02:00
|
|
|
|
]);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'opencode', scope: 'project', mode: 'mcp-cli' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.opencode/commands/ktx-analytics.md'), role: 'analytics-skill' },
|
2026-05-10 23:51:24 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.opencode/commands/ktx.md') },
|
2026-05-10 23:12:26 +02:00
|
|
|
|
]);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'universal', scope: 'project', mode: 'mcp-cli' })).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
2026-05-10 23:51:24 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx/SKILL.md') },
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
]);
|
|
|
|
|
|
expect(plannedKtxAgentFiles({ projectDir: tempDir, target: 'claude-desktop', scope: 'global', mode: 'mcp-cli' })).toEqual([
|
2026-05-19 15:21:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
kind: 'file',
|
|
|
|
|
|
path: join(tempDir, '.ktx/agents/claude/ktx-analytics.zip'),
|
|
|
|
|
|
role: 'claude-desktop-skill-bundle',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'file',
|
|
|
|
|
|
path: join(tempDir, '.ktx/agents/claude/ktx.zip'),
|
|
|
|
|
|
role: 'claude-desktop-skill-bundle',
|
|
|
|
|
|
},
|
2026-05-10 23:12:26 +02:00
|
|
|
|
]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('installs target files, writes a manifest, and marks agents complete', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
2026-05-10 23:51:24 +02:00
|
|
|
|
runKtxSetupAgentsStep(
|
2026-05-10 23:12:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'universal',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-10 23:12:26 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
2026-05-18 18:54:20 -04:00
|
|
|
|
).resolves.toMatchObject({
|
2026-05-10 23:12:26 +02:00
|
|
|
|
status: 'ready',
|
|
|
|
|
|
projectDir: tempDir,
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
installs: [{ target: 'universal', scope: 'project', mode: 'mcp-cli' }],
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await expect(stat(join(tempDir, '.agents/skills/ktx/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
const skill = await readFile(join(tempDir, '.agents/skills/ktx/SKILL.md'), 'utf-8');
|
2026-05-10 23:12:26 +02:00
|
|
|
|
expect(skill).toContain(`--project-dir ${tempDir}`);
|
|
|
|
|
|
expect(skill).toContain('must not print secrets');
|
2026-05-13 13:01:56 +02:00
|
|
|
|
expect(skill).toContain('status --json');
|
2026-05-20 01:52:37 +02:00
|
|
|
|
expect(skill).toContain('sl --json');
|
2026-05-15 15:31:51 -04:00
|
|
|
|
expect(skill).toContain('sl query');
|
|
|
|
|
|
expect(skill).toContain('--format json');
|
|
|
|
|
|
expect(skill).not.toContain('sl query --json');
|
2026-05-13 13:01:56 +02:00
|
|
|
|
expect(skill).not.toContain('agent ');
|
|
|
|
|
|
expect(skill).not.toContain('sql execute');
|
2026-05-10 23:51:24 +02:00
|
|
|
|
expect(await readKtxAgentInstallManifest(tempDir)).toMatchObject({
|
2026-05-10 23:12:26 +02:00
|
|
|
|
version: 1,
|
|
|
|
|
|
projectDir: tempDir,
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
installs: [{ target: 'universal', scope: 'project', mode: 'mcp-cli' }],
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
2026-05-12 18:23:04 -07:00
|
|
|
|
expect(await readKtxSetupState(tempDir)).toEqual({ completed_steps: ['agents'] });
|
2026-05-10 23:12:26 +02:00
|
|
|
|
expect(io.stderr()).toBe('');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 19:23:35 +02:00
|
|
|
|
it('installs a specified target in non-interactive mode without --yes', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
const mcpConfig = JSON.parse(await readFile(join(tempDir, '.mcp.json'), 'utf-8')) as {
|
|
|
|
|
|
mcpServers?: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(mcpConfig.mcpServers).toHaveProperty('ktx');
|
|
|
|
|
|
expect(io.stderr()).toBe('');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('prints concrete target guidance when non-interactive agent setup has no target', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toEqual({ status: 'missing-input', projectDir: tempDir });
|
|
|
|
|
|
|
|
|
|
|
|
expect(io.stderr()).toBe('Run in a TTY, or pass --target <target>.\n');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-18 18:54:20 -04:00
|
|
|
|
it('prints standalone agent next actions after successful installation', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
nextActions: expect.stringContaining('Run this command before using Claude Code:'),
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(io.stdout()).toContain('Required before using agents');
|
|
|
|
|
|
expect(io.stdout()).toContain('Run this command before using Claude Code:');
|
|
|
|
|
|
expect(io.stdout()).toContain('RUN:');
|
|
|
|
|
|
expect(io.stdout()).toContain(`ktx mcp start --project-dir ${tempDir}`);
|
|
|
|
|
|
expect(io.stdout()).toContain('If you need to stop MCP later:');
|
|
|
|
|
|
expect(io.stdout()).toContain(`ktx mcp stop --project-dir ${tempDir}`);
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(io.stdout()).toContain('All set.');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(io.stdout()).not.toContain('Finish agent setup');
|
|
|
|
|
|
expect(io.stdout()).not.toContain('Next actions');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('can return agent next actions without printing them', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
showNextActions: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
nextActions: expect.stringContaining(`ktx mcp start --project-dir ${tempDir}`),
|
|
|
|
|
|
});
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(io.stdout()).toContain('Claude Code · Project scope');
|
|
|
|
|
|
expect(io.stdout()).not.toContain('Agent integration complete');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(io.stdout()).not.toContain('Required before using agents');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(io.stdout()).not.toContain('All set.');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
it('installs the analytics skill from the runtime asset', async () => {
|
2026-05-15 02:35:09 +02:00
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'universal',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({ status: 'ready' });
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const analyticsSkill = await readFile(join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), 'utf-8');
|
|
|
|
|
|
expect(analyticsSkill).toContain('name: ktx-analytics');
|
|
|
|
|
|
expect(analyticsSkill).toContain('Always run `discover_data` before writing SQL.');
|
|
|
|
|
|
expect(analyticsSkill).toContain('Treat a `dictionary_search` miss as non-authoritative.');
|
|
|
|
|
|
expect(analyticsSkill).toContain('memory_ingest');
|
|
|
|
|
|
expect(analyticsSkill).toContain('ARR is reported in cents');
|
|
|
|
|
|
expect(analyticsSkill).not.toContain(`memory_${'capture'}`);
|
2026-05-15 02:35:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-12 23:51:46 +02:00
|
|
|
|
it('writes PATH-independent launcher commands for skills', async () => {
|
2026-05-10 16:01:58 -07:00
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'universal',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-10 16:01:58 -07:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({ status: 'ready' });
|
|
|
|
|
|
|
|
|
|
|
|
const skill = await readFile(join(tempDir, '.agents/skills/ktx/SKILL.md'), 'utf-8');
|
|
|
|
|
|
expect(skill).not.toContain('`ktx agent');
|
2026-05-13 13:01:56 +02:00
|
|
|
|
expect(skill).toContain('status --json');
|
|
|
|
|
|
expect(skill).toContain('sl query');
|
2026-05-15 15:31:51 -04:00
|
|
|
|
expect(skill).toContain('--format json');
|
|
|
|
|
|
expect(skill).not.toContain('sl query --json');
|
2026-05-13 13:01:56 +02:00
|
|
|
|
expect(skill).not.toContain('sql execute');
|
2026-05-10 16:01:58 -07:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-15 02:35:09 +02:00
|
|
|
|
it('writes Claude Code project MCP config and tracks the json key', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({ status: 'ready' });
|
|
|
|
|
|
|
|
|
|
|
|
const mcpJson = JSON.parse(await readFile(join(tempDir, '.mcp.json'), 'utf-8')) as {
|
|
|
|
|
|
mcpServers: { ktx: { type: string; url: string; headers?: Record<string, string> } };
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(mcpJson.mcpServers.ktx).toEqual({ type: 'http', url: 'http://localhost:7878/mcp' });
|
|
|
|
|
|
expect(await readKtxAgentInstallManifest(tempDir)).toMatchObject({
|
|
|
|
|
|
entries: expect.arrayContaining([{ kind: 'json-key', path: join(tempDir, '.mcp.json'), jsonPath: ['mcpServers', 'ktx'] }]),
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
chore(workspace): gate dead-code with knip production mode (#196)
* refactor(workspace): relocate @ktx/llm source into packages/cli/src/llm
* refactor(workspace): rewrite @ktx/llm imports to relative paths
* refactor(workspace): fold internal packages into cli
* chore(workspace): gate dead-code with knip production mode
Turn on production-mode knip plus an autofix run in pre-commit and the
`pnpm dead-code` script, document the `/** @internal */` convention for
test-only exports in AGENTS.md, annotate test-only exports across the
CLI with that JSDoc, and drop dead exports/wrappers the new gate
surfaced (e.g. `cli-project.ts`, `lookerRuntimeSourceToFileAdapterSource`,
`createLocalScanEnrichmentProvidersFromConfig`,
`PGLITE_OWNER_PROCESS_BACKEND_CAPABILITIES`, stale type re-exports).
Replace the loose `ignoreIssues` allowlist in `knip.json` with explicit
production entries so cross-package barrel leaks are caught.
* refactor(cli): delete internal barrel index.ts files
The 34 `index.ts` re-export barrels inside `packages/cli/src/` were
holdovers from the pre-fold multi-workspace structure. Post-fold-in they
served no production purpose: external consumers go through the single
package main entry, and in-repo callers mostly imported through them
only because the path was short. Internally, knip flagged most barrel
re-exports as production-dead (only reached via tests).
This change:
- Deletes every internal barrel except `packages/cli/src/index.ts`
(the published package entry).
- Rewrites ~270 source/test files to import each name directly from
the file that defines it.
- Moves `tools/warehouse-verification/index.ts` to
`create-warehouse-verification-tools.ts` (the function it defined
locally) and updates its single consumer.
- Renames `search/backend-conformance.ts` → `.test-utils.ts` to match
the existing test-helper file convention.
- Deletes 13 dead test-only chains (dbt-descriptions/*,
live-database/extracted-schema, live-database/structural-sync,
relationship-* feedback/review chain) plus their tests and a
cascading orphan integration test.
- Updates test mocks that pointed at deleted barrel paths
(notion-client, connector barrels in scan/local-scan-connectors
tests) to mock the source files instead.
- Points the maintainer benchmark script
(`scripts/relationship-benchmark-report.mjs`) at source files
instead of `dist/context/scan/index.js`.
- Drops the barrel `!` entries from `knip.json`; adds explicit
production entries only for the benchmark code reached via dist by
the maintainer script.
Net: 413 files changed, ~1.2k insertions, ~9.4k deletions.
`pnpm run dead-code` (Biome + knip default + knip production) and
`pnpm run type-check` are clean; 2277 tests pass.
* refactor(workspace): rename @ktx/cli to @kaelio/ktx and pack it directly
Promote the CLI workspace package to the public name `@kaelio/ktx` and
drop the separate `scripts/build-public-npm-package.mjs` wrapper. The
CLI package is now publishable in place (`publishConfig.access: public`,
`provenance: true`), so artifact packing uses `pnpm pack` against
`packages/cli/` instead of assembling a parallel package tree.
Updates all workspace filter invocations, docs, tests, and release
readiness checks to reference the new package name, and folds the
tarball-name helper into `scripts/public-npm-release-metadata.mjs`.
* docs: align "agent clients" and "data agents" terminology
Replace "client agents" with "agent clients" and "database agents" with
"data agents" across AGENTS.md, README.md, the docs-site copy, and the
matching setup-agents test description, matching the canonical
vocabulary in docs/terminology.md.
Also moves packages/cli/tsconfig.json's tsBuildInfoFile from
node_modules/.cache/ to dist/.tsbuildinfo so incremental builds survive
node_modules reinstalls.
* refactor(release): single source of truth for package version
Make packages/cli/package.json the single source of truth for the
@kaelio/ktx version. publicNpmPackageVersion() now reads it directly,
so artifact filenames, release-readiness checks, and the Python wheel
version all derive from one field. The duplicate
release-policy.json.publicNpmPackageVersion is removed.
Previously the two fields could drift: tarballs were named
kaelio-ktx-0.4.1.tgz while internally containing
@kaelio/ktx@0.0.0-private.
- update-public-release-version.mjs rewrites both Python pyproject.toml
files (ktx-daemon, ktx-sl) alongside the npm package.jsons,
normalizing the version for PEP 440 (e.g. 0.1.0-rc.2 -> 0.1.0rc2).
- semantic-release-config.cjs adds the two pyproject.toml files to
@semantic-release/git assets so the release commit back to main
carries every version source in lockstep.
- The six "?? '0.0.0-private'" fallback literals across the CLI are
replaced with "?? getKtxCliPackageInfo().version", and
createDefaultKtxMcpServer makes its version arg required.
- docs/release.md describes the actual commit-back model: the dev tree
always reflects the most recent release; no sentinel pin to
maintain.
Verified: pnpm run artifacts:build now produces
kaelio-ktx-0.4.1.tgz and kaelio_ktx-0.4.1-py3-none-any.whl with
@kaelio/ktx@0.4.1 inside. Full type-check, dead-code, and
2287 vitests + 173 script tests pass.
* refactor(cli): inject embedding provider resolution and detect sentence-transformers runtime
Make resolveProjectEmbeddingProvider and runtimeIo injectable in ingest and
scan command entrypoints so tests can stub them, and teach
resolvePublicIngestRuntimeRequirements to flag the local-embeddings runtime
feature when ktx.yaml selects sentence-transformers.
* chore(cli): mark buildLocalStatsStatus and LocalStatsStatus as @internal
Both symbols are consumed only by status-project.test.ts. Annotating with
/** @internal */ keeps knip's production-mode check clean without changing
runtime behavior.
* fix(cli): use real package metadata in print-command-tree
The stubbed package name embedded a forbidden product identifier that
tripped the boundary check in CI. Read the metadata from package.json
instead — keeps the rendered tree unchanged and removes a duplicate
source of truth.
* feat(cli): show embedding coverage in `ktx status`, drop duplicate disk counts
Inline `(N embedded)` next to the Wiki scope counts and Semantic-layer
source counts, computed with `SUM(embedding_json IS NOT NULL)` over
`knowledge_pages` and `local_sl_sources`. Rename the "Knowledge" label to
"Wiki" (canonical per `docs/terminology.md`) and rename the matching
`localStats.knowledgePages` field to `localStats.wikiPages`.
Drop `wiki=N md` and `semantic-layer=N yaml` from the Disk row — those
duplicated the per-surface rows above. Disk now reports only actual byte
usage (db, cache, raw-sources). The unused `wikiGlobalMarkdownCount` /
`semanticLayerYamlCount` fields, the `isMarkdownEntry` / `isYamlEntry`
helpers, and the `filter` arg on `summarizeDir` are removed.
2026-05-21 15:28:58 +02:00
|
|
|
|
it('prompts for MCP-first agent client connection mode in interactive setup', async () => {
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) => (message.startsWith('Where') ? 'project' : 'mcp')),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
expect(prompts.select).toHaveBeenCalledWith({
|
2026-06-11 13:49:45 +02:00
|
|
|
|
message: 'What should agents be allowed to do with this ktx project?',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
options: [
|
2026-05-18 18:54:20 -04:00
|
|
|
|
{
|
|
|
|
|
|
value: 'mcp',
|
2026-06-11 13:49:45 +02:00
|
|
|
|
label: 'Ask data questions with ktx MCP',
|
2026-05-18 18:54:20 -04:00
|
|
|
|
hint: 'Installs the MCP connection and analytics workflow skill. Best for normal use.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'mcp-cli',
|
2026-06-11 13:49:45 +02:00
|
|
|
|
label: 'Ask data questions + manage ktx with CLI commands',
|
2026-05-18 18:54:20 -04:00
|
|
|
|
hint: 'Adds an admin CLI skill so agents can run ktx status, sl, wiki, and setup commands.',
|
|
|
|
|
|
},
|
2026-05-24 19:30:06 +02:00
|
|
|
|
{
|
|
|
|
|
|
value: 'skip',
|
|
|
|
|
|
label: 'Skip agent setup for now',
|
|
|
|
|
|
hint: 'Leaves agent integration incomplete. You can run ktx setup --agents later.',
|
|
|
|
|
|
},
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(prompts.multiselect).toHaveBeenCalledWith(
|
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
|
options: expect.arrayContaining([{ value: 'claude-desktop', label: 'Claude Desktop' }]),
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-24 19:30:06 +02:00
|
|
|
|
it('lets interactive setup skip agent integration from the connection mode prompt', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async () => 'skip'),
|
|
|
|
|
|
multiselect: vi.fn(async () => {
|
|
|
|
|
|
throw new Error('target selection should not run');
|
|
|
|
|
|
}),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
2026-05-24 19:30:06 +02:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({ status: 'skipped', projectDir: tempDir });
|
|
|
|
|
|
|
|
|
|
|
|
expect(prompts.select).toHaveBeenCalledWith({
|
2026-06-11 13:49:45 +02:00
|
|
|
|
message: 'What should agents be allowed to do with this ktx project?',
|
2026-05-24 19:30:06 +02:00
|
|
|
|
options: [
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'mcp',
|
2026-06-11 13:49:45 +02:00
|
|
|
|
label: 'Ask data questions with ktx MCP',
|
2026-05-24 19:30:06 +02:00
|
|
|
|
hint: 'Installs the MCP connection and analytics workflow skill. Best for normal use.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'mcp-cli',
|
2026-06-11 13:49:45 +02:00
|
|
|
|
label: 'Ask data questions + manage ktx with CLI commands',
|
2026-05-24 19:30:06 +02:00
|
|
|
|
hint: 'Adds an admin CLI skill so agents can run ktx status, sl, wiki, and setup commands.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'skip',
|
|
|
|
|
|
label: 'Skip agent setup for now',
|
|
|
|
|
|
hint: 'Leaves agent integration incomplete. You can run ktx setup --agents later.',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(prompts.multiselect).not.toHaveBeenCalled();
|
|
|
|
|
|
expect(io.stdout()).toContain('Agent integration skipped.');
|
|
|
|
|
|
await expect(stat(join(tempDir, '.ktx/agents/install-manifest.json'))).rejects.toThrow();
|
|
|
|
|
|
expect(await readKtxSetupState(tempDir)).toEqual({ completed_steps: [] });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
it('prompts for global scope when every selected target supports it', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where should') ? 'global' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
cwd: tempDir,
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'global', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
expect(prompts.select).toHaveBeenCalledWith({
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
message: `Where should ktx install agent config?\n\nktx project: ${tempDir}`,
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
options: [
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
{ value: 'project', label: 'ktx project directory', hint: tempDir },
|
|
|
|
|
|
{ value: 'custom', label: 'Custom directory…', hint: 'Enter a path' },
|
2026-05-18 18:54:20 -04:00
|
|
|
|
{
|
|
|
|
|
|
value: 'global',
|
|
|
|
|
|
label: 'Global scope (user config)',
|
2026-06-11 13:49:45 +02:00
|
|
|
|
hint: 'Agents can load this ktx project from any working directory.',
|
2026-05-18 18:54:20 -04:00
|
|
|
|
},
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
it('registers Claude Desktop MCP and ships an uploadable analytics skill zip', async () => {
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
const envSnapshot = captureEnvKeys(process.env, ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY']);
|
|
|
|
|
|
const ktxEnvSnapshot = captureKtxEnv(process.env);
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
clearEnvKeys(process.env, ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY']);
|
|
|
|
|
|
clearKtxEnv(process.env);
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-desktop',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-desktop', scope: 'global', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
const analyticsSkillPath = join(tempDir, '.ktx/agents/claude/ktx-analytics.zip');
|
|
|
|
|
|
const adminSkillPath = join(tempDir, '.ktx/agents/claude/ktx.zip');
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const launcherPath = join(tempDir, '.ktx/agents/claude/ktx-plugin-runner.sh');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(stat(analyticsSkillPath)).resolves.toBeDefined();
|
|
|
|
|
|
await expect(stat(adminSkillPath)).rejects.toThrow();
|
2026-05-24 16:57:23 +02:00
|
|
|
|
await expect(stat(launcherPath)).rejects.toThrow();
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
|
|
|
|
|
|
const configPath = join(home, 'Library/Application Support/Claude/claude_desktop_config.json');
|
|
|
|
|
|
const config = JSON.parse(await readFile(configPath, 'utf-8')) as {
|
|
|
|
|
|
mcpServers: { ktx: { command: string; args: string[]; env?: Record<string, string> } };
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(config.mcpServers.ktx).toEqual({
|
2026-05-24 16:57:23 +02:00
|
|
|
|
command: process.execPath,
|
|
|
|
|
|
args: [expect.stringContaining('bin.js'), '--project-dir', tempDir, 'mcp', 'stdio'],
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(await readZipText(analyticsSkillPath, 'ktx-analytics/SKILL.md')).toContain('ktx Analytics Workflow');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(readZipText(analyticsSkillPath, 'ktx/SKILL.md')).rejects.toThrow('Missing zip entry');
|
|
|
|
|
|
await expect(readZipText(analyticsSkillPath, '.claude-plugin/plugin.json')).rejects.toThrow('Missing zip entry');
|
|
|
|
|
|
await expect(readZipText(analyticsSkillPath, 'skills/ktx-analytics/SKILL.md')).rejects.toThrow(
|
|
|
|
|
|
'Missing zip entry',
|
|
|
|
|
|
);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(io.stdout()).toContain('Claude Desktop');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(io.stdout()).toContain(analyticsSkillPath);
|
|
|
|
|
|
expect(io.stdout()).not.toContain(adminSkillPath);
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(io.stdout()).toContain('claude_desktop_config.json');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(io.stdout()).toContain('Required before using agents');
|
|
|
|
|
|
expect(io.stdout()).toContain('1. Restart Claude Desktop');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(io.stdout()).toContain('Claude Desktop loads ktx MCP after restart.');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(io.stdout()).toContain('2. Upload Claude Desktop skills');
|
|
|
|
|
|
expect(io.stdout()).toContain('Customize > Skills > + > Create skill > Upload a skill');
|
|
|
|
|
|
expect(io.stdout()).toContain('Upload this file:');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(io.stdout()).toContain('Toggle the uploaded ktx skills on.');
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(io.stdout()).not.toContain('Run `ktx mcp start`');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
restoreEnvKeys(process.env, envSnapshot);
|
|
|
|
|
|
restoreEnvKeys(process.env, ktxEnvSnapshot);
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('captures KTX_*, OPENAI_API_KEY, and ANTHROPIC_API_KEY into the Claude Desktop MCP env block', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
const envSnapshot = captureEnvKeys(process.env, [
|
|
|
|
|
|
'OPENAI_API_KEY',
|
|
|
|
|
|
'ANTHROPIC_API_KEY',
|
|
|
|
|
|
'KTX_LOG_LEVEL',
|
|
|
|
|
|
]);
|
|
|
|
|
|
const ktxEnvSnapshot = captureKtxEnv(process.env);
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
clearKtxEnv(process.env);
|
|
|
|
|
|
process.env.OPENAI_API_KEY = 'sk-test-openai'; // pragma: allowlist secret
|
|
|
|
|
|
process.env.ANTHROPIC_API_KEY = 'sk-ant-test'; // pragma: allowlist secret
|
|
|
|
|
|
process.env.KTX_LOG_LEVEL = 'debug';
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-desktop',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const configPath = join(home, 'Library/Application Support/Claude/claude_desktop_config.json');
|
|
|
|
|
|
const config = JSON.parse(await readFile(configPath, 'utf-8')) as {
|
|
|
|
|
|
mcpServers: { ktx: { env?: Record<string, string> } };
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(config.mcpServers.ktx.env).toEqual({
|
|
|
|
|
|
OPENAI_API_KEY: 'sk-test-openai', // pragma: allowlist secret
|
|
|
|
|
|
ANTHROPIC_API_KEY: 'sk-ant-test', // pragma: allowlist secret
|
|
|
|
|
|
KTX_LOG_LEVEL: 'debug',
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
restoreEnvKeys(process.env, envSnapshot);
|
|
|
|
|
|
restoreEnvKeys(process.env, ktxEnvSnapshot);
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
it('includes an uploadable admin CLI skill zip for Claude Desktop when requested', async () => {
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-desktop',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-desktop', scope: 'global', mode: 'mcp-cli' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
const analyticsSkillPath = join(tempDir, '.ktx/agents/claude/ktx-analytics.zip');
|
|
|
|
|
|
const adminSkillPath = join(tempDir, '.ktx/agents/claude/ktx.zip');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(await readZipText(analyticsSkillPath, 'ktx-analytics/SKILL.md')).toContain('ktx Analytics Workflow');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(readZipText(analyticsSkillPath, 'ktx/SKILL.md')).rejects.toThrow('Missing zip entry');
|
|
|
|
|
|
const adminSkill = await readZipText(adminSkillPath, 'ktx/SKILL.md');
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
expect(adminSkill).toContain(`--project-dir ${tempDir}`);
|
|
|
|
|
|
expect(adminSkill).toContain('status --json');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(readZipText(adminSkillPath, '.mcp.json')).rejects.toThrow('Missing zip entry');
|
|
|
|
|
|
await expect(readZipText(adminSkillPath, 'ktx-analytics/SKILL.md')).rejects.toThrow('Missing zip entry');
|
|
|
|
|
|
expect(io.stdout()).toContain(analyticsSkillPath);
|
|
|
|
|
|
expect(io.stdout()).toContain(adminSkillPath);
|
|
|
|
|
|
expect(io.stdout()).toContain('Upload each file separately:');
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('installs MCP client config and analytics skill without admin CLI files', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const mcpJson = JSON.parse(await readFile(join(tempDir, '.mcp.json'), 'utf-8')) as {
|
|
|
|
|
|
mcpServers: { ktx: { type: string; url: string } };
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(mcpJson.mcpServers.ktx).toEqual({ type: 'http', url: 'http://localhost:7878/mcp' });
|
|
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx/SKILL.md'))).rejects.toThrow();
|
|
|
|
|
|
await expect(stat(join(tempDir, '.claude/rules/ktx.md'))).rejects.toThrow();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-15 02:35:09 +02:00
|
|
|
|
it('writes Cursor project MCP config', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'cursor',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const cursorJson = JSON.parse(await readFile(join(tempDir, '.cursor/mcp.json'), 'utf-8')) as {
|
|
|
|
|
|
mcpServers: { ktx: { url: string; headers?: Record<string, string> } };
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(cursorJson.mcpServers.ktx).toEqual({ url: 'http://localhost:7878/mcp' });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
it('prints Codex, opencode, and universal snippets without mutating printed-only config files', async () => {
|
2026-05-15 02:35:09 +02:00
|
|
|
|
const codexIo = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'codex',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
codexIo.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(codexIo.stdout()).toContain('[mcp_servers.ktx]');
|
|
|
|
|
|
expect(codexIo.stdout()).toContain('url = "http://localhost:7878/mcp"');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(codexIo.stdout()).toContain('1. Configure Codex');
|
|
|
|
|
|
expect(codexIo.stdout()).toContain('Open ~/.codex/config.toml, then paste this block:');
|
|
|
|
|
|
expect(codexIo.stdout()).toContain('PASTE:');
|
2026-05-15 02:35:09 +02:00
|
|
|
|
|
|
|
|
|
|
const opencodeIo = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'opencode',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
opencodeIo.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(opencodeIo.stdout()).toContain('"mcp"');
|
|
|
|
|
|
expect(opencodeIo.stdout()).toContain('"type": "remote"');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(opencodeIo.stdout()).toContain('1. Configure OpenCode');
|
|
|
|
|
|
expect(opencodeIo.stdout()).toContain('Open opencode.json, then paste this block:');
|
2026-05-15 02:35:09 +02:00
|
|
|
|
await expect(readFile(join(tempDir, 'opencode.json'), 'utf-8')).rejects.toThrow();
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
|
|
|
|
|
|
const universalIo = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'universal',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
universalIo.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(universalIo.stdout()).toContain('Universal MCP endpoint:');
|
|
|
|
|
|
expect(universalIo.stdout()).toContain('http://localhost:7878/mcp');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(universalIo.stdout()).toContain('1. Configure unsupported MCP clients');
|
|
|
|
|
|
expect(universalIo.stdout()).toContain('Use this endpoint when setting up unsupported MCP clients:');
|
2026-05-15 02:35:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('uses MCP daemon state for port and token metadata without rendering literal tokens', async () => {
|
|
|
|
|
|
await mkdir(join(tempDir, '.ktx'), { recursive: true });
|
|
|
|
|
|
await writeFile(
|
|
|
|
|
|
join(tempDir, '.ktx/mcp.json'),
|
|
|
|
|
|
`${JSON.stringify(
|
|
|
|
|
|
{
|
|
|
|
|
|
schemaVersion: 1,
|
|
|
|
|
|
pid: 999999,
|
|
|
|
|
|
host: '127.0.0.1',
|
|
|
|
|
|
port: 8787,
|
|
|
|
|
|
tokenAuth: true,
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
startedAt: '2026-05-14T00:00:00.000Z',
|
|
|
|
|
|
logPath: join(tempDir, '.ktx/logs/mcp.log'),
|
|
|
|
|
|
},
|
|
|
|
|
|
null,
|
|
|
|
|
|
2,
|
|
|
|
|
|
)}\n`,
|
|
|
|
|
|
'utf-8',
|
|
|
|
|
|
);
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const previousToken = process.env.KTX_MCP_TOKEN;
|
|
|
|
|
|
process.env.KTX_MCP_TOKEN = 'secret-token';
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const rendered = JSON.stringify(JSON.parse(await readFile(join(tempDir, '.mcp.json'), 'utf-8')));
|
|
|
|
|
|
expect(rendered).toContain('http://127.0.0.1:8787/mcp');
|
|
|
|
|
|
expect(rendered).toContain('Bearer ${KTX_MCP_TOKEN}');
|
|
|
|
|
|
expect(rendered).not.toContain('secret-token');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(io.stdout()).toContain('Run this command before using Claude Code:');
|
|
|
|
|
|
expect(io.stdout()).toContain('RUN:');
|
|
|
|
|
|
expect(io.stdout()).toContain(`ktx mcp start --project-dir ${tempDir}`);
|
2026-05-15 02:35:09 +02:00
|
|
|
|
} finally {
|
|
|
|
|
|
if (previousToken === undefined) {
|
|
|
|
|
|
delete process.env.KTX_MCP_TOKEN;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
process.env.KTX_MCP_TOKEN = previousToken;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('writes Claude Code local MCP config under the project key in ~/.claude.json', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'local',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-15 02:35:09 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const config = JSON.parse(await readFile(join(home, '.claude.json'), 'utf-8')) as {
|
|
|
|
|
|
projects: Record<string, { mcpServers: { ktx: { type: string; url: string } } }>;
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(config.projects[tempDir].mcpServers.ktx).toEqual({ type: 'http', url: 'http://localhost:7878/mcp' });
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-12 23:51:46 +02:00
|
|
|
|
it('removes only manifest-listed files', async () => {
|
2026-05-10 23:12:26 +02:00
|
|
|
|
const io = makeIo();
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await runKtxSetupAgentsStep(
|
2026-05-10 23:12:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
2026-05-12 23:51:46 +02:00
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-12 23:51:46 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
2026-05-10 23:12:26 +02:00
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await writeFile(join(tempDir, '.claude/skills/ktx/keep.txt'), 'user file', 'utf-8');
|
2026-05-10 23:12:26 +02:00
|
|
|
|
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await expect(removeKtxAgentInstall(tempDir, io.io)).resolves.toBe(0);
|
2026-05-10 23:12:26 +02:00
|
|
|
|
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx/SKILL.md'))).rejects.toThrow();
|
2026-05-10 23:13:17 -07:00
|
|
|
|
await expect(stat(join(tempDir, '.claude/rules/ktx.md'))).rejects.toThrow();
|
2026-05-10 23:51:24 +02:00
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx/keep.txt'))).resolves.toBeDefined();
|
|
|
|
|
|
await expect(readKtxAgentInstallManifest(tempDir)).resolves.toEqual(null);
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
it('removes generated Claude Desktop skill zips from the manifest', async () => {
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-desktop',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
2026-05-19 15:21:49 +02:00
|
|
|
|
const analyticsSkillPath = join(tempDir, '.ktx/agents/claude/ktx-analytics.zip');
|
|
|
|
|
|
const adminSkillPath = join(tempDir, '.ktx/agents/claude/ktx.zip');
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const launcherPath = join(tempDir, '.ktx/agents/claude/ktx-plugin-runner.sh');
|
|
|
|
|
|
const configPath = join(home, 'Library/Application Support/Claude/claude_desktop_config.json');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(stat(analyticsSkillPath)).resolves.toBeDefined();
|
|
|
|
|
|
await expect(stat(adminSkillPath)).resolves.toBeDefined();
|
2026-05-24 16:57:23 +02:00
|
|
|
|
await expect(stat(launcherPath)).rejects.toThrow();
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const beforeConfig = JSON.parse(await readFile(configPath, 'utf-8')) as {
|
|
|
|
|
|
mcpServers: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(beforeConfig.mcpServers.ktx).toBeDefined();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(removeKtxAgentInstall(tempDir, io.io)).resolves.toBe(0);
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
await expect(stat(analyticsSkillPath)).rejects.toThrow();
|
|
|
|
|
|
await expect(stat(adminSkillPath)).rejects.toThrow();
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
const afterConfig = JSON.parse(await readFile(configPath, 'utf-8')) as {
|
|
|
|
|
|
mcpServers: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(afterConfig.mcpServers.ktx).toBeUndefined();
|
|
|
|
|
|
await expect(readKtxAgentInstallManifest(tempDir)).resolves.toEqual(null);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-12 21:31:11 -07:00
|
|
|
|
it('treats cancel as skip in interactive mode', async () => {
|
2026-05-10 23:12:26 +02:00
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async () => 'back'),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['codex']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
2026-05-10 23:12:26 +02:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
2026-05-10 23:51:24 +02:00
|
|
|
|
runKtxSetupAgentsStep(
|
2026-05-10 23:12:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-10 23:12:26 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
2026-05-12 21:31:11 -07:00
|
|
|
|
).resolves.toEqual({ status: 'skipped', projectDir: tempDir });
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
feat(cli): setup progress spinners, Tab-to-select, and banner polish (#296)
* fix(cli): double the height of the setup banner t crossbar
* fix(cli): unify setup multi-select hints and make Tab the select key
The six interactive multi-select surfaces in `ktx setup` documented three
different hint voices, one had no hint at all, and they named two different
select keys (Space vs Tab). Tab is the only key that can toggle selection
without colliding with type-to-search input, so make it the single documented
select key everywhere and compose every hint from one shared fragment
vocabulary in prompt-navigation.ts.
- Register `updateSettings({ aliases: { tab: 'space' } })` so Tab toggles flat
multiselects; the alias applies only to non-text prompts, leaving typed
search input (schema/Notion) untouched.
- Add the missing hint to the agent-targets prompt and drop the stray
"Space to select … Esc …" info line plus the now-dead writeSetupInfo helper.
- Replace the schema-scope ad-hoc hint with the searchable-multiselect voice
and standardize "filter" -> "search" vocabulary.
- Delete DEFAULT_TREE_PICKER_HELP_TEXT and the unused TreePickerChrome.helpText
seam; render the shared tree hint instead.
* refactor(cli): show LLM check progress for every setup backend
Rename runLlmHealthCheckWithProgress to validateModelWithProgress and
wrap the Claude subscription and Codex auth probes in the same spinner
progress as the Anthropic API and Vertex backends, so each backend shows
consistent "Checking <provider> LLM" output during setup.
* feat(cli): add ktx-orange progress spinners to setup steps
Add a shared runWithCliSpinner helper and a TTY-aware createCliSpinner:
an animated clack spinner in a terminal, and a static stderr-only spinner
before raw-mode pickers (the table tree picker and demo tour), where the
animated spinner's stdin grab would otherwise corrupt the next prompt.
Wrap the slow setup waits in progress spinners: managed runtime install,
embedding daemon start + first-run model download, embeddings health
check, the connection-test gate, and source validation / dbt clone /
Metabase discovery. Recolor every spinner frame from clack's magenta to
the ktx mascot orange (#FF8A4C) via the static helper and clack's
styleFrame option.
2026-06-12 16:43:10 +02:00
|
|
|
|
it('wraps the agent target prompt with the navigation hint and prints no separate hint line', async () => {
|
2026-05-10 23:12:26 +02:00
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
select: vi.fn(async () => 'mcp-cli'),
|
2026-05-10 23:12:26 +02:00
|
|
|
|
multiselect: vi.fn(async () => ['back']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
2026-05-10 23:12:26 +02:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
2026-05-10 23:51:24 +02:00
|
|
|
|
runKtxSetupAgentsStep(
|
2026-05-10 23:12:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-10 23:12:26 +02:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toEqual({ status: 'back', projectDir: tempDir });
|
|
|
|
|
|
|
|
|
|
|
|
expect(prompts.multiselect).toHaveBeenCalledWith(
|
|
|
|
|
|
expect.objectContaining({
|
feat(cli): setup progress spinners, Tab-to-select, and banner polish (#296)
* fix(cli): double the height of the setup banner t crossbar
* fix(cli): unify setup multi-select hints and make Tab the select key
The six interactive multi-select surfaces in `ktx setup` documented three
different hint voices, one had no hint at all, and they named two different
select keys (Space vs Tab). Tab is the only key that can toggle selection
without colliding with type-to-search input, so make it the single documented
select key everywhere and compose every hint from one shared fragment
vocabulary in prompt-navigation.ts.
- Register `updateSettings({ aliases: { tab: 'space' } })` so Tab toggles flat
multiselects; the alias applies only to non-text prompts, leaving typed
search input (schema/Notion) untouched.
- Add the missing hint to the agent-targets prompt and drop the stray
"Space to select … Esc …" info line plus the now-dead writeSetupInfo helper.
- Replace the schema-scope ad-hoc hint with the searchable-multiselect voice
and standardize "filter" -> "search" vocabulary.
- Delete DEFAULT_TREE_PICKER_HELP_TEXT and the unused TreePickerChrome.helpText
seam; render the shared tree hint instead.
* refactor(cli): show LLM check progress for every setup backend
Rename runLlmHealthCheckWithProgress to validateModelWithProgress and
wrap the Claude subscription and Codex auth probes in the same spinner
progress as the Anthropic API and Vertex backends, so each backend shows
consistent "Checking <provider> LLM" output during setup.
* feat(cli): add ktx-orange progress spinners to setup steps
Add a shared runWithCliSpinner helper and a TTY-aware createCliSpinner:
an animated clack spinner in a terminal, and a static stderr-only spinner
before raw-mode pickers (the table tree picker and demo tour), where the
animated spinner's stdin grab would otherwise corrupt the next prompt.
Wrap the slow setup waits in progress spinners: managed runtime install,
embedding daemon start + first-run model download, embeddings health
check, the connection-test gate, and source validation / dbt clone /
Metabase discovery. Recolor every spinner frame from clack's magenta to
the ktx mascot orange (#FF8A4C) via the static helper and clack's
styleFrame option.
2026-06-12 16:43:10 +02:00
|
|
|
|
message:
|
|
|
|
|
|
'Which agent targets should ktx install?\n' +
|
|
|
|
|
|
'Up/Down to move, Tab to select or unselect, Enter to confirm, Escape to go back, Ctrl+C to exit.',
|
2026-05-10 23:12:26 +02:00
|
|
|
|
}),
|
|
|
|
|
|
);
|
feat(cli): setup progress spinners, Tab-to-select, and banner polish (#296)
* fix(cli): double the height of the setup banner t crossbar
* fix(cli): unify setup multi-select hints and make Tab the select key
The six interactive multi-select surfaces in `ktx setup` documented three
different hint voices, one had no hint at all, and they named two different
select keys (Space vs Tab). Tab is the only key that can toggle selection
without colliding with type-to-search input, so make it the single documented
select key everywhere and compose every hint from one shared fragment
vocabulary in prompt-navigation.ts.
- Register `updateSettings({ aliases: { tab: 'space' } })` so Tab toggles flat
multiselects; the alias applies only to non-text prompts, leaving typed
search input (schema/Notion) untouched.
- Add the missing hint to the agent-targets prompt and drop the stray
"Space to select … Esc …" info line plus the now-dead writeSetupInfo helper.
- Replace the schema-scope ad-hoc hint with the searchable-multiselect voice
and standardize "filter" -> "search" vocabulary.
- Delete DEFAULT_TREE_PICKER_HELP_TEXT and the unused TreePickerChrome.helpText
seam; render the shared tree hint instead.
* refactor(cli): show LLM check progress for every setup backend
Rename runLlmHealthCheckWithProgress to validateModelWithProgress and
wrap the Claude subscription and Codex auth probes in the same spinner
progress as the Anthropic API and Vertex backends, so each backend shows
consistent "Checking <provider> LLM" output during setup.
* feat(cli): add ktx-orange progress spinners to setup steps
Add a shared runWithCliSpinner helper and a TTY-aware createCliSpinner:
an animated clack spinner in a terminal, and a static stderr-only spinner
before raw-mode pickers (the table tree picker and demo tour), where the
animated spinner's stdin grab would otherwise corrupt the next prompt.
Wrap the slow setup waits in progress spinners: managed runtime install,
embedding daemon start + first-run model download, embeddings health
check, the connection-test gate, and source validation / dbt clone /
Metabase discovery. Recolor every spinner frame from clack's magenta to
the ktx mascot orange (#FF8A4C) via the static helper and clack's
styleFrame option.
2026-06-12 16:43:10 +02:00
|
|
|
|
expect(io.stdout()).not.toContain('Space to select');
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|
2026-05-10 23:13:17 -07:00
|
|
|
|
|
|
|
|
|
|
it('prints per-agent install summary after successful installation', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
mode: 'mcp-cli',
|
2026-05-10 23:13:17 -07:00
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(output).toContain('Claude Code · Project scope');
|
|
|
|
|
|
expect(output).toContain(join(tempDir, '.mcp.json'));
|
|
|
|
|
|
expect(output).toContain('Requires MCP to be started.');
|
|
|
|
|
|
expect(output).toContain('Analytics skill installed.');
|
|
|
|
|
|
expect(output).toContain('Admin CLI skill installed.');
|
|
|
|
|
|
expect(output).not.toContain('Agent integration complete');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).not.toContain(`ktx project\n ${tempDir}`);
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(output).not.toContain('Installed agents');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(output).not.toContain('.claude/skills/ktx-analytics/SKILL.md');
|
|
|
|
|
|
expect(output).not.toContain('.claude/skills/ktx/SKILL.md');
|
|
|
|
|
|
expect(output).not.toContain('.claude/rules/ktx.md');
|
2026-05-10 23:13:17 -07:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-18 18:54:20 -04:00
|
|
|
|
it('formats summary with explicit project-scoped config paths', () => {
|
2026-05-19 15:21:49 +02:00
|
|
|
|
const summary = formatInstallSummaryLines(
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
[{ target: 'cursor', scope: 'project', mode: 'mcp-cli' }],
|
|
|
|
|
|
[
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx-analytics.mdc'), role: 'analytics-skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx.mdc') },
|
2026-05-18 18:54:20 -04:00
|
|
|
|
{ kind: 'json-key', path: join(tempDir, '.cursor/mcp.json'), jsonPath: ['mcpServers', 'ktx'] },
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
],
|
2026-05-10 23:13:17 -07:00
|
|
|
|
tempDir,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(summary).toEqual([
|
|
|
|
|
|
{
|
|
|
|
|
|
title: 'Cursor · Project scope',
|
|
|
|
|
|
lines: [
|
|
|
|
|
|
join(tempDir, '.cursor/mcp.json'),
|
|
|
|
|
|
'Requires MCP to be started.',
|
|
|
|
|
|
'Cursor rules installed.',
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
2026-05-10 23:13:17 -07:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('formats summary with multiple agent targets', () => {
|
2026-05-19 15:21:49 +02:00
|
|
|
|
const summary = formatInstallSummaryLines(
|
2026-05-10 23:13:17 -07:00
|
|
|
|
[
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
{ target: 'claude-code', scope: 'project', mode: 'mcp-cli' },
|
|
|
|
|
|
{ target: 'codex', scope: 'project', mode: 'mcp-cli' },
|
2026-05-10 23:13:17 -07:00
|
|
|
|
],
|
|
|
|
|
|
[
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
2026-05-10 23:13:17 -07:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx/SKILL.md'), role: 'skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/rules/ktx.md'), role: 'rule' },
|
2026-05-18 18:54:20 -04:00
|
|
|
|
{ kind: 'json-key', path: join(tempDir, '.mcp.json'), jsonPath: ['mcpServers', 'ktx'] },
|
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup
Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a
local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces
the CLI-only agent install mode with MCP+analytics (default) and an optional
admin CLI skill, renames the research skill to analytics, and lets interactive
setup pick project vs global scope when every target supports it. Extracts a
shared MCP server factory used by both HTTP and stdio entrypoints.
* Add MCP agent client setup support
* Polish setup output formatting
* Add MCP tool polish design spec
Design for slimming the MCP-registered surface from 25 to 11 tools,
introducing memory_ingest, applying the per-tool polish kit (annotations,
outputSchema, .describe(), in-band error wrapping, union-drift fixes,
type-narrowed jsonToolResult), emitting progress notifications on
sql_execution + sl_query, and refining the ktx-analytics SKILL.md to
match.
* Refine MCP tool polish design spec after adversarial review iteration 1
* Refine MCP tool polish design spec after adversarial review iteration 2
* Refine MCP tool polish design spec after adversarial review iteration 3
* refactor(context): rename memory capture service to ingest
* feat(mcp): slim research tool surface
* refactor(mcp): remove admin ports from server factory
* refactor(cli): rename text ingest memory port
* docs: update analytics skill for memory ingest
* chore: verify mcp surface rename
* Add MCP tool polish v1 surface change plan
* feat(context): polish mcp tool metadata
* fix(context): enforce resolved semantic layer compute sources
* feat(context): emit mcp query progress stages
* fix(context): keep mcp progress event internal
* Add MCP tool polish v1 metadata & progress plan
* Fix CI snapshot and docs checks
2026-05-16 11:39:55 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
2026-05-12 23:51:46 +02:00
|
|
|
|
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx/SKILL.md'), role: 'skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.codex/instructions/ktx.md'), role: 'rule' },
|
2026-05-10 23:13:17 -07:00
|
|
|
|
],
|
|
|
|
|
|
tempDir,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(summary).toEqual([
|
|
|
|
|
|
{
|
|
|
|
|
|
title: 'Claude Code · Project scope',
|
|
|
|
|
|
lines: [
|
|
|
|
|
|
join(tempDir, '.mcp.json'),
|
|
|
|
|
|
'Requires MCP to be started.',
|
|
|
|
|
|
'Analytics skill installed.',
|
|
|
|
|
|
'Admin CLI skill installed.',
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: 'Codex · Project scope',
|
|
|
|
|
|
lines: [
|
|
|
|
|
|
'Add the snippet shown below to ~/.codex/config.toml.',
|
|
|
|
|
|
'Requires MCP to be started.',
|
|
|
|
|
|
'Codex guidance installed.',
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
2026-05-18 18:54:20 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('prints one target-aware next actions block for mixed agent targets', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where should') ? 'project' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code', 'claude-desktop']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
2026-05-18 18:54:20 -04:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [
|
|
|
|
|
|
{ target: 'claude-code', scope: 'project', mode: 'mcp' },
|
|
|
|
|
|
{ target: 'claude-desktop', scope: 'global', mode: 'mcp' },
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
|
|
|
|
|
expect(output).toContain('Required before using agents');
|
|
|
|
|
|
expect(output).not.toContain('Next actions');
|
|
|
|
|
|
expect(output).toContain('1. Start MCP');
|
|
|
|
|
|
expect(output).toContain('Run this command before using Claude Code:');
|
|
|
|
|
|
expect(output).toContain(`ktx mcp start --project-dir ${tempDir}`);
|
|
|
|
|
|
expect(output).toContain(`ktx mcp stop --project-dir ${tempDir}\n\n2. Open Claude Code`);
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).toContain('Open Claude Code from the ktx project directory');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(output).toContain('RUN:');
|
|
|
|
|
|
expect(output).toContain(`cd '${tempDir}'`);
|
|
|
|
|
|
expect(output).toContain('3. Restart Claude Desktop');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).toContain('Claude Desktop loads ktx MCP after restart.');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(output).toContain('4. Upload Claude Desktop skills');
|
|
|
|
|
|
expect(output).toContain('Customize > Skills > + > Create skill > Upload a skill');
|
|
|
|
|
|
expect(output).toContain(join(tempDir, '.ktx/agents/claude/ktx-analytics.zip'));
|
|
|
|
|
|
expect(output).not.toContain(join(tempDir, '.ktx/agents/claude/ktx.zip'));
|
|
|
|
|
|
expect(output).toContain('Upload this file:');
|
|
|
|
|
|
expect(output).toContain('All set.');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(output).not.toContain('Finish Claude Desktop setup');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).not.toContain('Run `ktx mcp start` to enable the configured ktx MCP server.');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('does not tell global Claude Code installs to open from the project directory', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'global',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'global', mode: 'mcp' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
|
|
|
|
|
expect(output).toContain('2. Open Claude Code');
|
|
|
|
|
|
expect(output).toContain('RUN:');
|
|
|
|
|
|
expect(output).toContain('claude');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).not.toContain('Open Claude Code from the ktx project directory');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(output).not.toContain(`cd '${tempDir}'`);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('explains next actions for Codex, Cursor, OpenCode, and universal MCP targets', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'project' : 'mcp-cli',
|
|
|
|
|
|
),
|
2026-05-18 18:54:20 -04:00
|
|
|
|
multiselect: vi.fn(async () => ['codex', 'cursor', 'opencode', 'universal']),
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
text: vi.fn(async () => undefined),
|
2026-05-18 18:54:20 -04:00
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [
|
|
|
|
|
|
{ target: 'codex', scope: 'project', mode: 'mcp-cli' },
|
|
|
|
|
|
{ target: 'cursor', scope: 'project', mode: 'mcp-cli' },
|
|
|
|
|
|
{ target: 'opencode', scope: 'project', mode: 'mcp-cli' },
|
|
|
|
|
|
{ target: 'universal', scope: 'project', mode: 'mcp-cli' },
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
|
|
|
|
|
expect(output).toContain('Required before using agents');
|
|
|
|
|
|
expect(output).toContain('1. Configure Codex');
|
|
|
|
|
|
expect(output).toContain('2. Configure OpenCode');
|
|
|
|
|
|
expect(output).toContain('3. Configure unsupported MCP clients');
|
|
|
|
|
|
expect(output).toContain('4. Start MCP');
|
|
|
|
|
|
expect(output).toContain('Run this command before using Codex, Cursor, OpenCode, and Universal .agents:');
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(output).toContain('Open Cursor from the ktx project directory');
|
2026-05-18 18:54:20 -04:00
|
|
|
|
expect(output).toContain('Open ~/.codex/config.toml, then paste this block:\n\n PASTE:\n [mcp_servers.ktx]');
|
|
|
|
|
|
expect(output).toContain('Open opencode.json, then paste this block:');
|
|
|
|
|
|
expect(output).toContain('Use this endpoint when setting up unsupported MCP clients:');
|
|
|
|
|
|
expect(output).toContain('Codex guidance installed');
|
|
|
|
|
|
expect(output).toContain('Cursor rules installed');
|
|
|
|
|
|
expect(output).toContain('OpenCode commands installed');
|
|
|
|
|
|
expect(output).toContain('.agents guidance installed');
|
2026-05-10 23:13:17 -07:00
|
|
|
|
});
|
2026-05-19 15:21:49 +02:00
|
|
|
|
|
feat(cli): let ktx setup --agents choose an install directory (#298)
Split the fused directory concept into projectDir (what the agent config
references) and installRoot (where project-scoped files are written), so
users can install .claude/, .mcp.json, skills, and rules where they open
their agent instead of only in the ktx project directory.
- Add --install-dir <path> (resolved against cwd, created if missing,
mutually exclusive with --global/--local, rejected for claude-desktop).
- Add an interactive directory menu: ktx project dir / Current directory
(hidden when it equals the project dir) / Custom directory… / Global
scope (shown only when every target supports it).
- Expand a leading ~ in typed/quoted paths so the ~/… menu hints round-trip.
- Record installRoot in the install manifest and merge key; thread it
through file planning, MCP config paths, summaries, and next actions.
- Refresh uv.lock to 0.12.0 for the editable ktx-sl and ktx-daemon packages.
2026-06-13 00:46:56 +02:00
|
|
|
|
describe('install root', () => {
|
|
|
|
|
|
it('plans project-scoped files under installRoot, leaving projectDir as the default', () => {
|
|
|
|
|
|
const installRoot = join(tempDir, 'opened-here');
|
|
|
|
|
|
expect(
|
|
|
|
|
|
plannedKtxAgentFiles({
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
installRoot,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
}),
|
|
|
|
|
|
).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(installRoot, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(installRoot, '.claude/skills/ktx/SKILL.md'), role: 'skill' },
|
|
|
|
|
|
{ kind: 'file', path: join(installRoot, '.claude/rules/ktx.md'), role: 'rule' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
expect(
|
|
|
|
|
|
plannedKtxAgentFiles({ projectDir: tempDir, target: 'claude-code', scope: 'project', mode: 'mcp' }),
|
|
|
|
|
|
).toEqual([
|
|
|
|
|
|
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('shows the install path in the summary title only when installRoot differs from projectDir', () => {
|
|
|
|
|
|
const installRoot = join(tempDir, 'app');
|
|
|
|
|
|
const custom = formatInstallSummaryLines(
|
|
|
|
|
|
[{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot }],
|
|
|
|
|
|
[
|
|
|
|
|
|
{ kind: 'file', path: join(installRoot, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
|
|
|
|
|
{ kind: 'json-key', path: join(installRoot, '.mcp.json'), jsonPath: ['mcpServers', 'ktx'] },
|
|
|
|
|
|
],
|
|
|
|
|
|
tempDir,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(custom[0].title).toBe(`Claude Code · ${installRoot}`);
|
|
|
|
|
|
|
|
|
|
|
|
const same = formatInstallSummaryLines(
|
|
|
|
|
|
[{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: tempDir }],
|
|
|
|
|
|
[],
|
|
|
|
|
|
tempDir,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(same[0].title).toBe('Claude Code · Project scope');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('installs project files and next actions under an explicit installRoot', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const installRoot = join(tempDir, 'workspace');
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp-cli',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
installRoot,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp-cli', installRoot }],
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(stat(join(installRoot, '.claude/skills/ktx/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
const mcp = JSON.parse(await readFile(join(installRoot, '.mcp.json'), 'utf-8')) as {
|
|
|
|
|
|
mcpServers?: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
expect(mcp.mcpServers).toHaveProperty('ktx');
|
|
|
|
|
|
await expect(stat(join(tempDir, '.claude/skills/ktx/SKILL.md'))).rejects.toThrow();
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
|
|
|
|
|
expect(output).toContain('Open Claude Code from the install directory:');
|
|
|
|
|
|
expect(output).toContain(`cd '${installRoot}'`);
|
|
|
|
|
|
expect(output).toContain(`ktx mcp start --project-dir ${tempDir}`);
|
|
|
|
|
|
|
|
|
|
|
|
expect(await readKtxAgentInstallManifest(tempDir)).toMatchObject({
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp-cli', installRoot }],
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('fails when an explicit installRoot points at an existing file', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const filePath = join(tempDir, 'not-a-dir');
|
|
|
|
|
|
await writeFile(filePath, 'x', 'utf-8');
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
installRoot: filePath,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
),
|
|
|
|
|
|
).resolves.toEqual({ status: 'failed', projectDir: tempDir });
|
|
|
|
|
|
expect(io.stderr()).toContain('is a file, not a directory');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('installs into the current directory and records it in the manifest', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const openedDir = join(tempDir, 'opened');
|
|
|
|
|
|
await mkdir(openedDir, { recursive: true });
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'current' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code', 'cursor']),
|
|
|
|
|
|
text: vi.fn(async () => undefined),
|
|
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
cwd: openedDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [
|
|
|
|
|
|
{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: openedDir },
|
|
|
|
|
|
{ target: 'cursor', scope: 'project', mode: 'mcp', installRoot: openedDir },
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(stat(join(openedDir, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
await expect(stat(join(openedDir, '.cursor/mcp.json'))).resolves.toBeDefined();
|
|
|
|
|
|
|
|
|
|
|
|
const output = io.stdout();
|
|
|
|
|
|
expect(output).toContain('Open Cursor from the install directory:');
|
|
|
|
|
|
expect(output).toContain(openedDir);
|
|
|
|
|
|
|
|
|
|
|
|
expect(await readKtxAgentInstallManifest(tempDir)).toMatchObject({
|
|
|
|
|
|
installs: [
|
|
|
|
|
|
{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: openedDir },
|
|
|
|
|
|
{ target: 'cursor', scope: 'project', mode: 'mcp', installRoot: openedDir },
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('creates and installs into a typed custom directory', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const customDir = join(tempDir, 'custom-target');
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'custom' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
|
|
|
|
|
text: vi.fn(async () => customDir),
|
|
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
cwd: tempDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: customDir }],
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(stat(join(customDir, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('hides the current directory row when cwd equals the ktx project directory', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string; options: Array<{ value: string }> }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'project' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
|
|
|
|
|
text: vi.fn(async () => undefined),
|
|
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
cwd: tempDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const directoryCall = prompts.select.mock.calls.find(([opts]) => opts.message.startsWith('Where'));
|
|
|
|
|
|
expect(directoryCall).toBeDefined();
|
|
|
|
|
|
expect(directoryCall?.[0].options.map((option) => option.value)).toEqual(['project', 'custom', 'global']);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('re-prompts when a typed custom directory is an existing file', async () => {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const filePath = join(tempDir, 'afile');
|
|
|
|
|
|
await writeFile(filePath, 'x', 'utf-8');
|
|
|
|
|
|
const validDir = join(tempDir, 'valid');
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'custom' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
|
|
|
|
|
text: vi.fn<() => Promise<string | undefined>>().mockResolvedValueOnce(filePath).mockResolvedValueOnce(validDir),
|
|
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
cwd: tempDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: validDir }],
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(prompts.text).toHaveBeenCalledTimes(2);
|
|
|
|
|
|
expect(io.stderr()).toContain('is a file, not a directory');
|
|
|
|
|
|
await expect(stat(join(validDir, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('expands a leading ~ in a typed custom directory', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const prompts = {
|
|
|
|
|
|
select: vi.fn(async ({ message }: { message: string }) =>
|
|
|
|
|
|
message.startsWith('Where') ? 'custom' : 'mcp',
|
|
|
|
|
|
),
|
|
|
|
|
|
multiselect: vi.fn(async () => ['claude-code']),
|
|
|
|
|
|
text: vi.fn(async () => '~/opened-here'),
|
|
|
|
|
|
cancel: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const expected = join(home, 'opened-here');
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'auto',
|
|
|
|
|
|
yes: false,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
cwd: tempDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
{ prompts },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: expected }],
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(stat(join(expected, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
await expect(stat(join(tempDir, '~'))).rejects.toThrow();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('expands a leading ~ in an explicit installRoot', async () => {
|
|
|
|
|
|
const home = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-home-'));
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = home;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const io = makeIo();
|
|
|
|
|
|
const expected = join(home, 'flagged');
|
|
|
|
|
|
|
|
|
|
|
|
const result = await runKtxSetupAgentsStep(
|
|
|
|
|
|
{
|
|
|
|
|
|
projectDir: tempDir,
|
|
|
|
|
|
inputMode: 'disabled',
|
|
|
|
|
|
yes: true,
|
|
|
|
|
|
agents: true,
|
|
|
|
|
|
target: 'claude-code',
|
|
|
|
|
|
scope: 'project',
|
|
|
|
|
|
mode: 'mcp',
|
|
|
|
|
|
skipAgents: false,
|
|
|
|
|
|
installRoot: '~/flagged',
|
|
|
|
|
|
cwd: tempDir,
|
|
|
|
|
|
},
|
|
|
|
|
|
io.io,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
expect(result).toMatchObject({
|
|
|
|
|
|
status: 'ready',
|
|
|
|
|
|
installs: [{ target: 'claude-code', scope: 'project', mode: 'mcp', installRoot: expected }],
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(stat(join(expected, '.claude/skills/ktx-analytics/SKILL.md'))).resolves.toBeDefined();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.env.HOME = previousHome;
|
|
|
|
|
|
await rm(home, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-19 15:21:49 +02:00
|
|
|
|
describe('createAgentNextActionsLineFormatter', () => {
|
|
|
|
|
|
function makeColorStdout(): { write: (chunk: string) => boolean; hasColors: () => boolean } {
|
|
|
|
|
|
return { write: () => true, hasColors: () => true };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function makePlainStdout(): { write: (chunk: string) => boolean; hasColors: () => boolean } {
|
|
|
|
|
|
return { write: () => true, hasColors: () => false };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const ESC = String.fromCharCode(27);
|
|
|
|
|
|
|
|
|
|
|
|
it('returns the line untouched when the stream cannot render colors', () => {
|
|
|
|
|
|
const format = createAgentNextActionsLineFormatter(makePlainStdout());
|
|
|
|
|
|
expect(format('2. Upload Claude Desktop skills')).toBe('2. Upload Claude Desktop skills');
|
|
|
|
|
|
expect(format(' /tmp/ktx/.ktx/agents/claude/ktx.zip')).toBe(' /tmp/ktx/.ktx/agents/claude/ktx.zip');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('styles step headings and aligns sub-prose under the title', () => {
|
|
|
|
|
|
const format = createAgentNextActionsLineFormatter(makeColorStdout());
|
|
|
|
|
|
const heading = format('2. Upload Claude Desktop skills');
|
|
|
|
|
|
expect(heading).toContain(ESC);
|
|
|
|
|
|
expect(heading).toContain('2');
|
|
|
|
|
|
expect(heading).toContain('Upload Claude Desktop skills');
|
|
|
|
|
|
expect(heading).not.toMatch(/^2\. /);
|
|
|
|
|
|
|
2026-06-11 13:49:45 +02:00
|
|
|
|
const sub = format(' Toggle the uploaded ktx skills on.');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
expect(sub).toMatch(/^ {3}/);
|
2026-06-11 13:49:45 +02:00
|
|
|
|
expect(sub).toContain('Toggle the uploaded ktx skills on.');
|
2026-05-19 15:21:49 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('renders skill bundle .zip paths as bullets and shortens HOME to ~', () => {
|
|
|
|
|
|
const previousHome = process.env.HOME;
|
|
|
|
|
|
process.env.HOME = '/tmp/test-home';
|
|
|
|
|
|
try {
|
|
|
|
|
|
const format = createAgentNextActionsLineFormatter(makeColorStdout());
|
|
|
|
|
|
const line = format(' /tmp/test-home/.ktx/agents/claude/ktx-analytics.zip');
|
|
|
|
|
|
expect(line).toContain('•');
|
|
|
|
|
|
expect(line).toContain('~/.ktx/agents/claude/ktx-analytics.zip');
|
|
|
|
|
|
expect(line).not.toContain('/tmp/test-home/');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
if (previousHome === undefined) delete process.env.HOME;
|
|
|
|
|
|
else process.env.HOME = previousHome;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('replaces breadcrumb separators with a typographic chevron', () => {
|
|
|
|
|
|
const format = createAgentNextActionsLineFormatter(makeColorStdout());
|
|
|
|
|
|
const line = format(' Open Claude Desktop: Customize > Skills > + > Create skill > Upload a skill.');
|
|
|
|
|
|
expect(line).toContain('›');
|
|
|
|
|
|
expect(line).not.toContain(' > ');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('leaves already-styled lines untouched to avoid double-wrapping', () => {
|
|
|
|
|
|
const format = createAgentNextActionsLineFormatter(makeColorStdout());
|
|
|
|
|
|
const preStyled = `${ESC}[1m2. Already styled${ESC}[22m`;
|
|
|
|
|
|
expect(format(preStyled)).toBe(preStyled);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-05-10 23:12:26 +02:00
|
|
|
|
});
|