diff --git a/ui-tui/src/__tests__/inlineSlashSkill.test.ts b/ui-tui/src/__tests__/inlineSlashSkill.test.ts index d2f81b7059f04..61578fce682ac 100644 --- a/ui-tui/src/__tests__/inlineSlashSkill.test.ts +++ b/ui-tui/src/__tests__/inlineSlashSkill.test.ts @@ -64,6 +64,28 @@ describe('completionRequestForInput — inline skill references', () => { }) }) + it('completes a second slash in a line that starts with a command', () => { + // Only the first slash is an invocation. Routing the whole line to the + // completer offered nothing, so `/work /cle` went dead while + // `do /work then /cle` completed fine. + expect(completionRequestForInput('/work /cle')).toMatchObject({ + method: 'complete.slash', + params: { text: '/cle' }, + replaceFrom: 7, + skillsOnly: true + }) + }) + + it('leaves a command own arguments to the command', () => { + for (const input of ['/personality alic', '/cron ad', '/details ']) { + expect(completionRequestForInput(input)).toEqual({ + method: 'complete.slash', + params: { text: input }, + replaceFrom: 1 + }) + } + }) + it('routes a real mid-message path to path completion, not skills', () => { expect(completionRequestForInput('open src/foo/ba')).toMatchObject({ method: 'complete.path' }) expect(completionRequestForInput('open /usr/lo')).toMatchObject({ method: 'complete.path' }) diff --git a/ui-tui/src/hooks/useCompletion.ts b/ui-tui/src/hooks/useCompletion.ts index 468c8995b0330..cb9572f261500 100644 --- a/ui-tui/src/hooks/useCompletion.ts +++ b/ui-tui/src/hooks/useCompletion.ts @@ -41,15 +41,13 @@ export function completionRequestForInput( return null } - if (isSlashCommand) { - return { method: 'complete.slash', params: { text: input }, replaceFrom: 1 } - } - - // A `/token` mid-message is a skill reference dropped into prose. It's only - // reachable here because the path branch below would otherwise claim it: a - // bare `/cle` matches TAB_PATH_RE as an absolute path. Skills win that tie — - // the moment a second `/` is typed the inline trigger stops matching and - // path completion takes back over. + // A `/token` mid-message is a skill reference dropped into prose. Detected + // BEFORE the leading-command shape because only the first slash can be an + // invocation — `/help /cle` is a command whose argument names a skill, and + // routing the whole line to the backend's completer offered nothing at all. + // It only matches a whitespace-preceded slash sitting at the caret, so + // ordinary argument completion (`/cron ad`, `/personality alic`) is + // untouched. const inline = inlineSlashTrigger(input) if (inline) { @@ -61,6 +59,10 @@ export function completionRequestForInput( } } + if (isSlashCommand) { + return { method: 'complete.slash', params: { text: input }, replaceFrom: 1 } + } + if (!pathWord) { return null }