From a3cd16ea451c8abccb917fcbe34a6799cab74e0c Mon Sep 17 00:00:00 2001 From: LeanProductivity Date: Tue, 26 May 2026 12:46:16 +0300 Subject: [PATCH] fix(#74): PowerShell detection + per-OS shell paths - Expand Windows PowerShell detection to include MS Store install path (%LOCALAPPDATA%\Microsoft\WindowsApps\pwsh.exe) in addition to standard installer path (%ProgramFiles%\PowerShell\7\pwsh.exe) - Add -ExecutionPolicy Bypass to PowerShell shell integration script invocation to bypass default Restricted policy without affecting system-wide settings - Add per-OS shell path settings (Windows/macOS/Linux) to allow cross-platform users to configure shell paths separately without reconfiguring when switching devices. Legacy shellPath field kept as fallback for existing users. - Add resolveShellPath() helper to pick platform-specific shell path at spawn time with graceful fallback to legacy shellPath value for backwards compatibility Fixes #74 Fixes cross-platform shell configuration issues reported in community feedback Co-Authored-By: Claude --- src/pty-manager.ts | 9 +++++-- src/settings.ts | 50 ++++++++++++++++++++++++++++++++----- src/shell-integration.ts | 2 +- src/terminal-tab-manager.ts | 3 ++- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/pty-manager.ts b/src/pty-manager.ts index 8453bd2..49826d4 100644 --- a/src/pty-manager.ts +++ b/src/pty-manager.ts @@ -39,10 +39,15 @@ function loadNodePty(pluginDir: string): NodePtyModule { function getDefaultShell(): string { if (Platform.isWin) { - const pwsh = process.env.ProgramFiles + "\\PowerShell\\7\\pwsh.exe"; + const pwshPaths = [ + process.env.ProgramFiles + "\\PowerShell\\7\\pwsh.exe", // standard installer + (process.env.LOCALAPPDATA || "") + "\\Microsoft\\WindowsApps\\pwsh.exe", // MS Store + ]; try { const fs = window.require("fs") as typeof import("fs"); - if (fs.existsSync(pwsh)) return pwsh; + for (const p of pwshPaths) { + if (p && fs.existsSync(p)) return p; + } } catch { // ignore } diff --git a/src/settings.ts b/src/settings.ts index 6e67e08..8e7f74d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,4 +1,4 @@ -import { App, ColorComponent, DropdownComponent, Notice, PluginSettingTab, Setting, setIcon } from "obsidian"; +import { App, ColorComponent, DropdownComponent, Notice, Platform, PluginSettingTab, Setting, setIcon } from "obsidian"; import type TerminalPlugin from "./main"; import type { RecentSession, SavedViewState } from "./session-state"; import { @@ -22,7 +22,10 @@ export type WikiLinkInsertMode = "wikilink" | "vault-path" | "absolute-path"; export type CursorStyle = "block" | "bar" | "underline"; export interface TerminalPluginSettings { - shellPath: string; + shellPath: string; // legacy — kept for migration, not surfaced in UI + shellPathWin: string; + shellPathMac: string; + shellPathLinux: string; startupCommand: string; fontSize: number; fontFamily: string; @@ -58,6 +61,9 @@ export interface TerminalPluginSettings { export const DEFAULT_SETTINGS: TerminalPluginSettings = { shellPath: "", + shellPathWin: "", + shellPathMac: "", + shellPathLinux: "", startupCommand: "", fontSize: 14, fontFamily: "Menlo, Monaco, 'Courier New', monospace", @@ -88,6 +94,12 @@ export const DEFAULT_SETTINGS: TerminalPluginSettings = { wikiLinkInsertMode: "wikilink", }; +export function resolveShellPath(settings: TerminalPluginSettings): string { + if (Platform.isWin) return settings.shellPathWin || settings.shellPath; + if (Platform.isMacOS) return settings.shellPathMac || settings.shellPath; + return settings.shellPathLinux || settings.shellPath; +} + export class TerminalSettingTab extends PluginSettingTab { plugin: TerminalPlugin; private pendingNewColorName = ""; @@ -316,14 +328,40 @@ export class TerminalSettingTab extends PluginSettingTab { new Setting(containerEl).setName("Behavior").setHeading(); new Setting(containerEl) - .setName("Shell path") - .setDesc("Leave empty to auto-detect your default shell") + .setName("Shell path (Windows)") + .setDesc("Leave empty to auto-detect. Separate paths let you switch devices without reconfiguring.") .addText((text) => text .setPlaceholder("Auto-detect") - .setValue(this.plugin.settings.shellPath) + .setValue(this.plugin.settings.shellPathWin) .onChange(async (value) => { - this.plugin.settings.shellPath = value; + this.plugin.settings.shellPathWin = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(containerEl) + .setName("Shell path (macOS)") + .setDesc("Leave empty to auto-detect. Separate paths let you switch devices without reconfiguring.") + .addText((text) => + text + .setPlaceholder("Auto-detect") + .setValue(this.plugin.settings.shellPathMac) + .onChange(async (value) => { + this.plugin.settings.shellPathMac = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(containerEl) + .setName("Shell path (Linux)") + .setDesc("Leave empty to auto-detect. Separate paths let you switch devices without reconfiguring.") + .addText((text) => + text + .setPlaceholder("Auto-detect") + .setValue(this.plugin.settings.shellPathLinux) + .onChange(async (value) => { + this.plugin.settings.shellPathLinux = value; await this.plugin.saveSettings(); }) ); diff --git a/src/shell-integration.ts b/src/shell-integration.ts index be54087..91e885e 100644 --- a/src/shell-integration.ts +++ b/src/shell-integration.ts @@ -104,7 +104,7 @@ export function getShellIntegration( const initPath = ensureScript(scriptDir, "pwsh-init.ps1", PWSH_SCRIPT); return { env: {}, - args: ["-NoLogo", "-NoExit", "-File", initPath], + args: ["-NoLogo", "-NoExit", "-ExecutionPolicy", "Bypass", "-File", initPath], }; } // cmd.exe — no hook support diff --git a/src/terminal-tab-manager.ts b/src/terminal-tab-manager.ts index 363933c..c34c024 100644 --- a/src/terminal-tab-manager.ts +++ b/src/terminal-tab-manager.ts @@ -12,6 +12,7 @@ import { mixHex } from "./color-utils"; import { findTabColor, DEFAULT_TINT_STRENGTH, MAX_TINT_STRENGTH } from "./tab-colors"; import { ThemeRegistry } from "./theme-registry"; import type { TerminalPluginSettings, NotificationSound } from "./settings"; +import { resolveShellPath } from "./settings"; import type { BinaryManager } from "./binary-manager"; import type { SavedTab } from "./session-state"; import { WikiLinkAutocomplete, type AutocompleteEntry } from "./wikilink-autocomplete"; @@ -724,7 +725,7 @@ export class TerminalTabManager { } try { - pty.spawn(this.settings.shellPath, sessionCwd, cols, rows); + pty.spawn(resolveShellPath(this.settings), sessionCwd, cols, rows); } catch (err) { const message = err instanceof Error ? err.message : "unknown error"; console.error("Terminal: failed to spawn shell", err);