feat(chat): Implement /chat delete command (#2401)

This commit is contained in:
Hiroaki Mitsuyoshi
2025-07-28 07:18:12 +09:00
committed by GitHub
parent 9d07de7a5b
commit f3ffb00ed0
4 changed files with 194 additions and 4 deletions

View File

@@ -490,6 +490,68 @@ describe('Logger', () => {
});
});
describe('deleteCheckpoint', () => {
const conversation: Content[] = [
{ role: 'user', parts: [{ text: 'Content to be deleted' }] },
];
const tag = 'delete-me';
let taggedFilePath: string;
beforeEach(async () => {
taggedFilePath = path.join(
TEST_GEMINI_DIR,
`${CHECKPOINT_FILE_NAME.replace('.json', '')}-${tag}.json`,
);
// Create a file to be deleted
await fs.writeFile(taggedFilePath, JSON.stringify(conversation));
});
it('should delete the specified checkpoint file and return true', async () => {
const result = await logger.deleteCheckpoint(tag);
expect(result).toBe(true);
// Verify the file is actually gone
await expect(fs.access(taggedFilePath)).rejects.toThrow(/ENOENT/);
});
it('should return false if the checkpoint file does not exist', async () => {
const result = await logger.deleteCheckpoint('non-existent-tag');
expect(result).toBe(false);
});
it('should re-throw an error if file deletion fails for reasons other than not existing', async () => {
// Simulate a different error (e.g., permission denied)
vi.spyOn(fs, 'unlink').mockRejectedValueOnce(
new Error('EACCES: permission denied'),
);
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
await expect(logger.deleteCheckpoint(tag)).rejects.toThrow(
'EACCES: permission denied',
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
`Failed to delete checkpoint file ${taggedFilePath}:`,
expect.any(Error),
);
});
it('should return false if logger is not initialized', async () => {
const uninitializedLogger = new Logger(testSessionId);
uninitializedLogger.close();
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
const result = await uninitializedLogger.deleteCheckpoint(tag);
expect(result).toBe(false);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Logger not initialized or checkpoint file path not set. Cannot delete checkpoint.',
);
});
});
describe('close', () => {
it('should reset logger state', async () => {
await logger.logMessage(MessageSenderType.USER, 'A message');

View File

@@ -292,6 +292,30 @@ export class Logger {
}
}
async deleteCheckpoint(tag: string): Promise<boolean> {
if (!this.initialized || !this.geminiDir) {
console.error(
'Logger not initialized or checkpoint file path not set. Cannot delete checkpoint.',
);
return false;
}
const path = this._checkpointPath(tag);
try {
await fs.unlink(path);
return true;
} catch (error) {
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code === 'ENOENT') {
// File doesn't exist, which is fine.
return false;
}
console.error(`Failed to delete checkpoint file ${path}:`, error);
throw error;
}
}
close(): void {
this.initialized = false;
this.logFilePath = undefined;