Repository navigation
feat(cli): support skill activation via /skill-name in non-interactive mode - #29546
Hariharanpugazh wants to merge 2 commits into
Conversation
…e mode Registers SkillCommandLoader in the non-interactive slash command path and handles the 'tool' action result so that typing /skill-name activates a skill outside the interactive UI. Previously the loader was not registered (command was unknown) and the tool result type was unhandled (threw FatalInputError). Also adds a [Skill] suffix tag to skill-backed slash commands in the autocomplete suggestion menu, mirroring the existing [MCP] and [Agent] tags. Adds unit tests for the non-interactive handler and the [Skill] tag rendering. Refs google-gemini#21165
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enables skill activation in non-interactive CLI sessions by registering the necessary command loader and correctly processing tool-based command results. Additionally, it improves the user experience by adding a visual tag to skill commands in the autocomplete menu, ensuring consistency across different command types. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
📊 PR Size: size/M
|
There was a problem hiding this comment.
Code Review
This pull request integrates SkillCommandLoader into the non-interactive CLI command handler and adds support for displaying [Skill] tags in suggestions. It also implements handling for tool action types in non-interactive mode by forwarding the postSubmitPrompt to the model. The review feedback suggests throwing a FatalInputError instead of silently returning undefined when a tool action lacks a postSubmitPrompt in non-interactive mode, and updating the corresponding unit test to assert this behavior.
| case 'tool': | ||
| // Commands such as skill activation (`/skill-name`) resolve to a | ||
| // tool call. Non-interactive mode cannot schedule a UI tool call, | ||
| // so we forward the command's follow-up prompt to the model, which | ||
| // then invokes the tool (e.g. `activate_skill`) itself. If no | ||
| // follow-up prompt is provided there is nothing to send. | ||
| return result.postSubmitPrompt; |
There was a problem hiding this comment.
When a command resolves to a tool action in non-interactive mode, it cannot schedule a UI tool call. Therefore, it must rely on a postSubmitPrompt to forward the request to the model. If postSubmitPrompt is missing or undefined, the command cannot be executed and will silently fail (returning undefined).
Following the general rule to handle undefined cases for optional properties and avoid relying on implementation details of specific loaders, we should explicitly throw a FatalInputError if postSubmitPrompt is not provided, consistent with how other unsupported actions are handled in non-interactive mode.
| case 'tool': | |
| // Commands such as skill activation (`/skill-name`) resolve to a | |
| // tool call. Non-interactive mode cannot schedule a UI tool call, | |
| // so we forward the command's follow-up prompt to the model, which | |
| // then invokes the tool (e.g. `activate_skill`) itself. If no | |
| // follow-up prompt is provided there is nothing to send. | |
| return result.postSubmitPrompt; | |
| case 'tool': | |
| // Commands such as skill activation (`/skill-name`) resolve to a | |
| // tool call. Non-interactive mode cannot schedule a UI tool call, | |
| // so we forward the command's follow-up prompt to the model, which | |
| // then invokes the tool (e.g. `activate_skill`) itself. | |
| if (!result.postSubmitPrompt) { | |
| throw new FatalInputError( | |
| 'Exiting due to a tool action without a follow-up prompt, which is not supported in non-interactive mode.', | |
| ); | |
| } | |
| return result.postSubmitPrompt; |
References
- When consuming an object, if a property is optional in its type definition (interface), callers must handle the undefined case (e.g., by providing a default with ?? or throwing an error). Do not rely on the implementation details of the function that creates the object to always provide a value.
| it('returns undefined for a tool action without a postSubmitPrompt', async () => { | ||
| mockCommands([ | ||
| { | ||
| name: 'bare-tool', | ||
| description: 'bare', | ||
| kind: CommandKind.SKILL, | ||
| action: async () => ({ | ||
| type: 'tool', | ||
| toolName: 'activate_skill', | ||
| toolArgs: { name: 'bare' }, | ||
| }), | ||
| }, | ||
| ]); | ||
|
|
||
| const result = await handleSlashCommand( | ||
| '/bare-tool', | ||
| abortController, | ||
| mockConfig, | ||
| mockSettings, | ||
| ); | ||
| expect(result).toBeUndefined(); | ||
| }); |
There was a problem hiding this comment.
Since we are now throwing a FatalInputError when a tool action lacks a postSubmitPrompt in non-interactive mode, we should update this test to assert that it throws the expected error instead of returning undefined.
it('throws for a tool action without a postSubmitPrompt', async () => {
mockCommands([
{
name: 'bare-tool',
description: 'bare',
kind: CommandKind.SKILL,
action: async () => ({
type: 'tool',
toolName: 'activate_skill',
toolArgs: { name: 'bare' },
}),
},
]);
await expect(
handleSlashCommand(
'/bare-tool',
abortController,
mockConfig,
mockSettings,
),
).rejects.toThrow(FatalInputError);
});…ompt Addresses review feedback on google-gemini#29546: instead of silently returning undefined when a tool action has no postSubmitPrompt in non-interactive mode, throw a FatalInputError so the failure is explicit, consistent with other unsupported non-interactive actions. Updates the corresponding test.
Good call updated to throw FatalInputError when a tool action has no postSubmitPrompt, and adjusted the test to assert it. Pushed in 538458b. |
Registers
SkillCommandLoaderin the non-interactive slash command path and handles thetoolaction result so that typing/skill-nameactivates a skill outside the interactive UI. Previously the loader was not registered (command was unknown) and the tool result type was unhandled (threwFatalInputError).Also adds a
[Skill]suffix tag to skill-backed slash commands in the autocomplete suggestion menu, mirroring the existing[MCP]and[Agent]tags.Adds unit tests for the non-interactive handler and the
[Skill]tag rendering.Refs #21165
Summary
Skills already appear in the
/completion menu and can be activated via/skill-namein the interactive UI (viaSkillCommandLoaderand theactivate_skilltool). This PR closes two remaining gaps:nonInteractiveCliCommands.ts,SkillCommandLoaderwas not registered, so/skill-namewas treated as unknown text. Even if it had been registered, the result handler only understoodsubmit_promptandconfirm_shell_commands— a skill command returnstype: 'tool', which fell through to thedefaultcase and threwFatalInputError.[MCP]and[Agent].Details
packages/cli/src/nonInteractiveCliCommands.tsSkillCommandLoaderalongside the Builtin, MCP, and File loaders.case 'tool': forward the command'spostSubmitPromptto the model (which then invokesactivate_skill). Returnsundefinedwhen no prompt is present, so nothing is sent.packages/cli/src/ui/components/SuggestionsDisplay.tsx[CommandKind.SKILL]: ' [Skill]'toCOMMAND_KIND_SUFFIX.packages/cli/src/nonInteractiveCliCommands.test.ts(new): covers submit_prompt, tool/skill activation, tool-without-prompt, and confirm_shell_commands.packages/cli/src/ui/components/SuggestionsDisplay.test.tsx: new test asserting the[Skill]tag renders, plus snapshot.Related Issues
Refs #21165
How to Validate
npm run test --workspace @google/gemini-cli— the affected suites pass (non-interactive handler, SuggestionsDisplay, SkillCommandLoader).npm run typecheck --workspace @google/gemini-cli— clean.npm run lint --workspace @google/gemini-cli— clean.node packages/cli/dist/index.js -p "/your-skill-name"and confirm the skill activates instead of the command being echoed as plain text.