mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-22 08:38:08 +02:00
Bare invocations now do the obvious thing instead of erroring out, and mode-as-subcommand patterns collapse into flags on the parent. No new top-level commands. - `ktx ingest` (bare) ingests every configured connection. The `text` subcommand is gone; capture inline notes with `ktx ingest --text "..."` and files with `ktx ingest --file path` (use `-` for stdin). `--text`/`--file` reject a positional connection id; pass `--connection-id` to tag captured notes. - `ktx connection` (bare) lists; `ktx connection test` (bare) tests every configured connection. - `ktx wiki` and `ktx sl` flatten `list`/`search`: bare lists, with a `[query...]` positional searches (multi-word joined with spaces). `sl validate` and `sl query` stay as distinct verbs and now read `--connection-id` from the parent. - `ktx mcp` (bare) prints daemon status. Adds a shared `resolveConnectionSelection` helper consumed by ingest and connection test. Updates README, docs-site cli-reference and guides, next-steps strings, agent SKILL templates, and all affected tests. Per-package type-check, unit tests (605), smoke tests, and dead-code checks all pass.
103 lines
4.8 KiB
TypeScript
103 lines
4.8 KiB
TypeScript
import { type Command, Option } from '@commander-js/extra-typings';
|
|
import {
|
|
collectOption,
|
|
type KtxCliCommandContext,
|
|
parsePositiveIntegerOption,
|
|
resolveCommandProjectDir,
|
|
} from '../cli-program.js';
|
|
import type { KtxCliDeps, KtxCliIo } from '../index.js';
|
|
import { runtimeInstallPolicyFromFlags } from '../managed-python-command.js';
|
|
import type { KtxPublicIngestArgs } from '../public-ingest.js';
|
|
import { profileMark } from '../startup-profile.js';
|
|
import type { KtxTextIngestArgs } from '../text-ingest.js';
|
|
import { resolveConnectionSelection } from './connection-selection.js';
|
|
|
|
profileMark('module:commands/ingest-commands');
|
|
|
|
interface IngestCommandOptions {
|
|
runTextIngest: (args: KtxTextIngestArgs, io: KtxCliIo, deps: KtxCliDeps) => Promise<number>;
|
|
}
|
|
|
|
export function registerIngestCommands(
|
|
program: Command,
|
|
context: KtxCliCommandContext,
|
|
commandOptions: IngestCommandOptions,
|
|
): void {
|
|
const ingest = program
|
|
.command('ingest')
|
|
.description('Build or inspect KTX context, or capture text into memory')
|
|
.usage('[options] [connectionId]')
|
|
.argument('[connectionId]', 'Configured connection id to ingest (omit to ingest all)')
|
|
.option('--all', 'Ingest all configured connections', false)
|
|
.addOption(new Option('--fast', 'Use deterministic database schema ingest').conflicts('deep'))
|
|
.addOption(new Option('--deep', 'Use AI-enriched database ingest').conflicts('fast'))
|
|
.addOption(new Option('--query-history', 'Include database query-history usage patterns').conflicts('noQueryHistory'))
|
|
.addOption(new Option('--no-query-history', 'Skip database query-history usage patterns'))
|
|
.option('--query-history-window-days <days>', 'Query-history lookback window for this run', parsePositiveIntegerOption)
|
|
.option('--text <content>', 'Capture inline text into KTX memory; repeatable', collectOption, [])
|
|
.option('--file <path>', 'Capture a text file into KTX memory; use - for stdin; repeatable', collectOption, [])
|
|
.option('--connection-id <connectionId>', 'KTX connection id to tag captured text/file notes')
|
|
.option('--user-id <id>', 'Memory user id for text/file capture attribution', 'local-cli')
|
|
.option('--fail-fast', 'Stop after the first failed text/file item', false)
|
|
.addOption(new Option('--plain', 'Print plain text output').conflicts(['json']))
|
|
.addOption(new Option('--json', 'Print JSON output').conflicts(['plain']))
|
|
.option('--yes', 'Install required managed runtime features without prompting')
|
|
.option('--no-input', 'Disable interactive terminal input')
|
|
.showHelpAfterError();
|
|
|
|
ingest.action(async (connectionId: string | undefined, options, command) => {
|
|
const projectDir = resolveCommandProjectDir(command);
|
|
const hasTextCapture = options.text.length > 0 || options.file.length > 0;
|
|
|
|
if (hasTextCapture) {
|
|
if (connectionId !== undefined) {
|
|
command.error(
|
|
'error: --text/--file does not accept a positional connection id; use --connection-id <id> to tag captured notes',
|
|
);
|
|
}
|
|
if (options.all === true) {
|
|
command.error('error: --all cannot be combined with --text or --file');
|
|
}
|
|
context.setExitCode(
|
|
await commandOptions.runTextIngest(
|
|
{
|
|
projectDir,
|
|
texts: options.text,
|
|
files: options.file,
|
|
...(options.connectionId ? { connectionId: options.connectionId } : {}),
|
|
userId: options.userId,
|
|
json: options.json === true,
|
|
failFast: options.failFast === true,
|
|
},
|
|
context.io,
|
|
context.deps,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
const selection = resolveConnectionSelection({ connectionId, all: options.all === true });
|
|
const { runKtxPublicIngest } = await import('../public-ingest.js');
|
|
const queryHistory =
|
|
options.queryHistory === true ? 'enabled' : options.queryHistory === false ? 'disabled' : 'default';
|
|
const args: KtxPublicIngestArgs = {
|
|
command: 'run',
|
|
projectDir,
|
|
...(selection.kind === 'single' ? { targetConnectionId: selection.connectionId } : {}),
|
|
all: selection.kind === 'all',
|
|
json: options.json === true,
|
|
inputMode: options.input === false ? 'disabled' : 'auto',
|
|
...(options.fast === true ? { depth: 'fast' as const } : {}),
|
|
...(options.deep === true ? { depth: 'deep' as const } : {}),
|
|
queryHistory,
|
|
...(options.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: options.queryHistoryWindowDays } : {}),
|
|
cliVersion: context.packageInfo.version,
|
|
runtimeInstallPolicy: runtimeInstallPolicyFromFlags(options),
|
|
};
|
|
context.setExitCode(await (context.deps.publicIngest ?? runKtxPublicIngest)(args, context.io));
|
|
});
|
|
|
|
ingest.hook('preAction', (_thisCommand, actionCommand) => {
|
|
context.writeDebug?.('ingest', actionCommand);
|
|
});
|
|
}
|