feat(cli): smart defaults and flatter command surface for ktx (#177)

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.
This commit is contained in:
Andrey Avtomonov 2026-05-20 01:52:37 +02:00 committed by GitHub
parent 14626c294b
commit 2c9a58bb56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 438 additions and 380 deletions

View file

@ -165,10 +165,10 @@ describe('standalone example docs', () => {
for (const command of [
'ktx status --json',
'ktx sl list --json',
'ktx sl search "revenue" --json',
'ktx sl --json',
'ktx sl "revenue" --json',
'ktx sl query',
'ktx wiki search "revenue recognition" --json',
'ktx wiki "revenue recognition" --json',
]) {
assert.match(servingAgents, new RegExp(command.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
}
@ -252,7 +252,7 @@ describe('standalone example docs', () => {
const localWarehouseReadme = await readText('examples/local-warehouse/README.md');
assert.match(ingestReference, /ktx ingest <connectionId>/);
assert.match(ingestReference, /ktx ingest --all --deep/);
assert.match(ingestReference, /Build every configured connection/);
assert.match(ingestReference, /--query-history-window-days <days>/);
assert.match(buildingContext, /ktx ingest <connectionId>/);
assert.match(buildingContext, /ktx ingest --all/);

View file

@ -688,7 +688,6 @@ try {
'exec',
'ktx',
'wiki',
'search',
'revenue',
'--json',
'--limit',
@ -731,7 +730,6 @@ try {
'exec',
'ktx',
'sl',
'search',
'orders',
'--json',
'--connection-id',

View file

@ -475,9 +475,9 @@ describe('verification snippets', () => {
assert.doesNotMatch(source, /startSemanticDaemon/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'setup'/);
assert.match(source, /wiki', 'global', 'revenue\.md'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'wiki',\s*'search'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'wiki',\s*'revenue'/);
assert.match(source, /semantic-layer', 'warehouse', 'orders\.yaml'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'sl',\s*'search',\s*'orders'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'sl',\s*'orders'/);
assert.match(source, /orders\.order_count/);
assert.match(source, /node:sqlite/);
assert.match(source, /driver: sqlite/);