feat(cli): enforce required database selection and improve tree-picker UX (#86)

* feat(cli): enforce required database selection and improve tree-picker UX

- Require at least one database driver via prompt `required: true` instead of
  looping on empty selection; remove the now-dead retry/back-on-empty branch.
- Surface the recommended option with a "(recommended)" hint in the depth and
  query-history prompts.
- Tree picker: add `◧` partial glyph for parents whose descendants are checked,
  and make `a` toggle select-all-visible / select-none.

* fix(cli): drop unused export from tree-picker toggleSelectAllVisible

Knip flagged the export as unused; the function is only consumed by the
internal reducer via the 'toggle-select-all-visible' command, so demote
it to a module-local helper to keep CI's dead-code check green.

* test(cli): drop empty-selection warning assertion from setup test

The empty-selection retry/warning loop in `chooseDrivers` was removed in
favor of `multiselect`'s `required: true`, so the legacy warning string
is unreachable. Update the test to assert the simpler back-from-selection
return-to-embeddings flow.
This commit is contained in:
Andrey Avtomonov 2026-05-14 14:35:58 +02:00 committed by GitHub
parent e28b10454a
commit 6c4623f2ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 146 additions and 60 deletions

View file

@ -1094,7 +1094,7 @@ describe('setup status', () => {
expect(embeddings).toHaveBeenCalledTimes(1);
});
it('lets Back from database selection return to embedding setup after an empty selection warning', async () => {
it('lets Back from database selection return to embedding setup', async () => {
const testIo = makeIo();
const modelResults = [
{ status: 'ready' as const, projectDir: tempDir },
@ -1106,9 +1106,8 @@ describe('setup status', () => {
{ status: 'back' as const, projectDir: tempDir },
];
const embeddings = vi.fn(async () => embeddingResults.shift() ?? { status: 'back' as const, projectDir: tempDir });
const databaseMultiselectValues = [[], ['back']];
const databasePrompts = {
multiselect: vi.fn(async () => databaseMultiselectValues.shift() ?? ['back']),
multiselect: vi.fn(async () => ['back']),
select: vi.fn(async () => 'back'),
text: vi.fn(),
password: vi.fn(),
@ -1142,9 +1141,6 @@ describe('setup status', () => {
).resolves.toBe(0);
expect(databasePrompts.select).not.toHaveBeenCalled();
expect(testIo.stdout()).toContain(
'KTX cannot work without at least one database. Select a database or press Escape to go back.',
);
expect(embeddings).toHaveBeenCalledTimes(2);
expect(embeddings).toHaveBeenNthCalledWith(2, expect.objectContaining({ forcePrompt: true }), testIo.io);
expect(testIo.stderr()).not.toContain('No databases selected.');