feat(coding-agent): expand ~ in shellPath setting (#6470)

getShellPath() now runs the configured shellPath through the existing
normalizePath() helper, mirroring getSessionDir(). This lets shellPath
point at a home-directory-relative shell wrapper (e.g.
~/.local/bin/agent-shell-sandbox) and resolves consistently across
machines and OSes with different home directories.

refs #6458
This commit is contained in:
Aaron Ky-Riesenbach
2026-07-09 14:23:06 -07:00
committed by GitHub
parent 3664806f2f
commit 1a2542b11b
3 changed files with 33 additions and 3 deletions
+1 -1
View File
@@ -185,7 +185,7 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
| Setting | Type | Default | Description | | Setting | Type | Default | Description |
|---------|------|---------|-------------| |---------|------|---------|-------------|
| `shellPath` | string | - | Custom shell path (e.g., for Cygwin on Windows) | | `shellPath` | string | - | Custom shell path (e.g., for Cygwin on Windows); supports a leading `~` for the home directory |
| `shellCommandPrefix` | string | - | Prefix for every bash command (e.g., `"shopt -s expand_aliases"`) | | `shellCommandPrefix` | string | - | Prefix for every bash command (e.g., `"shopt -s expand_aliases"`) |
| `npmCommand` | string[] | - | Command argv used for npm package lookup/install operations (e.g., `["mise", "exec", "node@20", "--", "npm"]`) | | `npmCommand` | string[] | - | Command argv used for npm package lookup/install operations (e.g., `["mise", "exec", "node@20", "--", "npm"]`) |
@@ -95,7 +95,7 @@ export interface Settings {
hideThinkingBlock?: boolean; hideThinkingBlock?: boolean;
showCacheMissNotices?: boolean; // default: false - show transcript notices for significant prompt-cache misses showCacheMissNotices?: boolean; // default: false - show transcript notices for significant prompt-cache misses
externalEditor?: string; // Command for Ctrl+G external editor; takes precedence over VISUAL/EDITOR externalEditor?: string; // Command for Ctrl+G external editor; takes precedence over VISUAL/EDITOR
shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows) shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows); supports leading ~ expansion
quietStartup?: boolean; quietStartup?: boolean;
defaultProjectTrust?: DefaultProjectTrust; // default: "ask"; global setting only defaultProjectTrust?: DefaultProjectTrust; // default: "ask"; global setting only
shellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., "shopt -s expand_aliases" for alias support) shellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., "shopt -s expand_aliases" for alias support)
@@ -876,7 +876,8 @@ export class SettingsManager {
} }
getShellPath(): string | undefined { getShellPath(): string | undefined {
return this.settings.shellPath; const shellPath = this.settings.shellPath;
return shellPath ? normalizePath(shellPath) : shellPath;
} }
setShellPath(path: string | undefined): void { setShellPath(path: string | undefined): void {
@@ -479,4 +479,33 @@ describe("SettingsManager", () => {
expect(manager.getSessionDir()).toBe(join(homedir(), "sessions")); expect(manager.getSessionDir()).toBe(join(homedir(), "sessions"));
}); });
}); });
describe("getShellPath", () => {
it("should return undefined when not set", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ theme: "dark" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getShellPath()).toBeUndefined();
});
it("should return an absolute shellPath unchanged", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ shellPath: "/bin/zsh" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getShellPath()).toBe("/bin/zsh");
});
it("should expand ~ in shellPath", () => {
writeFileSync(
join(agentDir, "settings.json"),
JSON.stringify({ shellPath: "~/.local/bin/agent-shell-sandbox" }),
);
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getShellPath()).toBe(join(homedir(), ".local/bin/agent-shell-sandbox"));
});
it("should expand a bare ~ in shellPath", () => {
writeFileSync(join(agentDir, "settings.json"), JSON.stringify({ shellPath: "~" }));
const manager = SettingsManager.create(projectDir, agentDir);
expect(manager.getShellPath()).toBe(homedir());
});
});
}); });