-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(tui): add hierarchical skill group selector with tab navigation #2995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e2d10f1
d8fa805
bbabfeb
818a052
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": minor | ||
| --- | ||
|
|
||
| Add hierarchical group navigation selector for the /skill command. Run /skill to open the interactive selector. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,9 +54,11 @@ import { | |
| type BuiltinSlashCommandName, | ||
| } from './registry'; | ||
| import { handleReloadCommand, handleReloadTuiCommand } from './reload'; | ||
| import type { SkillListSession } from './skills'; | ||
| import { isUserActivatableSkill, type SkillListSession } from './skills'; | ||
| import { runSkillSelector } from './prompts'; | ||
| import { | ||
| canRestoreSubmittedInput, | ||
| resolveSkillCommand, | ||
| resolveSlashCommandInput, | ||
| slashBusyMessage, | ||
| slashCommandBusyReason, | ||
|
|
@@ -424,6 +426,7 @@ const SESSION_REQUIRING_COMMANDS: ReadonlySet<BuiltinSlashCommandName> = new Set | |
| 'goal', | ||
| 'init', | ||
| 'plan', | ||
| 'skill', | ||
| 'swarm', | ||
| 'undo', | ||
| 'web', | ||
|
|
@@ -606,8 +609,89 @@ async function handleBuiltInSlashCommand( | |
| case 'web': | ||
| await handleWebCommand(host); | ||
| return; | ||
| case 'skill': | ||
| await handleSkillCommand(host, args); | ||
| return; | ||
| default: | ||
| host.showError(`Unknown slash command: /${String(name)}`); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| async function handleSkillCommand( | ||
| host: SlashCommandHost, | ||
| args: string, | ||
| ): Promise<void> { | ||
| const busyReason = slashCommandBusyReason({ | ||
| isStreaming: host.state.appState.streamingPhase !== 'idle', | ||
| isCompacting: host.state.appState.isCompacting, | ||
| }); | ||
| if (busyReason !== undefined) { | ||
| host.showError(slashBusyMessage('skill', busyReason)); | ||
| return; | ||
| } | ||
|
|
||
| let session = host.session; | ||
| if (session === undefined) { | ||
| session = await ensureSessionForCommand(host); | ||
| if (session === undefined) return; | ||
| const busyCheck = slashCommandBusyReason({ | ||
| isStreaming: host.state.appState.streamingPhase !== 'idle', | ||
| isCompacting: host.state.appState.isCompacting, | ||
| }); | ||
| if (busyCheck !== undefined) { | ||
| host.showError(slashBusyMessage('skill', busyCheck)); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| let skills: readonly SkillSummary[] = []; | ||
| try { | ||
| skills = await session.listSkills(); | ||
|
Comment on lines
+648
to
+650
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh evidence in this revision is the still-uncovered Useful? React with 👍 / 👎. |
||
| } catch (error) { | ||
| host.showError(formatErrorMessage(error)); | ||
| return; | ||
| } | ||
|
|
||
| const activatableSkills = skills.filter(isUserActivatableSkill); | ||
| const trimmedArgs = args.trim(); | ||
|
|
||
| if ( | ||
| trimmedArgs.length > 0 && | ||
| trimmedArgs !== 'skill' && | ||
| trimmedArgs !== '/skill' && | ||
| trimmedArgs !== 'skills' && | ||
| trimmedArgs !== '/skills' | ||
| ) { | ||
| const spaceIdx = trimmedArgs.search(/\s/); | ||
| const firstWord = spaceIdx >= 0 ? trimmedArgs.slice(0, spaceIdx) : trimmedArgs; | ||
| const remainingArgs = spaceIdx >= 0 ? trimmedArgs.slice(spaceIdx + 1).trim() : ''; | ||
|
|
||
| const resolvedName = | ||
| resolveSkillCommand(host.skillCommandMap, firstWord) ?? | ||
| resolveSkillCommand(host.skillCommandMap, trimmedArgs) ?? | ||
| firstWord; | ||
| const targetSkill = activatableSkills.find( | ||
| (s) => s.name === resolvedName || s.name === firstWord || s.name === trimmedArgs, | ||
| ); | ||
| if (targetSkill !== undefined) { | ||
| const skillArgs = | ||
| targetSkill.name === resolvedName || targetSkill.name === firstWord ? remainingArgs : ''; | ||
| host.sendSkillActivation(session, targetSkill.name, skillArgs); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| const selectedSkill = await runSkillSelector(host, activatableSkills); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a session uses the supported Useful? React with 👍 / 👎. |
||
| if (selectedSkill !== undefined) { | ||
| const busyCheck = slashCommandBusyReason({ | ||
| isStreaming: host.state.appState.streamingPhase !== 'idle', | ||
| isCompacting: host.state.appState.isCompacting, | ||
| }); | ||
| if (busyCheck !== undefined) { | ||
| host.showError(slashBusyMessage('skill', busyCheck)); | ||
| return; | ||
| } | ||
| host.sendSkillActivation(session, selectedSkill.name, ''); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rewrite this as one short user-facing sentence stating only the change; the second usage-instruction sentence violates the repository’s required changeset format and will flow into release notes.
AGENTS.md reference: AGENTS.md:L85-L87
Useful? React with 👍 / 👎.