ktx/packages/cli/src/commands/ingest-commands.ts
Andrey Avtomonov 3f0d11e07d
feat(cli)!: remove fast mode; ktx ingest always builds enriched context (KLO-721) (#237)
Fast mode (the ktx ingest --fast/--deep database-ingest depth toggle) is removed.
ktx ingest now always builds the full enriched ("deep") context. There is no
structural fallback: a database connection without a configured model and
embeddings fails the enrichment-readiness preflight before any work runs, with
a 'Run ktx setup to configure a model and embeddings' hint.

- Remove --fast/--deep flags, the per-connection context.depth field, and the
  ktx setup depth prompt (delete setup-database-context-depth.ts).
- Rename ingest-depth.ts -> connection-drivers.ts; ingest always requests scan
  mode 'enriched'; readiness gate (enrichmentReadinessGaps) runs for every
  database target.
- Drop the database-context-depth telemetry step (Node + Python schema mirrors
  regenerated).
- Update CLI, setup, context-build view, docs, the public ktx skill, and the
  release-smoke / artifacts scripts (now assert the no-LLM guard failure).

ktx status --fast (a separate network-probe flag) is unchanged.

Follow-ups: KLO-726 (live progress for ktx ingest --all), KLO-727 (restore
credentialed successful-ingest release smoke coverage).
2026-05-29 17:41:04 +02:00

99 lines
4.5 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('--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',
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);
});
}