feat(cli): clean up command surface

This commit is contained in:
Andrey Avtomonov 2026-05-12 23:51:46 +02:00
parent 60457e9407
commit e15a4ebaec
61 changed files with 406 additions and 2076 deletions

View file

@ -1,52 +0,0 @@
import { type Command, InvalidArgumentError } from '@commander-js/extra-typings';
import { type KtxCliCommandContext, resolveCommandProjectDir } from '../cli-program.js';
import { runtimeInstallPolicyFromFlags } from '../managed-python-command.js';
import type { KtxServeArgs } from '../serve.js';
import { profileMark } from '../startup-profile.js';
profileMark('module:commands/serve-commands');
function parseMcp(value: string): 'stdio' {
if (value === 'stdio') {
return 'stdio';
}
throw new InvalidArgumentError('Only stdio is supported in this phase');
}
export function registerServeCommands(program: Command, context: KtxCliCommandContext): void {
program
.command('serve')
.description('Run standalone KTX services such as MCP stdio')
.requiredOption('--mcp <mode>', 'MCP transport mode', parseMcp)
.option('--user-id <id>', 'Local user id', 'local')
.option('--semantic-compute', 'Enable semantic-layer compute', false)
.option('--semantic-compute-url <url>', 'HTTP semantic-layer compute URL')
.option('--yes', 'Install the managed Python runtime without prompting when required', false)
.option('--no-input', 'Disable interactive managed runtime installation')
.option('--database-introspection-url <url>', 'Daemon URL for live-database introspection')
.option('--execute-queries', 'Allow semantic-layer query execution', false)
.option('--memory-capture', 'Enable memory capture', false)
.option('--memory-model <model>', 'Memory capture model')
.showHelpAfterError()
.action(async (options, command): Promise<void> => {
const semanticCompute = options.semanticCompute === true || Boolean(options.semanticComputeUrl);
if (options.executeQueries === true && !semanticCompute) {
throw new Error('--execute-queries requires --semantic-compute');
}
const args: KtxServeArgs = {
mcp: options.mcp,
projectDir: resolveCommandProjectDir(command),
userId: options.userId,
semanticCompute,
semanticComputeUrl: options.semanticComputeUrl,
databaseIntrospectionUrl: options.databaseIntrospectionUrl,
executeQueries: options.executeQueries === true,
memoryCapture: options.memoryCapture === true,
memoryModel: options.memoryModel,
cliVersion: context.packageInfo.version,
runtimeInstallPolicy: runtimeInstallPolicyFromFlags(options),
};
const runner = context.deps.serveStdio ?? (await import('../serve.js')).runKtxServeStdio;
context.setExitCode(await runner(args));
});
}

View file

@ -64,13 +64,6 @@ function agentScope(value: string): 'project' | 'global' {
throw new InvalidArgumentError(`invalid choice '${value}'`);
}
function agentInstallMode(value: string): 'cli' | 'mcp' | 'both' {
if (value === 'cli' || value === 'mcp' || value === 'both') {
return value;
}
throw new InvalidArgumentError(`invalid choice '${value}'`);
}
function positiveNumber(value: string): number {
const parsed = Number.parseInt(value, 10);
if (!Number.isInteger(parsed) || parsed <= 0) {
@ -232,9 +225,6 @@ export function registerSetupCommands(program: Command, context: KtxCliCommandCo
.addOption(new Option('--agent-scope <scope>', 'Agent install scope').argParser(agentScope).default('project'))
.option('--project', 'Install agent integration into the project scope', false)
.option('--global', 'Install agent integration into the global target scope', false)
.addOption(
new Option('--agent-install-mode <mode>', 'Agent install mode').argParser(agentInstallMode).default('cli'),
)
.option('--skip-agents', 'Leave agent integration incomplete for now', false)
.option('--yes', 'Accept safe defaults in non-interactive setup', false)
.option('--no-input', 'Disable interactive terminal input')
@ -371,7 +361,6 @@ export function registerSetupCommands(program: Command, context: KtxCliCommandCo
agents: options.agents === true,
...(options.target ? { target: options.target } : {}),
agentScope: resolvedAgentScope,
agentInstallMode: options.agentInstallMode,
skipAgents: options.skipAgents === true,
inputMode: options.input === false ? 'disabled' : 'auto',
yes: options.yes === true,

View file

@ -1,20 +1,46 @@
import type { Command } from '@commander-js/extra-typings';
import type { KtxCliCommandContext } from '../cli-program.js';
import { resolveCommandProjectDir } from '../cli-program.js';
import { resolveCommandProjectDir, resolveCommandProjectDirOverride } from '../cli-program.js';
import { findNearestKtxProjectDir } from '../project-resolver.js';
function outputMode(options: { json?: boolean }): 'plain' | 'json' {
return options.json === true ? 'json' : 'plain';
}
function inputMode(options: { input?: boolean }): { inputMode?: 'disabled' } {
return options.input === false ? { inputMode: 'disabled' } : {};
}
export function registerStatusCommands(program: Command, context: KtxCliCommandContext): void {
program
.command('status')
.description('Show current KTX project setup status')
.description('Check current KTX setup and project readiness')
.option('--json', 'Print JSON output', false)
.action(async (options: { json?: boolean }, command) => {
const runner = context.deps.setup ?? (await import('../setup.js')).runKtxSetup;
.option('--no-input', 'Disable interactive terminal input')
.action(async (options: { json?: boolean; input?: boolean }, command) => {
const runner = context.deps.doctor ?? (await import('../doctor.js')).runKtxDoctor;
const explicitOrEnvProjectDir = resolveCommandProjectDirOverride(command);
const nearestProjectDir = explicitOrEnvProjectDir ? undefined : findNearestKtxProjectDir(process.cwd());
if (!explicitOrEnvProjectDir && !nearestProjectDir) {
context.setExitCode(
await runner(
{
command: 'setup',
outputMode: outputMode(options),
...inputMode(options),
},
context.io,
),
);
return;
}
context.setExitCode(
await runner(
{
command: 'status',
command: 'project',
projectDir: resolveCommandProjectDir(command),
json: options.json === true,
outputMode: outputMode(options),
...inputMode(options),
},
context.io,
),