Skip to content

Commit b569a98

Browse files
sripasgSandyTao520
authored andcommitted
feat(acp): add /help command (#24839)
1 parent ea7dfe8 commit b569a98

File tree

4 files changed

+108
-0
lines changed

4 files changed

+108
-0
lines changed

packages/cli/src/acp/commandHandler.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@ describe('CommandHandler', () => {
2929

3030
const about = parse('/about');
3131
expect(about.commandToExecute?.name).toBe('about');
32+
33+
const help = parse('/help');
34+
expect(help.commandToExecute?.name).toBe('help');
3235
});
3336
});

packages/cli/src/acp/commandHandler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ExtensionsCommand } from './commands/extensions.js';
1111
import { InitCommand } from './commands/init.js';
1212
import { RestoreCommand } from './commands/restore.js';
1313
import { AboutCommand } from './commands/about.js';
14+
import { HelpCommand } from './commands/help.js';
1415

1516
export class CommandHandler {
1617
private registry: CommandRegistry;
@@ -26,6 +27,7 @@ export class CommandHandler {
2627
registry.register(new InitCommand());
2728
registry.register(new RestoreCommand());
2829
registry.register(new AboutCommand());
30+
registry.register(new HelpCommand(registry));
2931
return registry;
3032
}
3133

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { describe, it, expect } from 'vitest';
8+
import { HelpCommand } from './help.js';
9+
import { CommandRegistry } from './commandRegistry.js';
10+
import type { Command, CommandContext } from './types.js';
11+
12+
describe('HelpCommand', () => {
13+
it('returns formatted help text with sorted commands', async () => {
14+
const registry = new CommandRegistry();
15+
16+
const cmdB: Command = {
17+
name: 'bravo',
18+
description: 'Bravo command',
19+
execute: async () => ({ name: 'bravo', data: '' }),
20+
};
21+
22+
const cmdA: Command = {
23+
name: 'alpha',
24+
description: 'Alpha command',
25+
execute: async () => ({ name: 'alpha', data: '' }),
26+
};
27+
28+
registry.register(cmdB);
29+
registry.register(cmdA);
30+
31+
const helpCommand = new HelpCommand(registry);
32+
33+
const context = {} as CommandContext;
34+
35+
const response = await helpCommand.execute(context, []);
36+
37+
expect(response.name).toBe('help');
38+
39+
const data = response.data as string;
40+
41+
expect(data).toContain('Gemini CLI Help:');
42+
expect(data).toContain('### Basics');
43+
expect(data).toContain('### Commands');
44+
45+
const lines = data.split('\n');
46+
const alphaIndex = lines.findIndex((l) => l.includes('/alpha'));
47+
const bravoIndex = lines.findIndex((l) => l.includes('/bravo'));
48+
49+
expect(alphaIndex).toBeLessThan(bravoIndex);
50+
expect(alphaIndex).not.toBe(-1);
51+
expect(bravoIndex).not.toBe(-1);
52+
});
53+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import type {
8+
Command,
9+
CommandContext,
10+
CommandExecutionResponse,
11+
} from './types.js';
12+
import type { CommandRegistry } from './commandRegistry.js';
13+
14+
export class HelpCommand implements Command {
15+
readonly name = 'help';
16+
readonly description = 'Show available commands';
17+
18+
constructor(private registry: CommandRegistry) {}
19+
20+
async execute(
21+
_context: CommandContext,
22+
_args: string[] = [],
23+
): Promise<CommandExecutionResponse> {
24+
const commands = this.registry
25+
.getAllCommands()
26+
.sort((a, b) => a.name.localeCompare(b.name));
27+
28+
const lines: string[] = [];
29+
30+
lines.push('Gemini CLI Help:');
31+
lines.push('');
32+
lines.push('### Basics');
33+
lines.push(
34+
'- **Add context**: Use `@` to specify files for context (e.g., `@src/myFile.ts`) to target specific files or folders.',
35+
);
36+
lines.push('');
37+
38+
lines.push('### Commands');
39+
for (const cmd of commands) {
40+
if (cmd.description) {
41+
lines.push(`- **/${cmd.name}** - ${cmd.description}`);
42+
}
43+
}
44+
45+
return {
46+
name: this.name,
47+
data: lines.join('\n'),
48+
};
49+
}
50+
}

0 commit comments

Comments
 (0)