mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 22:02:59 -07:00
feat(cli): add /note slash command to append or view notes
This commit is contained in:
@@ -42,6 +42,7 @@ import { initCommand } from '../ui/commands/initCommand.js';
|
||||
import { mcpCommand } from '../ui/commands/mcpCommand.js';
|
||||
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
||||
import { modelCommand } from '../ui/commands/modelCommand.js';
|
||||
import { noteCommand } from '../ui/commands/noteCommand.js';
|
||||
import { oncallCommand } from '../ui/commands/oncallCommand.js';
|
||||
import { permissionsCommand } from '../ui/commands/permissionsCommand.js';
|
||||
import { planCommand } from '../ui/commands/planCommand.js';
|
||||
@@ -184,6 +185,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
||||
: [mcpCommand]),
|
||||
memoryCommand,
|
||||
modelCommand,
|
||||
noteCommand,
|
||||
...(this.config?.getFolderTrust() ? [permissionsCommand] : []),
|
||||
...(this.config?.isPlanEnabled() ? [planCommand] : []),
|
||||
policiesCommand,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { noteCommand } from './noteCommand.js';
|
||||
import { type CommandContext } from './types.js';
|
||||
|
||||
vi.mock('node:fs/promises');
|
||||
|
||||
describe('noteCommand', () => {
|
||||
const mockContext = {} as CommandContext;
|
||||
const notesPath = path.join(process.cwd(), 'notes.md');
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return notes content when no args provided and file exists', async () => {
|
||||
vi.mocked(fs.readFile).mockResolvedValue('existing note\n');
|
||||
|
||||
const result = await noteCommand.action!(mockContext, '');
|
||||
|
||||
expect(fs.readFile).toHaveBeenCalledWith(notesPath, 'utf8');
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: expect.stringContaining('existing note'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return info message when no args provided and file does not exist', async () => {
|
||||
vi.mocked(fs.readFile).mockRejectedValue(new Error('File not found'));
|
||||
|
||||
const result = await noteCommand.action!(mockContext, ' ');
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No notes found. Use "/note <text>" to add one.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should append note to file when args are provided', async () => {
|
||||
const note = 'this is a new note';
|
||||
vi.mocked(fs.appendFile).mockResolvedValue(undefined);
|
||||
|
||||
const result = await noteCommand.action!(mockContext, note);
|
||||
|
||||
expect(fs.appendFile).toHaveBeenCalledWith(notesPath, `${note}\n`);
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: expect.stringContaining('Note added'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error message when append fails', async () => {
|
||||
vi.mocked(fs.appendFile).mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
const result = await noteCommand.action!(mockContext, 'some note');
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: expect.stringContaining(
|
||||
'Failed to save note: Permission denied',
|
||||
),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { CommandKind, type SlashCommand } from './types.js';
|
||||
|
||||
export const noteCommand: SlashCommand = {
|
||||
name: 'note',
|
||||
description: 'Append a note to notes.md or view current notes',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: async (_context, args) => {
|
||||
const notesPath = path.join(process.cwd(), 'notes.md');
|
||||
|
||||
if (!args || args.trim().length === 0) {
|
||||
try {
|
||||
const content = await fs.readFile(notesPath, 'utf8');
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Current notes in ${notesPath}:\n\n${content}`,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No notes found. Use "/note <text>" to add one.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.appendFile(notesPath, `${args}\n`);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Note added to ${notesPath}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Failed to save note: ${error instanceof Error ? error.message : String(error)}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user