feat(coding-agent): Add sessionDir support in settings.json (#2598)

Users can now set sessionDir in global or project settings.json instead
of passing --session-dir on every invocation. CLI flag takes precedence,
then settings.json, then extension hooks.

Closes #2429

Co-authored-by: Sam Collins <81678+smcllns@users.noreply.github.com>
Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
This commit is contained in:
Sam
2026-03-25 16:03:36 -07:00
committed by GitHub
Unverified
parent d38ad0cd6b
commit 2b1fc90cd7
4 changed files with 47 additions and 7 deletions
+12
View File
@@ -127,6 +127,18 @@ When a provider requests a retry delay longer than `maxDelayMs` (e.g., Google's
`npmCommand` is used for all npm package-manager operations, including `npm root -g`, installs, uninstalls, and `npm install` inside git packages. Use argv-style entries exactly as the process should be launched.
### Sessions
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `sessionDir` | string | - | Directory where session files are stored. Accepts absolute or relative paths. |
```json
{ "sessionDir": ".pi/sessions" }
```
When multiple sources specify a session directory, `--session-dir` CLI flag takes precedence, then `sessionDir` in settings.json, then extension hooks.
### Model Cycling
| Setting | Type | Default | Description |
@@ -94,6 +94,7 @@ export interface Settings {
autocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)
showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME
markdown?: MarkdownSettings;
sessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)
}
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
@@ -530,6 +531,10 @@ export class SettingsManager {
this.save();
}
getSessionDir(): string | undefined {
return this.settings.sessionDir;
}
getDefaultProvider(): string | undefined {
return this.settings.defaultProvider;
}
+9 -7
View File
@@ -439,16 +439,15 @@ async function createSessionManager(
parsed: Args,
cwd: string,
extensions: LoadExtensionsResult,
settingsManager: SettingsManager,
): Promise<SessionManager | undefined> {
if (parsed.noSession) {
return SessionManager.inMemory();
}
// CLI flag takes precedence, otherwise ask extensions for custom session directory
let effectiveSessionDir = parsed.sessionDir;
if (!effectiveSessionDir) {
effectiveSessionDir = await callSessionDirectoryHook(extensions, cwd);
}
// Priority: CLI flag > settings.json > extension hook
const effectiveSessionDir =
parsed.sessionDir ?? settingsManager.getSessionDir() ?? (await callSessionDirectoryHook(extensions, cwd));
if (parsed.fork) {
const resolved = await resolveSessionPath(parsed.fork, cwd, effectiveSessionDir);
@@ -789,13 +788,16 @@ export async function main(args: string[]) {
time("resolveModelScope");
// Create session manager based on CLI flags
let sessionManager = await createSessionManager(parsed, cwd, extensionsResult);
let sessionManager = await createSessionManager(parsed, cwd, extensionsResult,
time("createSessionManager");
// Handle --resume: show session picker
if (parsed.resume) {
// Compute effective session dir for resume (same logic as createSessionManager)
const effectiveSessionDir = parsed.sessionDir || (await callSessionDirectoryHook(extensionsResult, cwd));
const effectiveSessionDir =
parsed.sessionDir ??
settingsManager.getSessionDir() ??
(await callSessionDirectoryHook(extensionsResult, cwd));
const selectedPath = await selectSession(
(onProgress) => SessionManager.list(cwd, effectiveSessionDir, onProgress),
@@ -288,4 +288,25 @@ describe("SettingsManager", () => {
expect(savedSettings.theme).toBe("light");
});
});
describe("getSessionDir", () => {
it("should return undefined when not set", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ theme: "dark" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getSessionDir()).toBeUndefined();
});
it("should return global sessionDir", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ sessionDir: "/tmp/sessions" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getSessionDir()).toBe("/tmp/sessions");
});
it("should return project sessionDir, overriding global", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ sessionDir: "/global/sessions" }));
writeFileSync(join(projectDir, ".pi", "settings.json"), JSON.stringify({ sessionDir: "./sessions" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getSessionDir()).toBe("./sessions");
});
});
});