From b44c65443d761bff454dcec2ab29a6cf4c8563e6 Mon Sep 17 00:00:00 2001 From: SenLinLeo <1664761477@qq.com> Date: Tue, 16 Jun 2026 16:25:45 +0800 Subject: [PATCH] fix(cli): treat highlighted install target as selected Signed-off-by: SenLinLeo <1664761477@qq.com> --- cli/src/agents/resolver.ts | 9 ++++- .../unit/agents/resolver-interactive.test.ts | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 cli/test/unit/agents/resolver-interactive.test.ts diff --git a/cli/src/agents/resolver.ts b/cli/src/agents/resolver.ts index 3d983727..2290ee09 100644 --- a/cli/src/agents/resolver.ts +++ b/cli/src/agents/resolver.ts @@ -160,6 +160,7 @@ function dedupeByRoot(candidates: AgentCandidate[]): AgentCandidate[] { async function selectTargetsInteractively(candidates: AgentCandidate[]): Promise { const prompts = await import('prompts') + let highlightedIndex = 0 const { selected } = await prompts.default({ type: 'multiselect', name: 'selected', @@ -167,7 +168,13 @@ async function selectTargetsInteractively(candidates: AgentCandidate[]): Promise choices: candidates.map(c => ({ title: `${c.agent} (${c.rootDir})`, value: c - })) + })), + onRender: function (this: { cursor?: number }) { + highlightedIndex = this.cursor ?? highlightedIndex + }, + format: (selectedTargets: AgentCandidate[]) => ( + selectedTargets.length > 0 ? selectedTargets : [candidates[highlightedIndex] ?? candidates[0]!] + ) }) if (!selected || selected.length === 0) { throw new CliError('installation cancelled', EXIT.usage) diff --git a/cli/test/unit/agents/resolver-interactive.test.ts b/cli/test/unit/agents/resolver-interactive.test.ts new file mode 100644 index 00000000..55bc99ac --- /dev/null +++ b/cli/test/unit/agents/resolver-interactive.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, mock, test } from 'bun:test' +import type { AgentCandidate } from '../../../src/agents/types' + +interface PromptOptions { + onRender?: (this: { cursor?: number }) => void + format?: (selectedTargets: AgentCandidate[]) => AgentCandidate[] +} + +mock.module('prompts', () => ({ + default: (options: PromptOptions) => { + options.onRender?.call({ cursor: 1 }) + return { selected: options.format?.([]) ?? [] } + } +})) + +const { resolveInstallTargets } = await import('../../../src/agents/resolver') + +describe('resolveInstallTargets interactive prompt', () => { + test('uses the highlighted target when Enter submits an empty multiselect', async () => { + const detected: AgentCandidate[] = [ + { agent: 'codex', rootDir: '/repo/.codex/skills', scope: 'project', source: 'detected' }, + { agent: 'claude-code', rootDir: '/repo/.claude/skills', scope: 'project', source: 'detected' } + ] + const highlighted = detected[1]! + + const targets = await resolveInstallTargets({ + cwd: '/repo', + agents: [], + json: false, + interactive: true, + detected + }) + + expect(targets).toEqual([highlighted]) + }) +})