mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-07 20:00:37 -07:00
Text can be added after /plan command (#22833)
This commit is contained in:
@@ -137,4 +137,105 @@ describe('parseSlashCommand', () => {
|
||||
expect(result.args).toBe('');
|
||||
expect(result.canonicalPath).toEqual([]);
|
||||
});
|
||||
|
||||
describe('backtracking', () => {
|
||||
const backtrackingCommands: readonly SlashCommand[] = [
|
||||
{
|
||||
name: 'parent',
|
||||
description: 'Parent command',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async () => {},
|
||||
subCommands: [
|
||||
{
|
||||
name: 'notakes',
|
||||
description: 'Subcommand that does not take arguments',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
takesArgs: false,
|
||||
action: async () => {},
|
||||
},
|
||||
{
|
||||
name: 'takes',
|
||||
description: 'Subcommand that takes arguments',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
takesArgs: true,
|
||||
action: async () => {},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
it('should backtrack to parent if subcommand has takesArgs: false and args are provided', () => {
|
||||
const result = parseSlashCommand(
|
||||
'/parent notakes some prompt',
|
||||
backtrackingCommands,
|
||||
);
|
||||
expect(result.commandToExecute?.name).toBe('parent');
|
||||
expect(result.args).toBe('notakes some prompt');
|
||||
expect(result.canonicalPath).toEqual(['parent']);
|
||||
});
|
||||
|
||||
it('should NOT backtrack if subcommand has takesArgs: false but NO args are provided', () => {
|
||||
const result = parseSlashCommand('/parent notakes', backtrackingCommands);
|
||||
expect(result.commandToExecute?.name).toBe('notakes');
|
||||
expect(result.args).toBe('');
|
||||
expect(result.canonicalPath).toEqual(['parent', 'notakes']);
|
||||
});
|
||||
|
||||
it('should NOT backtrack if subcommand has takesArgs: true and args are provided', () => {
|
||||
const result = parseSlashCommand(
|
||||
'/parent takes some args',
|
||||
backtrackingCommands,
|
||||
);
|
||||
expect(result.commandToExecute?.name).toBe('takes');
|
||||
expect(result.args).toBe('some args');
|
||||
expect(result.canonicalPath).toEqual(['parent', 'takes']);
|
||||
});
|
||||
|
||||
it('should NOT backtrack if parent has NO action', () => {
|
||||
const noActionCommands: readonly SlashCommand[] = [
|
||||
{
|
||||
name: 'parent',
|
||||
description: 'Parent without action',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'notakes',
|
||||
description: 'Subcommand without args',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
takesArgs: false,
|
||||
action: async () => {},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const result = parseSlashCommand(
|
||||
'/parent notakes some args',
|
||||
noActionCommands,
|
||||
);
|
||||
// It stays with the subcommand because parent can't handle it
|
||||
expect(result.commandToExecute?.name).toBe('notakes');
|
||||
expect(result.args).toBe('some args');
|
||||
expect(result.canonicalPath).toEqual(['parent', 'notakes']);
|
||||
});
|
||||
|
||||
it('should NOT backtrack if subcommand is NOT marked with takesArgs: false', () => {
|
||||
const result = parseSlashCommand(
|
||||
'/parent takes some args',
|
||||
backtrackingCommands,
|
||||
);
|
||||
expect(result.commandToExecute?.name).toBe('takes');
|
||||
expect(result.args).toBe('some args');
|
||||
expect(result.canonicalPath).toEqual(['parent', 'takes']);
|
||||
});
|
||||
|
||||
it('should backtrack if subcommand has takesArgs: false and args are provided (like /plan copy foo)', () => {
|
||||
const result = parseSlashCommand(
|
||||
'/parent notakes some prompt',
|
||||
backtrackingCommands,
|
||||
);
|
||||
expect(result.commandToExecute?.name).toBe('parent');
|
||||
expect(result.args).toBe('notakes some prompt');
|
||||
expect(result.canonicalPath).toEqual(['parent']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ export const parseSlashCommand = (
|
||||
let commandToExecute: SlashCommand | undefined;
|
||||
let pathIndex = 0;
|
||||
const canonicalPath: string[] = [];
|
||||
let parentCommand: SlashCommand | undefined;
|
||||
|
||||
for (const part of commandPath) {
|
||||
// TODO: For better performance and architectural clarity, this two-pass
|
||||
@@ -52,6 +53,7 @@ export const parseSlashCommand = (
|
||||
}
|
||||
|
||||
if (foundCommand) {
|
||||
parentCommand = commandToExecute;
|
||||
commandToExecute = foundCommand;
|
||||
canonicalPath.push(foundCommand.name);
|
||||
pathIndex++;
|
||||
@@ -67,5 +69,21 @@ export const parseSlashCommand = (
|
||||
|
||||
const args = parts.slice(pathIndex).join(' ');
|
||||
|
||||
// Backtrack if the matched (sub)command doesn't take arguments but some were provided,
|
||||
// AND the parent command is capable of handling them.
|
||||
if (
|
||||
commandToExecute &&
|
||||
commandToExecute.takesArgs === false &&
|
||||
args.length > 0 &&
|
||||
parentCommand &&
|
||||
parentCommand.action
|
||||
) {
|
||||
return {
|
||||
commandToExecute: parentCommand,
|
||||
args: parts.slice(pathIndex - 1).join(' '),
|
||||
canonicalPath: canonicalPath.slice(0, -1),
|
||||
};
|
||||
}
|
||||
|
||||
return { commandToExecute, args, canonicalPath };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user