feat: add command history persistence (#152)

This PR adds a command history persistence feature to Codex CLI that:

1. **Stores command history**: Commands are saved to
`~/.codex/history.json` and persist between CLI sessions.
2. **Navigates history**: Users can use the up/down arrow keys to
navigate through command history, similar to a traditional shell.
3. **Filters sensitive data**: Built-in regex patterns prevent commands
containing API keys, passwords, or tokens from being saved.
4. **Configurable**: Added configuration options for history size,
enabling/disabling history, and custom regex patterns for sensitive
content.
5. **New command**: Added `/clearhistory` command to clear command
history.

  ## Code Changes

- Added `src/utils/storage/command-history.ts` with functions for
history management
  - Extended config system to support history settings
  - Updated terminal input components to use persistent history
  - Added help text for the new `/clearhistory` command
  - Added CLAUDE.md file for guidance when working with the codebase

  ## Testing

  - All tests are passing
- Core functionality works with both input components (standard and
multiline)
- History navigation behaves correctly at line boundaries with the
multiline editor
This commit is contained in:
Tomas Cupr
2025-04-17 21:41:54 +02:00
committed by GitHub
parent 5e1d149eb5
commit 295079cf33
6 changed files with 326 additions and 29 deletions

View File

@@ -49,6 +49,11 @@ export type StoredConfig = {
approvalMode?: AutoApprovalMode;
fullAutoErrorMode?: FullAutoErrorMode;
memory?: MemoryConfig;
history?: {
maxSize?: number;
saveHistory?: boolean;
sensitivePatterns?: Array<string>;
};
};
// Minimal config written on first run. An *empty* model string ensures that
@@ -70,6 +75,11 @@ export type AppConfig = {
instructions: string;
fullAutoErrorMode?: FullAutoErrorMode;
memory?: MemoryConfig;
history?: {
maxSize: number;
saveHistory: boolean;
sensitivePatterns: Array<string>;
};
};
// ---------------------------------------------------------------------------
@@ -313,6 +323,21 @@ export const loadConfig = (
config.fullAutoErrorMode = storedConfig.fullAutoErrorMode;
}
// Add default history config if not provided
if (storedConfig.history !== undefined) {
config.history = {
maxSize: storedConfig.history.maxSize ?? 1000,
saveHistory: storedConfig.history.saveHistory ?? true,
sensitivePatterns: storedConfig.history.sensitivePatterns ?? [],
};
} else {
config.history = {
maxSize: 1000,
saveHistory: true,
sensitivePatterns: [],
};
}
return config;
};
@@ -341,14 +366,24 @@ export const saveConfig = (
}
const ext = extname(targetPath).toLowerCase();
// Create the config object to save
const configToSave: StoredConfig = {
model: config.model,
};
// Add history settings if they exist
if (config.history) {
configToSave.history = {
maxSize: config.history.maxSize,
saveHistory: config.history.saveHistory,
sensitivePatterns: config.history.sensitivePatterns,
};
}
if (ext === ".yaml" || ext === ".yml") {
writeFileSync(targetPath, dumpYaml({ model: config.model }), "utf-8");
writeFileSync(targetPath, dumpYaml(configToSave), "utf-8");
} else {
writeFileSync(
targetPath,
JSON.stringify({ model: config.model }, null, 2),
"utf-8",
);
writeFileSync(targetPath, JSON.stringify(configToSave, null, 2), "utf-8");
}
writeFileSync(instructionsPath, config.instructions, "utf-8");