mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 11:34:44 -07:00
feat(cli): allow safe tools to execute concurrently while agent is busy (#21988)
This commit is contained in:
@@ -162,6 +162,7 @@ import {
|
||||
import { LoginWithGoogleRestartDialog } from './auth/LoginWithGoogleRestartDialog.js';
|
||||
import { NewAgentsChoice } from './components/NewAgentsNotification.js';
|
||||
import { isSlashCommand } from './utils/commandUtils.js';
|
||||
import { parseSlashCommand } from '../utils/commands.js';
|
||||
import { useTerminalTheme } from './hooks/useTerminalTheme.js';
|
||||
import { useTimedMessage } from './hooks/useTimedMessage.js';
|
||||
import { useIsHelpDismissKey } from './utils/shortcutsHelp.js';
|
||||
@@ -1289,6 +1290,18 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
...pendingGeminiHistoryItems,
|
||||
]);
|
||||
|
||||
if (isSlash && isAgentRunning) {
|
||||
const { commandToExecute } = parseSlashCommand(
|
||||
submittedValue,
|
||||
slashCommands ?? [],
|
||||
);
|
||||
if (commandToExecute?.isSafeConcurrent) {
|
||||
void handleSlashCommand(submittedValue);
|
||||
addInput(submittedValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isModelSteeringEnabled() && isAgentRunning && !isSlash) {
|
||||
handleHintSubmit(submittedValue);
|
||||
addInput(submittedValue);
|
||||
@@ -1332,6 +1345,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
addMessage,
|
||||
addInput,
|
||||
submitQuery,
|
||||
handleSlashCommand,
|
||||
slashCommands,
|
||||
isMcpReady,
|
||||
streamingState,
|
||||
messageQueue.length,
|
||||
|
||||
@@ -23,6 +23,7 @@ export const aboutCommand: SlashCommand = {
|
||||
description: 'Show version info',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: async (context) => {
|
||||
const osVersion = process.platform;
|
||||
let sandboxEnv = 'no sandbox';
|
||||
|
||||
@@ -15,6 +15,7 @@ export const settingsCommand: SlashCommand = {
|
||||
description: 'View and edit Gemini CLI settings',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'settings',
|
||||
|
||||
@@ -84,6 +84,7 @@ export const statsCommand: SlashCommand = {
|
||||
description: 'Check session stats. Usage: /stats [session|model|tools]',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: false,
|
||||
isSafeConcurrent: true,
|
||||
action: async (context: CommandContext) => {
|
||||
await defaultSessionView(context);
|
||||
},
|
||||
@@ -93,6 +94,7 @@ export const statsCommand: SlashCommand = {
|
||||
description: 'Show session-specific usage statistics',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: async (context: CommandContext) => {
|
||||
await defaultSessionView(context);
|
||||
},
|
||||
@@ -102,6 +104,7 @@ export const statsCommand: SlashCommand = {
|
||||
description: 'Show model-specific usage statistics',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: (context: CommandContext) => {
|
||||
const { selectedAuthType, userEmail, tier } = getUserIdentity(context);
|
||||
const currentModel = context.services.config?.getModel();
|
||||
@@ -125,6 +128,7 @@ export const statsCommand: SlashCommand = {
|
||||
description: 'Show tool-specific usage statistics',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem({
|
||||
type: MessageType.TOOL_STATS,
|
||||
|
||||
@@ -207,6 +207,11 @@ export interface SlashCommand {
|
||||
*/
|
||||
autoExecute?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this command can be safely executed while the agent is busy (e.g. streaming a response).
|
||||
*/
|
||||
isSafeConcurrent?: boolean;
|
||||
|
||||
// Optional metadata for extension commands
|
||||
extensionName?: string;
|
||||
extensionId?: string;
|
||||
|
||||
@@ -11,6 +11,7 @@ export const vimCommand: SlashCommand = {
|
||||
description: 'Toggle vim mode on/off',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
isSafeConcurrent: true,
|
||||
action: async (context, _args) => {
|
||||
const newVimState = await context.ui.toggleVimEnabled();
|
||||
|
||||
|
||||
@@ -94,6 +94,12 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
const mockSlashCommands: SlashCommand[] = [
|
||||
{
|
||||
name: 'stats',
|
||||
description: 'Check stats',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
isSafeConcurrent: true,
|
||||
},
|
||||
{
|
||||
name: 'clear',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
@@ -3876,6 +3882,13 @@ describe('InputPrompt', () => {
|
||||
shouldSubmit: false,
|
||||
errorMessage: 'Slash commands cannot be queued',
|
||||
},
|
||||
{
|
||||
name: 'should allow concurrent-safe slash commands',
|
||||
bufferText: '/stats',
|
||||
shellMode: false,
|
||||
shouldSubmit: true,
|
||||
errorMessage: null,
|
||||
},
|
||||
{
|
||||
name: 'should prevent shell commands',
|
||||
bufferText: 'ls',
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
isAutoExecutableCommand,
|
||||
isSlashCommand,
|
||||
} from '../utils/commandUtils.js';
|
||||
import { parseSlashCommand } from '../../utils/commands.js';
|
||||
import * as path from 'node:path';
|
||||
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
|
||||
import { getSafeLowColorBackground } from '../themes/color-utils.js';
|
||||
@@ -408,6 +409,17 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
(isSlash || isShell) &&
|
||||
streamingState === StreamingState.Responding
|
||||
) {
|
||||
if (isSlash) {
|
||||
const { commandToExecute } = parseSlashCommand(
|
||||
trimmedMessage,
|
||||
slashCommands,
|
||||
);
|
||||
if (commandToExecute?.isSafeConcurrent) {
|
||||
inputHistory.handleSubmit(trimmedMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setQueueErrorMessage(
|
||||
`${isShell ? 'Shell' : 'Slash'} commands cannot be queued`,
|
||||
);
|
||||
@@ -415,7 +427,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
}
|
||||
inputHistory.handleSubmit(trimmedMessage);
|
||||
},
|
||||
[inputHistory, shellModeActive, streamingState, setQueueErrorMessage],
|
||||
[
|
||||
inputHistory,
|
||||
shellModeActive,
|
||||
streamingState,
|
||||
setQueueErrorMessage,
|
||||
slashCommands,
|
||||
],
|
||||
);
|
||||
|
||||
// Effect to reset completion if history navigation just occurred and set the text
|
||||
|
||||
Reference in New Issue
Block a user