diff --git a/src/main/sharedSettingsFile.test.ts b/src/main/sharedSettingsFile.test.ts index a84912a24..6cf39fb57 100644 --- a/src/main/sharedSettingsFile.test.ts +++ b/src/main/sharedSettingsFile.test.ts @@ -84,6 +84,9 @@ describe("sharedSettingsFile", () => { locale: "system", gitTextLanguage: "en", terminalPosition: "right", + windowsShellPath: "auto", + windowsInternalShellPath: "auto", + windowsShellArguments: "", commitGenProvider: "auto", commitGenModel: "", commitGenEffort: "", @@ -215,6 +218,9 @@ describe("sharedSettingsFile", () => { locale: "system", gitTextLanguage: "en", terminalPosition: "right", + windowsShellPath: "auto", + windowsInternalShellPath: "auto", + windowsShellArguments: "", commitGenProvider: "auto", commitGenModel: "", commitGenEffort: "", diff --git a/src/renderer/actions/agentLoginActions.test.ts b/src/renderer/actions/agentLoginActions.test.ts index 9e2199343..f0055aae9 100644 --- a/src/renderer/actions/agentLoginActions.test.ts +++ b/src/renderer/actions/agentLoginActions.test.ts @@ -3,7 +3,7 @@ import type { Project } from "@/shared/contracts"; import type { SupervisorEvent } from "@/shared/ipc"; const bridge = vi.hoisted(() => ({ - startShell: vi.fn<() => Promise>(), + startShell: vi.fn<(payload: unknown) => Promise>(), closeThread: vi.fn<() => Promise>(), onSupervisorEvent: vi.fn<(handler: (event: SupervisorEvent) => void) => () => void>(), openExternal: vi.fn<(url: string) => Promise>(), @@ -18,6 +18,9 @@ const loginTerminalStore = vi.hoisted(() => ({ active: undefined as { onForceClose?: () => void; shellId: string } | undefined, })); const writeScriptToShellMock = vi.hoisted(() => vi.fn<(shellId: string, script: string) => void>()); +const startShellWithCurrentSettingsMock = vi.hoisted(() => + vi.fn<(payload: unknown) => Promise>(), +); vi.mock("@heroui/react", () => ({ toast: { @@ -62,6 +65,8 @@ vi.mock("@/renderer/state/sharedSettingsStore", () => ({ })); vi.mock("@/renderer/utils/shellUtils", () => ({ + disposeRoutedShellSession: vi.fn<(shellId: string) => void>(), + startShellWithCurrentSettings: startShellWithCurrentSettingsMock, writeScriptToShell: writeScriptToShellMock, })); @@ -133,6 +138,9 @@ describe("runAgentLoginCommand", () => { loginTerminalStore.markFailed.mockReset(); loginTerminalStore.active = undefined; writeScriptToShellMock.mockReset(); + startShellWithCurrentSettingsMock + .mockReset() + .mockImplementation((payload) => bridge.startShell(payload)); }); it("opens hard-wrapped WSL auth URLs in the native browser", () => { @@ -213,6 +221,13 @@ describe("runAgentLoginCommand", () => { "Clear-Host; $env:CLAUDE_CONFIG_DIR = 'C:\\Users\\sdsle\\.poracode\\claude-profiles\\home'; claude auth login", ); expect(script).not.toContain("CLAUDE_CONFIG_DIR=C:"); + expect(startShellWithCurrentSettingsMock).toHaveBeenCalledWith( + expect.objectContaining({ + projectLocation: windowsProject.location, + startInHome: true, + windowsShellRuntime: "powershell", + }), + ); }); it("sets profile env via an inline POSIX prefix on WSL", () => { @@ -482,5 +497,14 @@ describe("runAgentLoginCommand", () => { shellId: expect.stringMatching(/^update:/u), }), ); + expect(startShellWithCurrentSettingsMock).toHaveBeenCalledWith( + expect.objectContaining({ + projectLocation: posixProject.location, + startInHome: true, + }), + ); + expect(startShellWithCurrentSettingsMock.mock.calls[0]?.[0]).not.toHaveProperty( + "windowsShellRuntime", + ); }); }); diff --git a/src/renderer/actions/agentLoginActions.ts b/src/renderer/actions/agentLoginActions.ts index 6257efef4..63fed98c5 100644 --- a/src/renderer/actions/agentLoginActions.ts +++ b/src/renderer/actions/agentLoginActions.ts @@ -8,7 +8,11 @@ import { useAppStore } from "@/renderer/state/appStore"; import { useDevTerminalStore } from "@/renderer/state/devTerminalStore"; import { useLoginTerminalStore } from "@/renderer/state/loginTerminalStore"; import { watchRoutedTerminal } from "@/renderer/state/remoteTerminalFeed"; -import { disposeRoutedShellSession, writeScriptToShell } from "@/renderer/utils/shellUtils"; +import { + disposeRoutedShellSession, + startShellWithCurrentSettings, + writeScriptToShell, +} from "@/renderer/utils/shellUtils"; function resolveLoginProject(): Project | undefined { const app = useAppStore.getState(); @@ -129,20 +133,23 @@ export function runAgentLoginCommand(input: { }, }); - void readBridge() + void startShellWithCurrentSettings({ // Auth is global (writes to ~/.), so run login in the user's home // directory rather than the (possibly ephemeral) project worktree. - .startShell({ shellId, projectLocation: project.location, startInHome: true }) - .catch((error) => { - // The shell never started, so the completion watcher would otherwise leak - // (and leave callers' pending UI stuck). Tear it down and report failure. - stopWatching(); - fireOnce(-1); - toast.danger( - error instanceof Error ? error.message : i18n._(msg`Unable to open ${input.label} login.`), - ); - useLoginTerminalStore.getState().close(); - }); + shellId, + projectLocation: project.location, + startInHome: true, + ...(project.location.kind === "windows" ? { windowsShellRuntime: "powershell" as const } : {}), + }).catch((error) => { + // The shell never started, so the completion watcher would otherwise leak + // (and leave callers' pending UI stuck). Tear it down and report failure. + stopWatching(); + fireOnce(-1); + toast.danger( + error instanceof Error ? error.message : i18n._(msg`Unable to open ${input.label} login.`), + ); + useLoginTerminalStore.getState().close(); + }); writeScriptToShell(shellId, script, project.remoteServerId); return true; } @@ -225,18 +232,21 @@ export function runAgentInstallCommand(input: { }, }); - void readBridge() + void startShellWithCurrentSettings({ // Installers shouldn't run inside the (possibly ephemeral) project // worktree — launch the shell in the user's home directory instead. - .startShell({ shellId, projectLocation: project.location, startInHome: true }) - .catch((error) => { - stopWatching(); - fireOnce(-1); - toast.danger( - error instanceof Error ? error.message : i18n._(msg`Unable to install ${input.label}.`), - ); - useLoginTerminalStore.getState().close(); - }); + shellId, + projectLocation: project.location, + startInHome: true, + ...(project.location.kind === "windows" ? { windowsShellRuntime: "powershell" as const } : {}), + }).catch((error) => { + stopWatching(); + fireOnce(-1); + toast.danger( + error instanceof Error ? error.message : i18n._(msg`Unable to install ${input.label}.`), + ); + useLoginTerminalStore.getState().close(); + }); writeScriptToShell(shellId, script, project.remoteServerId); return true; } diff --git a/src/renderer/actions/terminalActions.test.ts b/src/renderer/actions/terminalActions.test.ts index f6b3f6f9c..f96111fd4 100644 --- a/src/renderer/actions/terminalActions.test.ts +++ b/src/renderer/actions/terminalActions.test.ts @@ -54,7 +54,7 @@ describe("runProjectAction", () => { }); }); - it("restarts an action in its existing tracked terminal", () => { + it("restarts an action in its existing tracked terminal", async () => { runProjectAction(project.id, "dev"); const firstTab = useDevTerminalStore.getState().tabs[0]!; @@ -65,7 +65,7 @@ describe("runProjectAction", () => { id: firstTab.id, runActionId: "dev", }); - expect(bridge.startShell).toHaveBeenCalledTimes(2); + await vi.waitFor(() => expect(bridge.startShell).toHaveBeenCalledTimes(2)); expect(bridge.startShell).toHaveBeenNthCalledWith( 2, expect.objectContaining({ shellId: firstTab.id }), @@ -177,6 +177,7 @@ describe("runProjectAction", () => { runProjectAction(project.id, "dev"); const tab = useDevTerminalStore.getState().tabs[0]!; runProjectAction(project.id, "dev"); + await vi.waitFor(() => expect(bridge.startShell).toHaveBeenCalledTimes(2)); resolveSecond(); rejectFirst(new Error("old start failed")); diff --git a/src/renderer/components/common/Select.test.tsx b/src/renderer/components/common/Select.test.tsx index 62aad40e4..48670e693 100644 --- a/src/renderer/components/common/Select.test.tsx +++ b/src/renderer/components/common/Select.test.tsx @@ -35,12 +35,12 @@ describe("Select rich options", () => { responsiveMenuState.mobile = false; }); - it("renders icon and detail in the desktop trigger and selects a rich option", async () => { + it("renders icon in the desktop trigger and selects a rich option with detail", async () => { const onChange = vi.fn<(value: string) => void>(); render(); const trigger = screen.getByRole("button", { name: "Project" }); - expect(trigger).toHaveTextContent("AlphaC:\\Alpha"); + expect(trigger).toHaveTextContent("Alpha"); fireEvent.click(trigger); const beta = await screen.findByRole("button", { name: /Beta/u }); diff --git a/src/renderer/components/common/Select.tsx b/src/renderer/components/common/Select.tsx index 525686f9c..c5aaa165b 100644 --- a/src/renderer/components/common/Select.tsx +++ b/src/renderer/components/common/Select.tsx @@ -72,11 +72,6 @@ export function Select(props: SelectProps) { {selectedOption?.label ?? placeholder} - {selectedOption?.detail ? ( - - {selectedOption.detail} - - ) : null} } @@ -96,12 +91,14 @@ export function Select(props: SelectProps) { }} > {option.icon} - {option.label} - {option.detail ? ( - - {option.detail} - - ) : null} +
+ {option.label} + {option.detail ? ( + + {option.detail} + + ) : null} +
{selected ? : null} ); @@ -118,17 +115,26 @@ export function Select(props: SelectProps) { // rule but later in import order, collapsing the end padding that // reserves room for the checkmark so it overlaps long labels. The // `pe-7` utility (utilities layer) restores it. - + {option.icon || option.detail ? ( - <> +
{option.icon} -
- +
+ {option.detail ? ( - {option.detail} + + {option.detail} + ) : null}
- +
) : ( option.label )} @@ -147,21 +153,15 @@ export function Select(props: SelectProps) { {label ? : null} - {({ defaultChildren, isPlaceholder }) => - !isPlaceholder && selectedOption?.icon ? ( + {({ defaultChildren, isPlaceholder }) => { + if (isPlaceholder || !selectedOption) return defaultChildren; + return ( {selectedOption.icon} {selectedOption.label} - {selectedOption.detail ? ( - - {selectedOption.detail} - - ) : null} - ) : ( - defaultChildren - ) - } + ); + }} diff --git a/src/renderer/locales/de/messages.po b/src/renderer/locales/de/messages.po index 36d9b99bf..7c39a0897 100644 --- a/src/renderer/locales/de/messages.po +++ b/src/renderer/locales/de/messages.po @@ -1028,6 +1028,16 @@ msgstr "Aufgabe hinzufügen" msgid "Add workspace" msgstr "Arbeitsbereich hinzufügen" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Zusätzliche Argumente, die an jede neue Shell des Terminal-Panels übergeben werden. Werte mit Leerzeichen müssen in Anführungszeichen gesetzt werden." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Zusätzliche Argumente, die an jede neue Windows-Shell übergeben werden. Setzen Sie Werte mit Leerzeichen in Anführungszeichen." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Eine benutzerdefinierte Basis-URL bekanntgeben, z. B. für einen Reverse-Proxy oder Tunnel. Für automatisch leer lassen." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Automatisch" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Automatisch ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Automatisch genehmigen" @@ -1582,6 +1597,15 @@ msgstr "Auto-Auswahl" msgid "Auto-show terminal panel" msgstr "Terminal-Panel automatisch anzeigen" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automatisch" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automatisch ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Übergeordneten Ordner auswählen" msgid "Choose at least two candidates and enter a prompt." msgstr "Wählen Sie mindestens zwei Kandidaten aus und geben Sie einen Prompt ein." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Automatisch auswählen oder für neue Terminals eine erkannte Shell festlegen." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Wählen Sie aus, wie Sie sich anmelden möchten." msgid "Choose import destination" msgstr "Importziel auswählen" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Shells für das Terminal-Panel und die internen Befehle von Poracode auswählen." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "SSH-Identitätsdatei auswählen" @@ -2713,6 +2746,10 @@ msgstr "Befehl ist erforderlich." msgid "Command not found." msgstr "Befehl nicht gefunden." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Eingabeaufforderung" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Standardmäßige PR-Automatisierung" msgid "Default thread removal" msgstr "Standardmäßiges Entfernen von Threads" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Standardmäßige Windows-Shell" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Fokussierte Arbeit an andere installierte Agenten delegieren und die Ergebnisse koordinieren." @@ -4201,6 +4243,10 @@ msgstr "Lässt eingehende Hook-Umschläge auf dem Supervisor fallen, sodass Agen msgid "Duplicate server name in scan" msgstr "Doppelter Servername im Scan" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "z. B. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "z. B. 512k oder 1m" @@ -5891,6 +5937,11 @@ msgstr "Anweisungen" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Anweisungen, denen Agenten folgen, wenn sie auswählen, an welchen Agenten oder welches Modell delegiert wird." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Interne Befehle und Agenten" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Portweiterleitung ist nicht aktiviert" msgid "Ports" msgstr "Ports" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (empfohlen)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (empfohlen)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PR-Aktionen" @@ -9987,6 +10054,10 @@ msgstr "Tastenkürzel speichern" msgid "Saved credentials are shared across all environments." msgstr "Gespeicherte Anmeldeinformationen werden in allen Umgebungen gemeinsam genutzt." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Gespeicherte Shell" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Kopplungs-QR-Code scannen" @@ -10424,6 +10495,11 @@ msgstr "Teilen Sie Ihre Aktivität" msgid "Shared" msgstr "Geteilt" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Shell-Argumente" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Quelle des Skill-Marketplace" msgid "Skill runs" msgstr "Skill-Ausführungen" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Fähigkeit: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Terminal nicht bereit – Auswahl zum Composer hinzugefügt." msgid "Terminal panel font size" msgstr "Schriftgröße des Terminalpanels" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Shell des Terminal-Panels" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Terminalposition" msgid "Terminal scroll speed" msgstr "Scrollgeschwindigkeit des Terminals" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Argumente der Terminal-Shell" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Verbraucht" msgid "Used by {conflict}" msgstr "Von {conflict} verwendet" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Wird für Agenten, Authentifizierung, Installationen und interne Befehle von Poracode verwendet." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Wird für neue interaktive Sitzungen im Terminal-Panel verwendet." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Wird verwendet, wenn kein Anbieter angegeben ist. Explizite Crossagents-Nutzung steht an erster Stelle, gefolgt von Favoriten, normaler Agent-Nutzung und integrierter Reihenfolge." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows wird noch nicht unterstützt. Installieren Sie innerhalb der WSL oder unter macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows-Shells" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Gewinner zusammengeführt." diff --git a/src/renderer/locales/en/messages.po b/src/renderer/locales/en/messages.po index b0dbac57b..d66b7fd0a 100644 --- a/src/renderer/locales/en/messages.po +++ b/src/renderer/locales/en/messages.po @@ -1028,6 +1028,16 @@ msgstr "Add to-do" msgid "Add workspace" msgstr "Add workspace" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Additional arguments passed to each new Windows shell. Quote values containing spaces." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Auto" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Auto ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Auto Approve" @@ -1582,6 +1597,15 @@ msgstr "Auto-selection" msgid "Auto-show terminal panel" msgstr "Auto-show terminal panel" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automatic" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automatic ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Choose a parent folder" msgid "Choose at least two candidates and enter a prompt." msgstr "Choose at least two candidates and enter a prompt." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Choose automatically or override with a detected shell for new terminals." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Choose how to sign in." msgid "Choose import destination" msgstr "Choose import destination" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Choose shells for the Terminal panel and Poracode's internal commands." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Choose SSH identity file" @@ -2713,6 +2746,10 @@ msgstr "Command is required." msgid "Command not found." msgstr "Command not found." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Command Prompt" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Default PR automation" msgid "Default thread removal" msgstr "Default thread removal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Default Windows shell" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Delegate focused work to other installed agents and coordinate the results." @@ -4201,6 +4243,10 @@ msgstr "Drops incoming hook envelopes on the supervisor so agents fall back to L msgid "Duplicate server name in scan" msgstr "Duplicate server name in scan" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "e.g. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "e.g. 512k or 1m" @@ -5891,6 +5937,11 @@ msgstr "Instructions" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Instructions agents follow when choosing which agent or model to delegate to." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Internal commands and agents" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Port forwarding isn't enabled" msgid "Ports" msgstr "Ports" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (recommended)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (recommended)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PR actions" @@ -9987,6 +10054,10 @@ msgstr "Save shortcut" msgid "Saved credentials are shared across all environments." msgstr "Saved credentials are shared across all environments." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Saved shell" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Scan pairing QR code" @@ -10424,6 +10495,11 @@ msgstr "Share your activity" msgid "Shared" msgstr "Shared" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Shell arguments" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Skill marketplace source" msgid "Skill runs" msgstr "Skill runs" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Skill: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Terminal not ready — added selection to composer." msgid "Terminal panel font size" msgstr "Terminal panel font size" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Terminal panel shell" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Terminal position" msgid "Terminal scroll speed" msgstr "Terminal scroll speed" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Terminal shell arguments" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Used" msgid "Used by {conflict}" msgstr "Used by {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Used for agents, authentication, installs, and Poracode's internal commands." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Used for new interactive Terminal-panel sessions." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows is not supported yet. Install inside WSL or on macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows shells" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Winner merged." diff --git a/src/renderer/locales/es/messages.po b/src/renderer/locales/es/messages.po index 13710237b..0a29ac607 100644 --- a/src/renderer/locales/es/messages.po +++ b/src/renderer/locales/es/messages.po @@ -1028,6 +1028,16 @@ msgstr "Añadir tarea" msgid "Add workspace" msgstr "Añadir espacio de trabajo" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Argumentos adicionales que se pasan a cada nuevo shell del panel de terminal. Escribe entre comillas los valores que contengan espacios." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Argumentos adicionales que se pasan a cada nuevo shell de Windows. Pon entre comillas los valores que contengan espacios." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Anuncia una URL base personalizada, por ejemplo un proxy inverso o un túnel. Déjalo vacío para que sea automático." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Automático" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Automático ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Aprobación automática" @@ -1582,6 +1597,15 @@ msgstr "Selección automática" msgid "Auto-show terminal panel" msgstr "Mostrar el panel del terminal automáticamente" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automático" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automático ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Elige una carpeta principal" msgid "Choose at least two candidates and enter a prompt." msgstr "Elige al menos dos candidatos e introduce un prompt." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Elige automáticamente o reemplaza la selección con un shell detectado para los terminales nuevos." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Elige cómo iniciar sesión." msgid "Choose import destination" msgstr "Elegir destino de importación" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Elige los shells del panel de terminal y de los comandos internos de Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Elegir archivo de identidad SSH" @@ -2713,6 +2746,10 @@ msgstr "El comando es obligatorio." msgid "Command not found." msgstr "No se encontró el comando." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Símbolo del sistema" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Automatización predeterminada de PR" msgid "Default thread removal" msgstr "Eliminación de hilos predeterminada" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Shell de Windows predeterminado" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Delega trabajo específico a otros agentes instalados y coordina los resultados." @@ -4201,6 +4243,10 @@ msgstr "Descarta los envoltorios de hook entrantes en el supervisor para que los msgid "Duplicate server name in scan" msgstr "Nombre de servidor duplicado en el análisis" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "p. ej. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "p. ej. 512k o 1m" @@ -5891,6 +5937,11 @@ msgstr "Instrucciones" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Instrucciones que siguen los agentes al elegir a qué agente o modelo delegar." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Comandos internos y agentes" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "El reenvío de puertos no está habilitado" msgid "Ports" msgstr "Puertos" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (recomendado)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (recomendado)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Acciones de PR" @@ -9987,6 +10054,10 @@ msgstr "Guardar atajo" msgid "Saved credentials are shared across all environments." msgstr "Las credenciales guardadas se comparten entre todos los entornos." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Shell guardado" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Escanear código QR de emparejamiento" @@ -10424,6 +10495,11 @@ msgstr "Comparte tu actividad" msgid "Shared" msgstr "Compartidas" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Argumentos del shell" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Fuente del marketplace de skills" msgid "Skill runs" msgstr "Ejecuciones de skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Habilidad: {skill}" @@ -11464,6 +11543,11 @@ msgstr "El terminal no está listo — se agregó la selección al compositor." msgid "Terminal panel font size" msgstr "Tamaño de fuente del panel del terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Shell del panel de terminal" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Posición del terminal" msgid "Terminal scroll speed" msgstr "Velocidad de desplazamiento del terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Argumentos del shell del terminal" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Usado" msgid "Used by {conflict}" msgstr "Lo usa {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Se usa para agentes, autenticación, instalaciones y comandos internos de Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Se usa para las nuevas sesiones interactivas del panel de terminal." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Se usa cuando no se especifica un proveedor. El uso explícito de Crossagents ocupa el primer lugar, seguido de los favoritos, el uso normal de agentes y el orden integrado." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows aún no es compatible. Instala dentro de WSL o en macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Shells de Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Ganador fusionado." diff --git a/src/renderer/locales/fr/messages.po b/src/renderer/locales/fr/messages.po index 5656754c5..5f0f5c577 100644 --- a/src/renderer/locales/fr/messages.po +++ b/src/renderer/locales/fr/messages.po @@ -1028,6 +1028,16 @@ msgstr "Ajouter une tâche" msgid "Add workspace" msgstr "Ajouter un espace de travail" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Arguments supplémentaires transmis à chaque nouveau shell du panneau de terminal. Placez entre guillemets les valeurs contenant des espaces." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Arguments supplémentaires transmis à chaque nouveau shell Windows. Placez entre guillemets les valeurs contenant des espaces." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Annoncez une URL de base personnalisée, par ex. un proxy inverse ou un tunnel. Laissez vide pour un choix automatique." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Automatique" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Automatique ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Approuver automatiquement" @@ -1582,6 +1597,15 @@ msgstr "Sélection automatique" msgid "Auto-show terminal panel" msgstr "Afficher automatiquement le panneau de terminal" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automatique" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automatique ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Choisissez un dossier parent" msgid "Choose at least two candidates and enter a prompt." msgstr "Choisissez au moins deux candidats et saisissez une invite." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Choisissez automatiquement ou utilisez un shell détecté pour les nouveaux terminaux." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Choisissez comment vous connecter." msgid "Choose import destination" msgstr "Choisir la destination de l’importation" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Choisissez les shells du panneau de terminal et des commandes internes de Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Choisir le fichier d’identité SSH" @@ -2713,6 +2746,10 @@ msgstr "La commande est obligatoire." msgid "Command not found." msgstr "Commande introuvable." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Invite de commandes" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Automatisation des PR par défaut" msgid "Default thread removal" msgstr "Suppression du fil par défaut" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Shell Windows par défaut" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Déléguez un travail ciblé à d'autres agents installés et coordonnez les résultats." @@ -4201,6 +4243,10 @@ msgstr "Supprime les enveloppes de hook entrantes sur le superviseur afin que le msgid "Duplicate server name in scan" msgstr "Nom de serveur en double dans l’analyse" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "p. ex. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "p. ex. 512k ou 1m" @@ -5891,6 +5937,11 @@ msgstr "Instructions" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Instructions que les agents suivent pour choisir à quel agent ou modèle déléguer." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Commandes internes et agents" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8574,6 +8625,22 @@ msgstr "Le transfert de port n'est pas activé" msgid "Ports" msgstr "Ports" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (recommandé)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (recommandé)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Actions de la PR" @@ -9986,6 +10053,10 @@ msgstr "Enregistrer le raccourci" msgid "Saved credentials are shared across all environments." msgstr "Les informations d'identification enregistrées sont partagées dans tous les environnements." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Shell enregistré" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Scanner le QR code d'association" @@ -10423,6 +10494,11 @@ msgstr "Partagez votre activité" msgid "Shared" msgstr "Partagées" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Arguments du shell" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10704,7 +10780,10 @@ msgstr "Source de la marketplace de skills" msgid "Skill runs" msgstr "Exécutions de skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Compétence :{skill}" @@ -11463,6 +11542,11 @@ msgstr "Terminal non prêt – sélection ajoutée au compositeur." msgid "Terminal panel font size" msgstr "Taille de la police du panneau Terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Shell du panneau de terminal" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11473,6 +11557,11 @@ msgstr "Position du terminal" msgid "Terminal scroll speed" msgstr "Vitesse de défilement du terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Arguments du shell du terminal" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12792,6 +12881,16 @@ msgstr "Utilisé" msgid "Used by {conflict}" msgstr "Utilisé par {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Utilisé pour les agents, l’authentification, les installations et les commandes internes de Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Utilisé pour les nouvelles sessions interactives du panneau de terminal." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Utilisé lorsqu’aucun fournisseur n’est spécifié. L’utilisation explicite de Crossagents est classée en premier, suivie des favoris, de l’utilisation normale des agents et de l’ordre intégré." @@ -13145,6 +13244,14 @@ msgstr "Fenêtres" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows n'est pas encore pris en charge. Installez dans WSL ou sur macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Shells Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Gagnant fusionné." diff --git a/src/renderer/locales/ja/messages.po b/src/renderer/locales/ja/messages.po index 6afe5af68..539b67846 100644 --- a/src/renderer/locales/ja/messages.po +++ b/src/renderer/locales/ja/messages.po @@ -1027,6 +1027,16 @@ msgstr "タスクを追加" msgid "Add workspace" msgstr "ワークスペースを追加" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "新しいターミナルパネルのシェルごとに渡す追加引数です。空白を含む値は引用符で囲んでください。" + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "新しい各 Windows シェルに渡す追加引数です。スペースを含む値は引用符で囲んでください。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "カスタムのベースURLを通知します(例: リバースプロキシやトンネル)。空欄の場合は自動的に設定されます。" @@ -1516,6 +1526,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "自動" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "自動 ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "自動承認" @@ -1581,6 +1596,15 @@ msgstr "自動選択" msgid "Auto-show terminal panel" msgstr "端末パネルの自動表示" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "自動" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "自動 ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2222,6 +2246,11 @@ msgstr "親フォルダーを選択" msgid "Choose at least two candidates and enter a prompt." msgstr "候補を 2 つ以上選択し、プロンプトを入力してください。" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "自動で選択するか、新しいターミナルで検出されたシェルを指定します。" + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2239,6 +2268,10 @@ msgstr "サインイン方法を選択します。" msgid "Choose import destination" msgstr "インポート先を選択" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "ターミナルパネルと Poracode の内部コマンドで使用するシェルを選択します。" + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "SSH 秘密鍵ファイルを選択" @@ -2712,6 +2745,10 @@ msgstr "コマンドは必須です。" msgid "Command not found." msgstr "コマンドが見つかりません。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "コマンド プロンプト" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3697,6 +3734,11 @@ msgstr "PR自動化のデフォルト" msgid "Default thread removal" msgstr "デフォルトのスレッドの削除" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "既定の Windows シェル" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "他のインストール済みエージェントに作業を委任し、結果を調整します。" @@ -4200,6 +4242,10 @@ msgstr "受信したフック エンベロープをスーパーバイザー側 msgid "Duplicate server name in scan" msgstr "スキャン内でサーバー名が重複しています" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "例: -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "例: 512k または 1m" @@ -5890,6 +5936,11 @@ msgstr "指示" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "どのエージェントやモデルに委任するかを選ぶ際に、エージェントが従う指示。" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "内部コマンドとエージェント" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8573,6 +8624,22 @@ msgstr "ポート転送が有効になっていません" msgid "Ports" msgstr "ポート" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version}(推奨)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7(推奨)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PRアクション" @@ -9985,6 +10052,10 @@ msgstr "ショートカットを保存" msgid "Saved credentials are shared across all environments." msgstr "保存された認証情報はすべての環境で共有されます。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "保存済みのシェル" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "ペアリング QR コードをスキャン" @@ -10422,6 +10493,11 @@ msgstr "アクティビティを共有" msgid "Shared" msgstr "共有" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "シェル引数" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10703,7 +10779,10 @@ msgstr "スキルマーケットプレイスのソース" msgid "Skill runs" msgstr "スキル実行" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "スキル:{skill}" @@ -11462,6 +11541,11 @@ msgstr "ターミナルの準備ができていません — コンポーザー msgid "Terminal panel font size" msgstr "ターミナルパネルのフォントサイズ" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "ターミナルパネルのシェル" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11472,6 +11556,11 @@ msgstr "ターミナルの位置" msgid "Terminal scroll speed" msgstr "ターミナルのスクロール速度" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "ターミナルシェルの引数" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12791,6 +12880,16 @@ msgstr "使用済み" msgid "Used by {conflict}" msgstr "{conflict}で使用中" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "エージェント、認証、インストール、Poracode の内部コマンドに使用します。" + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "新しい対話型ターミナルパネルセッションに使用します。" + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "プロバイダーが指定されていない場合に使用されます。Crossagents で明示的に使用したもの、 お気に入り、通常のエージェント使用、組み込み順の順に優先されます。" @@ -13144,6 +13243,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows はまだサポートされていません。WSL 内または macOS/Linux にインストールしてください。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows シェル" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "勝者をマージしました。" diff --git a/src/renderer/locales/ko/messages.po b/src/renderer/locales/ko/messages.po index 502d8a41b..ac37b60f9 100644 --- a/src/renderer/locales/ko/messages.po +++ b/src/renderer/locales/ko/messages.po @@ -1028,6 +1028,16 @@ msgstr "할 일 추가" msgid "Add workspace" msgstr "작업 영역 추가" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "새 터미널 패널 셸마다 전달할 추가 인수입니다. 공백이 포함된 값은 따옴표로 묶으세요." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "새 Windows 셸마다 전달할 추가 인수입니다. 공백이 포함된 값은 따옴표로 묶으세요." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "사용자 정의 기본 URL을 게시합니다(예: 리버스 프록시 또는 터널). 자동으로 설정하려면 비워 두세요." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "자동" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "자동 ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "자동 승인" @@ -1582,6 +1597,15 @@ msgstr "자동 선택" msgid "Auto-show terminal panel" msgstr "터미널 패널 자동 표시" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "자동" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "자동 ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "상위 폴더 선택" msgid "Choose at least two candidates and enter a prompt." msgstr "후보를 두 개 이상 선택하고 프롬프트를 입력하세요." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "자동으로 선택하거나 새 터미널에 사용할 감지된 셸을 지정합니다." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "로그인 방법을 선택하세요." msgid "Choose import destination" msgstr "가져오기 대상 선택" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "터미널 패널과 Poracode 내부 명령에 사용할 셸을 선택합니다." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "SSH ID 파일 선택" @@ -2713,6 +2746,10 @@ msgstr "명령을 입력해야 합니다." msgid "Command not found." msgstr "명령을 찾을 수 없습니다." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "명령 프롬프트" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "기본 PR 자동화" msgid "Default thread removal" msgstr "기본 스레드 제거" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "기본 Windows 셸" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "설치된 다른 에이전트에 작업을 위임하고 결과를 조율합니다." @@ -4201,6 +4243,10 @@ msgstr "에이전트가 설치 또는 iTerm2 알림을 건드리지 않고 L2(OS msgid "Duplicate server name in scan" msgstr "스캔에 중복된 서버 이름이 있습니다" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "예: -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "예: 512k 또는 1m" @@ -5891,6 +5937,11 @@ msgstr "지침" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "에이전트가 어떤 에이전트나 모델에 위임할지 선택할 때 따르는 지침입니다." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "내부 명령 및 에이전트" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "포트 포워딩이 활성화되지 않았습니다" msgid "Ports" msgstr "포트" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version}(권장)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7(권장)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PR 작업" @@ -9987,6 +10054,10 @@ msgstr "단축키 저장" msgid "Saved credentials are shared across all environments." msgstr "저장된 자격 증명은 모든 환경에서 공유됩니다." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "저장된 셸" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "페어링 QR 코드 스캔" @@ -10424,6 +10495,11 @@ msgstr "활동 공유하기" msgid "Shared" msgstr "공유" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "셸 인수" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "스킬 마켓플레이스 소스" msgid "Skill runs" msgstr "스킬 실행" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "스킬: {skill}" @@ -11464,6 +11543,11 @@ msgstr "터미널이 준비되지 않음 - 작성기에 선택 항목이 추가 msgid "Terminal panel font size" msgstr "터미널 패널 글꼴 크기" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "터미널 패널 셸" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "터미널 위치" msgid "Terminal scroll speed" msgstr "터미널 스크롤 속도" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "터미널 셸 인수" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "사용됨" msgid "Used by {conflict}" msgstr "{conflict}에서 사용 중" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "에이전트, 인증, 설치 및 Poracode 내부 명령에 사용됩니다." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "새 대화형 터미널 패널 세션에 사용됩니다." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "제공자를 지정하지 않았을 때 사용합니다. 명시적인 Crossagents 사용, 즐겨찾기, 일반 에이전트 사용, 기본 제공 순서 순으로 우선합니다." @@ -13146,6 +13245,14 @@ msgstr "윈도우" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "윈도우는 아직 지원되지 않습니다. WSL 내부 또는 macOS/Linux에 설치합니다." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows 셸" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "우승자를 병합했습니다." diff --git a/src/renderer/locales/pl/messages.po b/src/renderer/locales/pl/messages.po index f4fa4f70a..eed5a6168 100644 --- a/src/renderer/locales/pl/messages.po +++ b/src/renderer/locales/pl/messages.po @@ -1028,6 +1028,16 @@ msgstr "Dodaj zadanie" msgid "Add workspace" msgstr "Dodaj obszar roboczy" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Dodatkowe argumenty przekazywane do każdej nowej powłoki panelu terminala. Wartości zawierające spacje ujmij w cudzysłowy." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Dodatkowe argumenty przekazywane do każdej nowej powłoki Windows. Wartości zawierające spacje ujmij w cudzysłowy." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Ustaw niestandardowy bazowy URL, np. reverse proxy lub tunel. Pozostaw puste, aby ustawić automatycznie." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Auto" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Automatycznie ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Automatyczne zatwierdzanie" @@ -1582,6 +1597,15 @@ msgstr "Wybór automatyczny" msgid "Auto-show terminal panel" msgstr "Automatyczne wyświetlanie panelu terminali" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automatycznie" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automatycznie ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Wybierz folder nadrzędny" msgid "Choose at least two candidates and enter a prompt." msgstr "Wybierz co najmniej dwóch kandydatów i wpisz prompt." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Wybierz automatycznie lub ustaw wykrytą powłokę dla nowych terminali." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Wybierz sposób logowania." msgid "Choose import destination" msgstr "Wybierz miejsce importu" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Wybierz powłoki dla panelu terminala i poleceń wewnętrznych Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Wybierz plik tożsamości SSH" @@ -2713,6 +2746,10 @@ msgstr "Polecenie jest wymagane." msgid "Command not found." msgstr "Nie znaleziono polecenia." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Wiersz polecenia" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Domyślna automatyzacja PR" msgid "Default thread removal" msgstr "Domyślne usuwanie wątku" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Domyślna powłoka Windows" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Deleguj konkretne zadania innym zainstalowanym agentom i koordynuj wyniki." @@ -4201,6 +4243,10 @@ msgstr "Upuszcza przychodzące koperty haków na nadzorcę, dzięki czemu agenci msgid "Duplicate server name in scan" msgstr "Powtórzona nazwa serwera w wynikach skanowania" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "np. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "np. 512k lub 1m" @@ -5891,6 +5937,11 @@ msgstr "Instrukcje" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Instrukcje, którymi kierują się agenci przy wyborze agenta lub modelu, któremu delegują zadanie." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Polecenia wewnętrzne i agenci" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Przekierowywanie portów nie jest włączone" msgid "Ports" msgstr "Porty" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (zalecany)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (zalecane)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Działania PR" @@ -9987,6 +10054,10 @@ msgstr "Zapisz skrót" msgid "Saved credentials are shared across all environments." msgstr "Zapisane poświadczenia są udostępniane we wszystkich środowiskach." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Zapisana powłoka" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Skanuj kod QR parowania" @@ -10424,6 +10495,11 @@ msgstr "Udostępnij swoją aktywność" msgid "Shared" msgstr "Współdzielone" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Argumenty powłoki" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Źródło marketplace skilli" msgid "Skill runs" msgstr "Uruchomienia skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Umiejętność: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Terminal nie jest gotowy — dodano zaznaczenie do pola tworzenia wiadom msgid "Terminal panel font size" msgstr "Rozmiar czcionki panelu terminala" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Powłoka panelu terminala" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Pozycja terminala" msgid "Terminal scroll speed" msgstr "Szybkość przewijania terminala" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Argumenty powłoki terminala" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Używany" msgid "Used by {conflict}" msgstr "Używany przez {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Używana przez agentów, uwierzytelnianie, instalacje i polecenia wewnętrzne Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Używana w nowych interaktywnych sesjach panelu terminala." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Używane, gdy nie określono dostawcy. Najpierw brane jest pod uwagę jawne użycie Crossagents, następnie ulubione, zwykłe użycie agentów i kolejność wbudowana." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "System Windows nie jest jeszcze obsługiwany. Zainstaluj w WSL lub na macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Powłoki Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Scalono zmiany zwycięzcy." diff --git a/src/renderer/locales/pt-BR/messages.po b/src/renderer/locales/pt-BR/messages.po index 4d843f27c..bd4fd4448 100644 --- a/src/renderer/locales/pt-BR/messages.po +++ b/src/renderer/locales/pt-BR/messages.po @@ -1028,6 +1028,16 @@ msgstr "Adicionar tarefa" msgid "Add workspace" msgstr "Adicionar espaço de trabalho" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Argumentos adicionais passados para cada novo shell do painel do terminal. Coloque entre aspas os valores que contêm espaços." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Argumentos adicionais passados para cada novo shell do Windows. Coloque entre aspas os valores que contêm espaços." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Anuncie uma URL base personalizada, por exemplo, um proxy reverso ou túnel. Deixe em branco para automático." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Automático" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Automático ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Aprovação automática" @@ -1582,6 +1597,15 @@ msgstr "Seleção automática" msgid "Auto-show terminal panel" msgstr "Mostrar painel do terminal automaticamente" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Automático" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Automático ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Escolha uma pasta pai" msgid "Choose at least two candidates and enter a prompt." msgstr "Escolha pelo menos dois candidatos e insira um prompt." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Escolha automaticamente ou defina um shell detectado para novos terminais." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Escolha como fazer login." msgid "Choose import destination" msgstr "Escolher destino da importação" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Escolha os shells do painel do terminal e dos comandos internos do Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Escolher arquivo de identidade SSH" @@ -2713,6 +2746,10 @@ msgstr "O comando é obrigatório." msgid "Command not found." msgstr "Comando não encontrado." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Prompt de Comando" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Automação padrão de PR" msgid "Default thread removal" msgstr "Remoção de thread padrão" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Shell padrão do Windows" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Delegue trabalho específico a outros agentes instalados e coordene os resultados." @@ -4201,6 +4243,10 @@ msgstr "Descarta envelopes de gancho recebidos no supervisor para que os agentes msgid "Duplicate server name in scan" msgstr "Nome de servidor duplicado na verificação" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "ex.: -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "ex.: 512k ou 1m" @@ -5891,6 +5937,11 @@ msgstr "Instruções" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Instruções que os agentes seguem ao escolher para qual agente ou modelo delegar." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Comandos internos e agentes" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "O encaminhamento de portas não está ativado" msgid "Ports" msgstr "Portas" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (recomendado)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (recomendado)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Ações de PR" @@ -9987,6 +10054,10 @@ msgstr "Salvar atalho" msgid "Saved credentials are shared across all environments." msgstr "As credenciais salvas são compartilhadas em todos os ambientes." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Shell salvo" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Escanear QR code de pareamento" @@ -10424,6 +10495,11 @@ msgstr "Compartilhe sua atividade" msgid "Shared" msgstr "Compartilhadas" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Argumentos do shell" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Fonte do marketplace de skills" msgid "Skill runs" msgstr "Execuções de skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Habilidade: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Terminal não pronto — seleção adicionada ao compositor." msgid "Terminal panel font size" msgstr "Tamanho da fonte do painel do terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Shell do painel do terminal" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Posição do terminal" msgid "Terminal scroll speed" msgstr "Velocidade de rolagem do terminal" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Argumentos do shell do terminal" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Usado" msgid "Used by {conflict}" msgstr "Usado por {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Usado para agentes, autenticação, instalações e comandos internos do Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Usado para novas sessões interativas do painel do terminal." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Usado quando nenhum provedor é especificado. O uso explícito de Crossagents vem primeiro, seguido pelos favoritos, pelo uso normal de agentes e pela ordem integrada." @@ -13146,6 +13245,14 @@ msgstr "Janelas" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "O Windows ainda não é compatível. Instale dentro do WSL ou no macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Shells do Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Vencedor mesclado." diff --git a/src/renderer/locales/ru/messages.po b/src/renderer/locales/ru/messages.po index 1de48890f..30867443b 100644 --- a/src/renderer/locales/ru/messages.po +++ b/src/renderer/locales/ru/messages.po @@ -1028,6 +1028,16 @@ msgstr "Добавить задачу" msgid "Add workspace" msgstr "Добавить рабочую область" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Дополнительные аргументы для каждой новой оболочки панели терминала. Значения с пробелами заключайте в кавычки." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Дополнительные аргументы, передаваемые каждой новой оболочке Windows. Значения с пробелами заключайте в кавычки." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Укажите собственный базовый URL, например обратный прокси или туннель. Оставьте пустым для автоматического режима." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Авто" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Автоматически ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Автоодобрение" @@ -1582,6 +1597,15 @@ msgstr "Автовыбор" msgid "Auto-show terminal panel" msgstr "Автопоказ панели терминала" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Автоматически" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Автоматически ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Выберите родительскую папку" msgid "Choose at least two candidates and enter a prompt." msgstr "Выберите как минимум двух кандидатов и введите промпт." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Выберите автоматически или укажите обнаруженную оболочку для новых терминалов." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Выберите способ входа." msgid "Choose import destination" msgstr "Выберите область импорта" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Выберите оболочки для панели терминала и внутренних команд Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Выбрать файл идентификации SSH" @@ -2713,6 +2746,10 @@ msgstr "Команда обязательна." msgid "Command not found." msgstr "Команда не найдена." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Командная строка" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Автоматизация PR по умолчанию" msgid "Default thread removal" msgstr "Удаление тредов по умолчанию" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Оболочка Windows по умолчанию" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Делегируйте задачи другим установленным агентам и координируйте результаты." @@ -4201,6 +4243,10 @@ msgstr "Отбрасывает входящие конверты хуков на msgid "Duplicate server name in scan" msgstr "Повторяющееся имя сервера в результатах сканирования" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "напр. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "напр. 512k или 1m" @@ -5891,6 +5937,11 @@ msgstr "Инструкции" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Инструкции, которым следуют агенты при выборе агента или модели для делегирования." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Внутренние команды и агенты" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Перенаправление портов не включено" msgid "Ports" msgstr "Порты" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (рекомендуется)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (рекомендуется)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Действия PR" @@ -9987,6 +10054,10 @@ msgstr "Сохранить сочетание клавиш" msgid "Saved credentials are shared across all environments." msgstr "Сохранённые учётные данные используются во всех средах." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Сохранённая оболочка" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Сканировать QR-код привязки" @@ -10424,6 +10495,11 @@ msgstr "Поделитесь своей активностью" msgid "Shared" msgstr "Общие" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Аргументы оболочки" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Источник маркетплейса навыков" msgid "Skill runs" msgstr "Запуски skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Навык: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Терминал не готов — выбор добавлен в ко msgid "Terminal panel font size" msgstr "Размер шрифта панели терминала" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Оболочка панели терминала" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Положение терминала" msgid "Terminal scroll speed" msgstr "Скорость прокрутки терминала" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Аргументы оболочки терминала" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Использовано" msgid "Used by {conflict}" msgstr "Используется командой {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Используется для агентов, аутентификации, установки и внутренних команд Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Используется для новых интерактивных сеансов панели терминала." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Используется, когда провайдер не указан. На первом месте — явное использование Crossagents, затем избранное, обычное использование агентов и встроенный порядок." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows пока не поддерживается. Установите в WSL или на macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Оболочки Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Изменения победителя слиты." diff --git a/src/renderer/locales/tr/messages.po b/src/renderer/locales/tr/messages.po index 68a73ab7c..481f772af 100644 --- a/src/renderer/locales/tr/messages.po +++ b/src/renderer/locales/tr/messages.po @@ -1028,6 +1028,16 @@ msgstr "Yapılacak ekle" msgid "Add workspace" msgstr "Çalışma alanı ekle" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Her yeni terminal paneli kabuğuna aktarılan ek bağımsız değişkenler. Boşluk içeren değerleri tırnak içine alın." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Her yeni Windows kabuğuna aktarılan ek bağımsız değişkenler. Boşluk içeren değerleri tırnak içine alın." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Özel bir temel URL duyurun, örn. bir ters proxy veya tünel. Otomatik için boş bırakın." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Otomatik" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Otomatik ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Otomatik Onayla" @@ -1582,6 +1597,15 @@ msgstr "Otomatik seçim" msgid "Auto-show terminal panel" msgstr "Terminal panelini otomatik göster" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Otomatik" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Otomatik ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Bir ana klasör seçin" msgid "Choose at least two candidates and enter a prompt." msgstr "En az iki aday seçin ve bir istem girin." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Otomatik seçin veya yeni terminaller için algılanan bir kabuk belirleyin." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Nasıl oturum açılacağını seçin." msgid "Choose import destination" msgstr "İçe aktarma hedefini seç" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Terminal paneli ve Poracode dahili komutları için kabukları seçin." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "SSH kimlik dosyasını seç" @@ -2713,6 +2746,10 @@ msgstr "Komut gereklidir." msgid "Command not found." msgstr "Komut bulunamadı." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Komut İstemi" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Varsayılan PR otomasyonu" msgid "Default thread removal" msgstr "Varsayılan konu kaldırma" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Varsayılan Windows kabuğu" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Odaklı işleri kurulu diğer ajanlara devredin ve sonuçları koordine edin." @@ -4201,6 +4243,10 @@ msgstr "Aracıların kurulum veya iTerm2 bildirimlerine dokunmadan L2'ye (OSC 9; msgid "Duplicate server name in scan" msgstr "Taramada yinelenen sunucu adı" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "örn. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "örn. 512k veya 1m" @@ -5891,6 +5937,11 @@ msgstr "Talimatlar" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Aracıların hangi aracıya veya modele devredeceğini seçerken izlediği talimatlar." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Dahili komutlar ve ajanlar" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Port yönlendirme etkin değil" msgid "Ports" msgstr "Portlar" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (önerilen)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (önerilen)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PR eylemleri" @@ -9987,6 +10054,10 @@ msgstr "Kısayolu kaydet" msgid "Saved credentials are shared across all environments." msgstr "Kaydedilen kimlik bilgileri tüm ortamlarda paylaşılır." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Kaydedilmiş kabuk" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Eşleştirme QR kodunu tara" @@ -10424,6 +10495,11 @@ msgstr "Etkinliğinizi paylaşın" msgid "Shared" msgstr "Paylaşılan" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Kabuk bağımsız değişkenleri" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Beceri pazar yeri kaynağı" msgid "Skill runs" msgstr "Skill çalıştırmaları" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Beceri: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Terminal hazır değil — seçim Composer'a eklendi." msgid "Terminal panel font size" msgstr "Terminal paneli yazı tipi boyutu" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Terminal paneli kabuğu" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Terminal konumu" msgid "Terminal scroll speed" msgstr "Terminal kaydırma hızı" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Terminal kabuğu bağımsız değişkenleri" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Kullanılmış" msgid "Used by {conflict}" msgstr "{conflict} tarafından kullanılıyor" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Ajanlar, kimlik doğrulama, yüklemeler ve Poracode dahili komutları için kullanılır." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Yeni etkileşimli terminal paneli oturumları için kullanılır." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Sağlayıcı belirtilmediğinde kullanılır. Önce açık Crossagents kullanımı, ardından favoriler, normal ajan kullanımı ve yerleşik sıra gelir." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows henüz desteklenmiyor. WSL'nin içine veya macOS/Linux'a yükleyin." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows kabukları" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Kazanan birleştirildi." diff --git a/src/renderer/locales/uk/messages.po b/src/renderer/locales/uk/messages.po index c3fa09a30..a603c16e5 100644 --- a/src/renderer/locales/uk/messages.po +++ b/src/renderer/locales/uk/messages.po @@ -1028,6 +1028,16 @@ msgstr "Додати завдання" msgid "Add workspace" msgstr "Додати робочу область" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Додаткові аргументи для кожної нової оболонки панелі термінала. Значення з пробілами беріть у лапки." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Додаткові аргументи, що передаються кожній новій оболонці Windows. Значення з пробілами беріть у лапки." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Оголосіть власний базовий URL, наприклад зворотний проксі або тунель. Залиште порожнім для автоматичного режиму." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Авто" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Автоматично ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Автосхвалення" @@ -1582,6 +1597,15 @@ msgstr "Автовибір" msgid "Auto-show terminal panel" msgstr "Автоматично показувати панель термінала" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Автоматично" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Автоматично ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Виберіть батьківську папку" msgid "Choose at least two candidates and enter a prompt." msgstr "Виберіть щонайменше двох кандидатів і введіть промпт." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Виберіть автоматично або вкажіть виявлену оболонку для нових терміналів." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Виберіть спосіб входу." msgid "Choose import destination" msgstr "Вибрати місце імпорту" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Виберіть оболонки для панелі термінала та внутрішніх команд Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Вибрати файл ідентифікації SSH" @@ -2713,6 +2746,10 @@ msgstr "Команда обов’язкова." msgid "Command not found." msgstr "Команду не знайдено." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Командний рядок" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Автоматизація PR за замовчуванням" msgid "Default thread removal" msgstr "Типове видалення треду" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Оболонка Windows за замовчуванням" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Делегуйте завдання іншим встановленим агентам і координуйте результати." @@ -4201,6 +4243,10 @@ msgstr "Відкидає вхідні конверти хуків на супе msgid "Duplicate server name in scan" msgstr "Повторювана назва сервера в результатах сканування" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "напр. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "напр. 512k або 1m" @@ -5891,6 +5937,11 @@ msgstr "Інструкції" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Інструкції, яких дотримуються агенти, обираючи, якому агенту чи моделі делегувати." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Внутрішні команди й агенти" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Переспрямування портів не увімкнено" msgid "Ports" msgstr "Порти" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (рекомендовано)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (рекомендовано)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "Дії PR" @@ -9987,6 +10054,10 @@ msgstr "Зберегти комбінацію клавіш" msgid "Saved credentials are shared across all environments." msgstr "Збережені облікові дані використовуються в усіх середовищах." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Збережена оболонка" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Сканировать QR-код привязки" @@ -10424,6 +10495,11 @@ msgstr "Поділіться своєю активністю" msgid "Shared" msgstr "Спільні" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Аргументи оболонки" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Джерело маркетплейсу навичок" msgid "Skill runs" msgstr "Запуски skills" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Навичка: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Термінал не готовий — вибір додано в ко msgid "Terminal panel font size" msgstr "Розмір шрифту панелі термінала" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Оболонка панелі термінала" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Розташування термінала" msgid "Terminal scroll speed" msgstr "Швидкість прокручування термінала" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Аргументи оболонки термінала" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Використано" msgid "Used by {conflict}" msgstr "Використовується для {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Використовується для агентів, автентифікації, інсталяцій і внутрішніх команд Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Використовується для нових інтерактивних сеансів панелі термінала." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Використовується, коли провайдера не вказано. Спочатку враховується явне використання Crossagents, потім обране, звичайне використання агентів і вбудований порядок." @@ -13146,6 +13245,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows поки не підтримується. Встановіть у WSL або на macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Оболонки Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Зміни переможця злито." diff --git a/src/renderer/locales/vi/messages.po b/src/renderer/locales/vi/messages.po index e0bccba1c..aea973eeb 100644 --- a/src/renderer/locales/vi/messages.po +++ b/src/renderer/locales/vi/messages.po @@ -1028,6 +1028,16 @@ msgstr "Thêm việc cần làm" msgid "Add workspace" msgstr "Thêm không gian làm việc" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "Các đối số bổ sung được truyền cho mỗi shell mới của bảng terminal. Hãy đặt giá trị có khoảng trắng trong dấu ngoặc kép." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "Các đối số bổ sung được truyền cho mỗi shell Windows mới. Đặt các giá trị có khoảng trắng trong dấu ngoặc kép." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Công bố một URL cơ sở tùy chỉnh, ví dụ một reverse proxy hoặc tunnel. Để trống để dùng tự động." @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "Tự động" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "Tự động ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "Tự động phê duyệt" @@ -1582,6 +1597,15 @@ msgstr "Tự động chọn" msgid "Auto-show terminal panel" msgstr "Tự động hiển thị bảng điều khiển thiết bị đầu cuối" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "Tự động" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "Tự động ({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "Chọn một thư mục cha" msgid "Choose at least two candidates and enter a prompt." msgstr "Chọn ít nhất hai ứng viên và nhập lời nhắc." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "Tự động chọn hoặc chỉ định một shell đã phát hiện cho các terminal mới." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "Chọn cách đăng nhập." msgid "Choose import destination" msgstr "Chọn đích nhập" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "Chọn shell cho bảng terminal và các lệnh nội bộ của Poracode." + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "Chọn tệp danh tính SSH" @@ -2713,6 +2746,10 @@ msgstr "Bắt buộc phải có lệnh." msgid "Command not found." msgstr "Không tìm thấy lệnh." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "Dấu nhắc Lệnh" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "Tự động hóa PR mặc định" msgid "Default thread removal" msgstr "Hành vi xóa chủ đề mặc định" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "Shell Windows mặc định" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "Giao việc cụ thể cho các agent đã cài khác và điều phối kết quả." @@ -4201,6 +4243,10 @@ msgstr "Loại bỏ các gói hook đến tại trình giám sát để các tá msgid "Duplicate server name in scan" msgstr "Tên máy chủ bị trùng trong kết quả quét" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "vd. -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "vd. 512k hoặc 1m" @@ -5891,6 +5937,11 @@ msgstr "Hướng dẫn" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "Hướng dẫn mà các tác nhân tuân theo khi chọn tác nhân hoặc mô hình để ủy thác." +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "Lệnh nội bộ và tác nhân" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8575,6 +8626,22 @@ msgstr "Chưa bật chuyển tiếp cổng" msgid "Ports" msgstr "Cổng" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version} (đề xuất)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7 (khuyên dùng)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "hoạt động PR" @@ -9987,6 +10054,10 @@ msgstr "Lưu phím tắt" msgid "Saved credentials are shared across all environments." msgstr "Thông tin đăng nhập đã lưu được chia sẻ trên tất cả các môi trường." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "Shell đã lưu" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "Quét mã QR ghép đôi" @@ -10424,6 +10495,11 @@ msgstr "Chia sẻ hoạt động của bạn" msgid "Shared" msgstr "Dùng chung" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Đối số shell" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10705,7 +10781,10 @@ msgstr "Nguồn marketplace skill" msgid "Skill runs" msgstr "Lượt chạy skill" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "Kỹ năng: {skill}" @@ -11464,6 +11543,11 @@ msgstr "Thiết bị đầu cuối chưa sẵn sàng - đã thêm lựa chọn v msgid "Terminal panel font size" msgstr "Kích thước phông chữ của bảng điều khiển thiết bị đầu cuối" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "Shell của bảng terminal" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11474,6 +11558,11 @@ msgstr "Vị trí đầu cuối" msgid "Terminal scroll speed" msgstr "Tốc độ cuộn thiết bị đầu cuối" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "Đối số shell terminal" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12793,6 +12882,16 @@ msgstr "Đã dùng" msgid "Used by {conflict}" msgstr "Đã được dùng bởi {conflict}" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "Dùng cho tác nhân, xác thực, cài đặt và các lệnh nội bộ của Poracode." + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "Dùng cho các phiên tương tác mới trong bảng terminal." + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "Được dùng khi không chỉ định nhà cung cấp. Mức sử dụng Crossagents rõ ràng được ưu tiên trước, tiếp theo là mục yêu thích, mức sử dụng tác nhân thông thường và thứ tự tích hợp." @@ -13146,6 +13245,14 @@ msgstr "cửa sổ" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "Windows chưa được hỗ trợ. Cài đặt bên trong WSL hoặc trên macOS/Linux." +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Shell Windows" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "Đã hợp nhất ứng viên thắng." diff --git a/src/renderer/locales/zh-CN/messages.po b/src/renderer/locales/zh-CN/messages.po index 2663ac13f..07e9242bf 100644 --- a/src/renderer/locales/zh-CN/messages.po +++ b/src/renderer/locales/zh-CN/messages.po @@ -1028,6 +1028,16 @@ msgstr "添加待办事项" msgid "Add workspace" msgstr "添加工作区" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces." +msgstr "传递给每个新终端面板 shell 的附加参数。包含空格的值请用引号括起来。" + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Additional arguments passed to each new Windows shell. Quote values containing spaces." +#~ msgstr "传递给每个新 Windows shell 的其他参数。包含空格的值请用引号括起来。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "公布自定义基础 URL,例如反向代理或隧道。留空则自动设置。" @@ -1517,6 +1527,11 @@ msgstr "Authorization: Bearer token" msgid "Auto" msgstr "自动" +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Auto ({0})" +#~ msgstr "自动 ({0})" + #: src/renderer/components/providers/composerControlBuilders.tsx msgid "Auto Approve" msgstr "自动批准" @@ -1582,6 +1597,15 @@ msgstr "自动选择" msgid "Auto-show terminal panel" msgstr "自动显示终端面板" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic" +#~ msgstr "自动" + +#. placeholder {0}: shellName(automaticShell) +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Automatic ({0})" +#~ msgstr "自动({0})" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Automatically show the terminal panel when running commands or creating worktrees." @@ -2223,6 +2247,11 @@ msgstr "选择父文件夹" msgid "Choose at least two candidates and enter a prompt." msgstr "请选择至少两个候选项并输入提示。" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Choose automatically or override with a detected shell for new terminals." +#~ msgstr "自动选择,或为新终端指定检测到的 shell。" + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,6 +2269,10 @@ msgstr "选择登录方式。" msgid "Choose import destination" msgstr "选择导入目标" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Choose shells for the Terminal panel and Poracode's internal commands." +msgstr "选择终端面板和 Poracode 内部命令所使用的 shell。" + #: src/renderer/views/SettingsOverlay/parts/SshConnectionForm.tsx msgid "Choose SSH identity file" msgstr "选择 SSH 身份文件" @@ -2713,6 +2746,10 @@ msgstr "命令为必填项。" msgid "Command not found." msgstr "未找到命令。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Command Prompt" +msgstr "命令提示符" + #: src/renderer/commands/CommandPalette.tsx #: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Commands" @@ -3698,6 +3735,11 @@ msgstr "默认 PR 自动化" msgid "Default thread removal" msgstr "默认线程删除" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Default Windows shell" +#~ msgstr "默认 Windows shell" + #: src/renderer/components/plugins/pluginCopy.ts msgid "Delegate focused work to other installed agents and coordinate the results." msgstr "将专项工作委派给其他已安装的代理并协调结果。" @@ -4201,6 +4243,10 @@ msgstr "在监管进程上丢弃传入的挂钩信封,以便代理回退到 L2 msgid "Duplicate server name in scan" msgstr "扫描结果中存在重复的服务器名称" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "e.g. -NoProfile" +msgstr "例如 -NoProfile" + #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "e.g. 512k or 1m" msgstr "例如 512k 或 1m" @@ -5891,6 +5937,11 @@ msgstr "指令" msgid "Instructions agents follow when choosing which agent or model to delegate to." msgstr "代理在选择要委派给哪个代理或模型时遵循的说明。" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Internal commands and agents" +msgstr "内部命令和代理" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #: src/renderer/views/RemoteThreadView/RemoteThreadView.tsx #~ msgid "Interrupt" @@ -8574,6 +8625,22 @@ msgstr "未启用端口转发" msgid "Ports" msgstr "端口" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version}" +msgstr "PowerShell {version}" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "PowerShell {version} (recommended)" +msgstr "PowerShell {version}(推荐)" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7" +#~ msgstr "PowerShell 7" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "PowerShell 7 (recommended)" +#~ msgstr "PowerShell 7(推荐)" + #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "PR actions" msgstr "PR 操作" @@ -9986,6 +10053,10 @@ msgstr "保存快捷键" msgid "Saved credentials are shared across all environments." msgstr "保存的凭据在所有环境中共享。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Saved shell" +msgstr "已保存的 shell" + #: src/mobile/QrScanner.tsx msgid "Scan pairing QR code" msgstr "扫描配对二维码" @@ -10423,6 +10494,11 @@ msgstr "分享你的活动" msgid "Shared" msgstr "共享" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +#~ msgid "Shell arguments" +#~ msgstr "Shell 参数" + #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx #: src/renderer/views/SettingsOverlay/parts/ShortcutsSettings.tsx msgid "Shortcuts" @@ -10704,7 +10780,10 @@ msgstr "技能市场来源" msgid "Skill runs" msgstr "技能运行次数" +#: src/renderer/components/composer/SlashCommandChip.ts #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts +#: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx +#: src/renderer/components/thread/ThreadCommandPanel.tsx msgid "Skill: {skill}" msgstr "技能:{skill}" @@ -11463,6 +11542,11 @@ msgstr "终端未就绪 — 已将所选内容添加到编辑器。" msgid "Terminal panel font size" msgstr "终端面板字体大小" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal panel shell" +msgstr "终端面板 shell" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx msgid "Terminal position" @@ -11473,6 +11557,11 @@ msgstr "终端位置" msgid "Terminal scroll speed" msgstr "终端滚动速度" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Terminal shell arguments" +msgstr "终端 shell 参数" + #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/BottomTerminalLayout.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/parts/RightTerminalLayout.tsx msgid "Terminal tabs" @@ -12792,6 +12881,16 @@ msgstr "已使用" msgid "Used by {conflict}" msgstr "已被{conflict}使用" +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for agents, authentication, installs, and Poracode's internal commands." +msgstr "用于代理、身份验证、安装和 Poracode 内部命令。" + +#: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Used for new interactive Terminal-panel sessions." +msgstr "用于新的交互式终端面板会话。" + #: src/renderer/views/SettingsOverlay/parts/CrossagentRoutingSection.tsx #~ msgid "Used when no provider is specified. Explicit Crossagents usage ranks first, followed by favorites, normal agent usage, and built-in order." #~ msgstr "未指定提供商时使用。优先按明确的 Crossagents 使用情况排名,其次是收藏、普通智能体使用情况和内置顺序。" @@ -13145,6 +13244,14 @@ msgstr "Windows" #~ msgid "Windows is not supported yet. Install inside WSL or on macOS/Linux." #~ msgstr "尚不支持 Windows。在 WSL 内部或 macOS/Linux 上安装。" +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows PowerShell 5.1" +msgstr "Windows PowerShell 5.1" + +#: src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +msgid "Windows shells" +msgstr "Windows shell" + #: src/renderer/views/ExperimentView/ExperimentView.tsx msgid "Winner merged." msgstr "已合并优胜者。" diff --git a/src/renderer/state/sharedSettingsStore.test.ts b/src/renderer/state/sharedSettingsStore.test.ts index c5b66d080..d7cddb512 100644 --- a/src/renderer/state/sharedSettingsStore.test.ts +++ b/src/renderer/state/sharedSettingsStore.test.ts @@ -1,8 +1,15 @@ -import { beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { pluginFixture, seedBuiltInPlugins } from "@/renderer/testUtils/plugins"; -import { useSharedSettings } from "./sharedSettingsStore"; +import { useSharedSettings, waitForPendingSharedSettings } from "./sharedSettingsStore"; + +const originalPoracodeBridge = window.poracode; describe("sharedSettingsStore", () => { + afterEach(() => { + if (originalPoracodeBridge) window.poracode = originalPoracodeBridge; + else Reflect.deleteProperty(window, "poracode"); + }); + beforeEach(() => { localStorage.clear(); seedBuiltInPlugins(); @@ -44,6 +51,47 @@ describe("sharedSettingsStore", () => { expect(useSharedSettings.getState().themeMode).toBe("light"); }); + it("updates the Windows shell path and arguments", () => { + useSharedSettings.getState().setWindowsShellPath("C:\\Tools\\pwsh.exe"); + useSharedSettings.getState().setWindowsInternalShellPath("C:\\Tools\\pwsh-preview.exe"); + useSharedSettings.getState().setWindowsShellArguments("-NoProfile"); + + expect(useSharedSettings.getState()).toMatchObject({ + windowsShellPath: "C:\\Tools\\pwsh.exe", + windowsInternalShellPath: "C:\\Tools\\pwsh-preview.exe", + windowsShellArguments: "-NoProfile", + }); + }); + + it("clamps Windows shell arguments to the shared settings maximum", () => { + useSharedSettings.getState().setWindowsShellArguments("x".repeat(8_193)); + expect(useSharedSettings.getState().windowsShellArguments).toHaveLength(8_192); + }); + + it("exposes pending settings persistence as a launch barrier", async () => { + let finishWrite!: () => void; + const setSharedSettings = vi.fn<() => Promise>( + () => + new Promise((resolve) => { + finishWrite = resolve; + }), + ); + window.poracode = { setSharedSettings } as unknown as typeof window.poracode; + + useSharedSettings.getState().setThemeMode("dark"); + let barrierFinished = false; + const barrier = waitForPendingSharedSettings().then(() => { + barrierFinished = true; + }); + await Promise.resolve(); + + expect(setSharedSettings).toHaveBeenCalledOnce(); + expect(barrierFinished).toBe(false); + finishWrite(); + await barrier; + expect(barrierFinished).toBe(true); + }); + it("updates the stale thread unload timing", () => { useSharedSettings.getState().setStaleThreadUnloadMinutes(30); expect(useSharedSettings.getState().staleThreadUnloadMinutes).toBe(30); diff --git a/src/renderer/state/sharedSettingsStore.ts b/src/renderer/state/sharedSettingsStore.ts index f0e7c1365..5e71ab4df 100644 --- a/src/renderer/state/sharedSettingsStore.ts +++ b/src/renderer/state/sharedSettingsStore.ts @@ -4,6 +4,7 @@ import { defaultSharedSettings, normalizeSidebarShortcutOrder, normalizeSharedSettings, + WINDOWS_SHELL_ARGUMENTS_MAX, type CliPickerTarget, type PreventSleep, type SidebarShortcutId, @@ -51,6 +52,9 @@ interface SharedSettingsState extends SharedSettings { setLocale: (locale: LocaleSetting) => void; setGitTextLanguage: (value: AiContentLanguage) => void; setTerminalPosition: (position: TerminalPosition) => void; + setWindowsShellPath: (path: string) => void; + setWindowsInternalShellPath: (path: string) => void; + setWindowsShellArguments: (args: string) => void; setCommitGenConfig: (provider: string, model: string, effort: string, fast: boolean) => void; setTitleGenConfig: (provider: string, model: string, effort: string, fast: boolean) => void; setConflictResolverConfig: ( @@ -218,6 +222,7 @@ function loadFallbackSettings(): SharedSettings { * clobber the file with default values before the real settings are loaded. */ let initialLoadDone = !hasBridge(); +let pendingSharedSettingsWrite: Promise | undefined; function persistSettings(settings: SharedSettingsInput): void { if (typeof window === "undefined") { @@ -227,10 +232,22 @@ function persistSettings(settings: SharedSettingsInput): void { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); if (hasBridge() && initialLoadDone) { - void readBridge().setSharedSettings(settings); + const write = readBridge().setSharedSettings(settings); + pendingSharedSettingsWrite = write; + void write + .catch(() => undefined) + .then(() => { + if (pendingSharedSettingsWrite === write) { + pendingSharedSettingsWrite = undefined; + } + }); } } +export async function waitForPendingSharedSettings(): Promise { + await pendingSharedSettingsWrite; +} + /** * Awaitable counterpart to `persistSettings`'s fire-and-forget bridge write. * Callers that need a guarantee the settings file reflects the latest store @@ -242,6 +259,10 @@ export async function flushSharedSettings(): Promise { if (!hasBridge() || !initialLoadDone) { return; } + if (pendingSharedSettingsWrite) { + await pendingSharedSettingsWrite; + return; + } await readBridge().setSharedSettings(selectSharedSettings(useSharedSettings.getState())); } @@ -299,6 +320,18 @@ export const useSharedSettings = create()((set, get) => ({ set({ terminalPosition }); persistSettings(selectSharedSettings(get())); }, + setWindowsShellPath: (windowsShellPath) => { + set({ windowsShellPath }); + persistSettings(selectSharedSettings(get())); + }, + setWindowsInternalShellPath: (windowsInternalShellPath) => { + set({ windowsInternalShellPath }); + persistSettings(selectSharedSettings(get())); + }, + setWindowsShellArguments: (windowsShellArguments) => { + set({ windowsShellArguments: windowsShellArguments.slice(0, WINDOWS_SHELL_ARGUMENTS_MAX) }); + persistSettings(selectSharedSettings(get())); + }, setCommitGenConfig: (commitGenProvider, commitGenModel, commitGenEffort, commitGenFast) => { set({ commitGenProvider, commitGenModel, commitGenEffort, commitGenFast }); persistSettings(selectSharedSettings(get())); @@ -911,6 +944,9 @@ function selectSharedSettings(state: SharedSettingsState): SharedSettingsInput { locale: state.locale, gitTextLanguage: state.gitTextLanguage, terminalPosition: state.terminalPosition, + windowsShellPath: state.windowsShellPath, + windowsInternalShellPath: state.windowsInternalShellPath, + windowsShellArguments: state.windowsShellArguments, commitGenProvider: state.commitGenProvider, commitGenModel: state.commitGenModel, commitGenEffort: state.commitGenEffort, diff --git a/src/renderer/utils/shellUtils.test.ts b/src/renderer/utils/shellUtils.test.ts index ba00b7053..af11828e0 100644 --- a/src/renderer/utils/shellUtils.test.ts +++ b/src/renderer/utils/shellUtils.test.ts @@ -10,6 +10,7 @@ const bridge = vi.hoisted(() => ({ const supervisorHandlers = vi.hoisted(() => [] as Array<(event: SupervisorEvent) => void>); const toastDanger = vi.hoisted(() => vi.fn<(message: string) => void>()); +const waitForPendingSharedSettings = vi.hoisted(() => vi.fn<() => Promise>()); vi.mock("@heroui/react", () => ({ toast: { @@ -23,12 +24,17 @@ vi.mock("@/renderer/bridge", () => ({ readBridge: () => bridge, })); +vi.mock("@/renderer/state/sharedSettingsStore", () => ({ + waitForPendingSharedSettings, +})); + import { appendExitOnSuccess, buildScriptWithExitOnSuccess, clearEagerShellStart, closeThreads, runShellScriptToCompletion, + startShellWithCurrentSettings, startShellWithToast, wasShellStartedEagerly, writeScriptToShell, @@ -58,6 +64,48 @@ function unwrapBashScript(script: string): string { return quoted.slice(1, -1).replaceAll("'\\''", "'"); } +beforeEach(() => { + waitForPendingSharedSettings.mockReset().mockResolvedValue(undefined); +}); + +describe("startShellWithCurrentSettings", () => { + it("waits for settings persistence before dispatching the shell launch", async () => { + let finishFlush!: () => void; + waitForPendingSharedSettings.mockReturnValueOnce( + new Promise((resolve) => { + finishFlush = resolve; + }), + ); + bridge.startShell.mockReset().mockResolvedValue(undefined); + + const starting = startShellWithCurrentSettings({ + shellId: "shell:settings-barrier", + projectLocation: { kind: "windows", path: "C:\\repo" }, + }); + expect(bridge.startShell).not.toHaveBeenCalled(); + + finishFlush(); + await starting; + + expect(waitForPendingSharedSettings.mock.invocationCallOrder[0]).toBeLessThan( + bridge.startShell.mock.invocationCallOrder[0]!, + ); + }); + + it("still starts with the last persisted settings if the pending write fails", async () => { + waitForPendingSharedSettings.mockRejectedValueOnce(new Error("settings write failed")); + bridge.startShell.mockReset().mockResolvedValue(undefined); + + await expect( + startShellWithCurrentSettings({ + shellId: "shell:settings-fallback", + projectLocation: { kind: "wsl", distro: "Ubuntu", linuxPath: "/repo", uncPath: "" }, + }), + ).resolves.toBeUndefined(); + expect(bridge.startShell).toHaveBeenCalledOnce(); + }); +}); + describe("appendExitOnSuccess", () => { it("uses `&& exit` for posix shells (exit is a command)", () => { expect(appendExitOnSuccess("npm ci", "posix")).toBe("npm ci && exit"); @@ -143,6 +191,7 @@ describe("eager shell start registry", () => { { shellId: "shell:reused", projectLocation: { kind: "windows", path: "C:\\p" } }, "dev", ); + await vi.waitFor(() => expect(bridge.startShell).toHaveBeenCalledTimes(2)); resolveSecond(); await second; rejectFirst(new Error("old start failed")); @@ -448,14 +497,16 @@ describe("runShellScriptToCompletion", () => { ); expect(sender).toHaveBeenCalledWith({ type: "terminal-watch", id: shellId }); - expect(bridge.startShell).toHaveBeenCalledWith({ - shellId, - projectLocation: { - kind: "posix", - path: "/remote/project", - remoteServerId: desktopId, - }, - }); + await vi.waitFor(() => + expect(bridge.startShell).toHaveBeenCalledWith({ + shellId, + projectLocation: { + kind: "posix", + path: "/remote/project", + remoteServerId: desktopId, + }, + }), + ); handleRemoteTerminalServerMessage(desktopId, { type: "terminal-output", id: shellId, diff --git a/src/renderer/utils/shellUtils.ts b/src/renderer/utils/shellUtils.ts index d26b70dd1..4eefd4d19 100644 --- a/src/renderer/utils/shellUtils.ts +++ b/src/renderer/utils/shellUtils.ts @@ -1,8 +1,9 @@ import { toast } from "@heroui/react"; import { msg } from "@lingui/core/macro"; -import type { ProjectLocation } from "@/shared/contracts"; +import type { ProjectLocation, StartShellPayload } from "@/shared/contracts"; import { readBridge } from "@/renderer/bridge"; import { i18n } from "@/renderer/i18n/i18n"; +import { waitForPendingSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { createRoutedShellSession, disposeRoutedShellSession, @@ -10,12 +11,6 @@ import { export { disposeRoutedShellSession } from "@/renderer/utils/routedShellSession"; -interface StartShellPayload { - shellId: string; - projectLocation: ProjectLocation; - worktreePath?: string; -} - /** * Shell ids started eagerly (outside the terminal panel) — run actions, * command-palette terminal commands, worktree setup shells. The panel's @@ -36,12 +31,16 @@ export function clearEagerShellStart(shellId: string): void { disposeRoutedShellSession(shellId); } +export async function startShellWithCurrentSettings(payload: StartShellPayload): Promise { + await waitForPendingSharedSettings().catch(() => undefined); + await readBridge().startShell(payload); +} + export function startShellWithToast(payload: StartShellPayload, label: string): Promise { const startToken = Symbol(payload.shellId); eagerlyStartedShells.add(payload.shellId); eagerStartTokens.set(payload.shellId, startToken); - return readBridge() - .startShell(payload) + return startShellWithCurrentSettings(payload) .then(() => { if (eagerStartTokens.get(payload.shellId) === startToken) { eagerStartTokens.delete(payload.shellId); @@ -295,9 +294,9 @@ export async function runShellScriptToCompletion( onExited: () => settle(resolve), onWriteError: (error) => settle(() => reject(error)), }); - void readBridge() - .startShell({ shellId, projectLocation }) - .catch((error) => settle(() => reject(error))); + void startShellWithCurrentSettings({ shellId, projectLocation }).catch((error) => + settle(() => reject(error)), + ); }); } diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.test.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.test.tsx index f3e75dbb8..bff6385c8 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.test.tsx +++ b/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.test.tsx @@ -12,7 +12,7 @@ import { DevTerminalPanel } from "./DevTerminalPanel"; const { bridge, remote, layouts, toast } = vi.hoisted(() => ({ bridge: { closeThread: vi.fn<() => Promise>().mockResolvedValue(undefined), - startShell: vi.fn<() => Promise>().mockResolvedValue(undefined), + startShell: vi.fn<(payload: unknown) => Promise>().mockResolvedValue(undefined), }, remote: { watchTerminal: @@ -39,6 +39,11 @@ vi.mock("@/renderer/bridge", () => ({ readBridge: () => bridge, })); +vi.mock("@/renderer/utils/shellUtils", async (importActual) => ({ + ...(await importActual()), + startShellWithCurrentSettings: (payload: unknown) => bridge.startShell(payload), +})); + vi.mock("@/renderer/state/remoteTerminalFeed", () => ({ watchRemoteTerminal: remote.watchTerminal, })); diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.tsx index deba47677..801c03c00 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.tsx +++ b/src/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel.tsx @@ -12,7 +12,11 @@ import { useDevTerminalStore, type DevTerminalTab } from "@/renderer/state/devTe import { watchRemoteTerminal } from "@/renderer/state/remoteTerminalFeed"; import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { closeAllPanels } from "@/renderer/actions/panelActions"; -import { clearEagerShellStart, wasShellStartedEagerly } from "@/renderer/utils/shellUtils"; +import { + clearEagerShellStart, + startShellWithCurrentSettings, + wasShellStartedEagerly, +} from "@/renderer/utils/shellUtils"; import { formatProjectScopeLabel } from "@/renderer/utils/projectScopeLabel"; import type { TerminalSize } from "@/shared/contracts"; import { friendlyError } from "@/shared/messages"; @@ -117,17 +121,15 @@ export function DevTerminalPanel(props: { ? buildWorktreeLocation(project.location, owningTab.worktreePath) : project.location; spawnedRef.current.add(terminalId); - void readBridge() - .startShell({ - shellId: terminalId, - projectLocation: location, - ...(owningTab.worktreePath ? { worktreePath: owningTab.worktreePath } : {}), - initialSize: size, - }) - .catch((error) => { - spawnedRef.current.delete(terminalId); - toast.danger(friendlyError(error)); - }); + void startShellWithCurrentSettings({ + shellId: terminalId, + projectLocation: location, + ...(owningTab.worktreePath ? { worktreePath: owningTab.worktreePath } : {}), + initialSize: size, + }).catch((error) => { + spawnedRef.current.delete(terminalId); + toast.danger(friendlyError(error)); + }); } function handleCloseTab(tab: DevTerminalTab) { diff --git a/src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx b/src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx index 721db4309..8f7e9fa4e 100644 --- a/src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx +++ b/src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx @@ -46,7 +46,7 @@ import { import { ProviderIcon } from "@/renderer/components/providers/ProviderIcon"; import { PixelLoader, SidebarButton } from "@/renderer/components/common"; import { useSidebar } from "@/renderer/views/MainView/parts/AppShell/AppShell"; -import { isDevApp, isRemoteSession } from "@/renderer/bridge"; +import { isDevApp, isRemoteSession, isWindows } from "@/renderer/bridge"; import { searchSettings } from "./settingsSearchIndex"; import type { SettingsSection } from "./types"; @@ -330,7 +330,8 @@ export function SettingsSidebar(props: { ] : group.sections, ); - const settingMatches = query === "" ? [] : searchSettings(query, t, { devMode, remoteSession }); + const settingMatches = + query === "" ? [] : searchSettings(query, t, { devMode, remoteSession, windows: isWindows() }); const matchesBySection = new Map(); for (const match of settingMatches) { const list = matchesBySection.get(match.section) ?? []; diff --git a/src/renderer/views/SettingsOverlay/parts/TerminalSettings.test.tsx b/src/renderer/views/SettingsOverlay/parts/TerminalSettings.test.tsx index 3c3d52e05..288fd47b4 100644 --- a/src/renderer/views/SettingsOverlay/parts/TerminalSettings.test.tsx +++ b/src/renderer/views/SettingsOverlay/parts/TerminalSettings.test.tsx @@ -1,20 +1,49 @@ -import { screen } from "@testing-library/react"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; const bridgeMock = vi.hoisted(() => ({ isRemoteSession: vi.fn<() => boolean>(() => false), + isWindows: vi.fn<() => boolean>(() => true), + getAvailableWindowsShells: vi.fn< + () => Promise> + >(async () => [ + { kind: "pwsh", path: "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe" }, + { + kind: "powershell", + path: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]), })); vi.mock("@/renderer/bridge", () => ({ isRemoteSession: bridgeMock.isRemoteSession, + isWindows: bridgeMock.isWindows, + readBridge: () => ({ getAvailableWindowsShells: bridgeMock.getAvailableWindowsShells }), })); +import { WINDOWS_SHELL_AUTO } from "@/shared/settings"; +import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { TerminalSettings } from "./TerminalSettings"; describe("TerminalSettings", () => { beforeEach(() => { bridgeMock.isRemoteSession.mockReturnValue(false); + bridgeMock.isWindows.mockReturnValue(true); + bridgeMock.getAvailableWindowsShells.mockReset().mockResolvedValue([ + { kind: "pwsh", path: "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe" }, + { + kind: "powershell", + path: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]); + useSharedSettings.setState({ + windowsShellPath: WINDOWS_SHELL_AUTO, + windowsInternalShellPath: WINDOWS_SHELL_AUTO, + windowsShellArguments: "", + }); }); it("shows desktop panel controls in local sessions", () => { @@ -23,6 +52,166 @@ describe("TerminalSettings", () => { expect(screen.getByText("Terminal position")).toBeInTheDocument(); expect(screen.getByText("Auto-show terminal panel")).toBeInTheDocument(); expect(screen.getByText("Browser pick target (CLI threads)")).toBeInTheDocument(); + expect(screen.getByText("Windows shells")).toBeInTheDocument(); + expect(screen.getByText("Windows shells").closest("[data-slot='card']")).toHaveClass( + "card--transparent", + "border-y", + ); + expect(screen.getByText("Terminal panel shell")).toBeInTheDocument(); + expect(screen.getByText("Internal commands and agents")).toBeInTheDocument(); + expect(screen.getByText("Terminal shell arguments")).toBeInTheDocument(); + }); + + it("lists detected Windows shells and persists the selected path and arguments", async () => { + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7")); + fireEvent.click(select); + const powershell = await screen.findByRole("option", { name: /^Windows PowerShell 5\.1/u }); + expect(powershell).toHaveTextContent( + "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + ); + fireEvent.click(powershell); + fireEvent.change(screen.getByLabelText("Terminal shell arguments"), { + target: { value: '-NoProfile -File "C:\\profile scripts\\init.ps1"' }, + }); + + expect(useSharedSettings.getState()).toMatchObject({ + windowsShellPath: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + windowsShellArguments: '-NoProfile -File "C:\\profile scripts\\init.ps1"', + }); + }); + + it("preselects PowerShell 7 without showing an Auto option", async () => { + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7")); + + fireEvent.click(select); + expect( + await screen.findByRole("option", { name: /PowerShell 7 \(recommended\)/u }), + ).toBeInTheDocument(); + expect(screen.queryByRole("option", { name: /^Auto/u })).toBeNull(); + }); + + it("does not mark PowerShell 5.1 as recommended when PowerShell 7 is not available", async () => { + bridgeMock.getAvailableWindowsShells.mockResolvedValueOnce([ + { + kind: "powershell", + path: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]); + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("Windows PowerShell 5.1")); + + fireEvent.click(select); + expect( + await screen.findByRole("option", { name: /^Windows PowerShell 5.1/u }), + ).toBeInTheDocument(); + expect(screen.queryByRole("option", { name: /recommended/iu })).toBeNull(); + }); + + it("keeps an undetected saved override visible so a detected shell can replace it", async () => { + useSharedSettings.setState({ windowsShellPath: "C:\\removed\\pwsh.exe" }); + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("Saved shell")); + fireEvent.click(select); + const saved = await screen.findByRole("option", { name: /^Saved shell/u }); + expect(saved).toHaveTextContent("C:\\removed\\pwsh.exe"); + fireEvent.click(screen.getByRole("option", { name: /PowerShell 7 \(recommended\)/u })); + + expect(useSharedSettings.getState().windowsShellPath).toBe( + "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe", + ); + }); + + it("does not treat a saved detected path as missing before inventory loads", async () => { + let finishDetection!: ( + shells: Array<{ kind: "pwsh" | "powershell" | "cmd"; path: string }>, + ) => void; + bridgeMock.getAvailableWindowsShells.mockReturnValueOnce( + new Promise((resolve) => { + finishDetection = resolve; + }), + ); + useSharedSettings.setState({ + windowsShellPath: "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe", + }); + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + expect(select).not.toHaveTextContent("Saved shell"); + + finishDetection([ + { kind: "pwsh", path: "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe" }, + { + kind: "powershell", + path: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7")); + expect(select).not.toHaveTextContent("Saved shell"); + }); + + it("matches a saved detected path case-insensitively", async () => { + useSharedSettings.setState({ + windowsShellPath: "c:\\program files\\windowsapps\\powershell\\pwsh.exe", + }); + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7")); + expect(select).not.toHaveTextContent("Saved shell"); + }); + + it("lists side-by-side PowerShell versions so the user can pick a specific install", async () => { + bridgeMock.getAvailableWindowsShells.mockResolvedValueOnce([ + { kind: "pwsh", path: "C:\\Program Files\\PowerShell\\7.2\\pwsh.exe", version: "7.2" }, + { kind: "pwsh", path: "C:\\Program Files\\PowerShell\\7.1\\pwsh.exe", version: "7.1" }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]); + render(); + + const select = screen.getByLabelText("Terminal panel shell"); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7.2")); + fireEvent.click(select); + expect( + await screen.findByRole("option", { name: /PowerShell 7\.2 \(recommended\)/u }), + ).toBeInTheDocument(); + const older = await screen.findByRole("option", { name: /^PowerShell 7\.1/u }); + fireEvent.click(older); + + expect(useSharedSettings.getState().windowsShellPath).toBe( + "C:\\Program Files\\PowerShell\\7.1\\pwsh.exe", + ); + }); + + it("persists a separate PowerShell choice for internal commands and agents", async () => { + bridgeMock.getAvailableWindowsShells.mockResolvedValueOnce([ + { kind: "pwsh", path: "C:\\Program Files\\PowerShell\\7.2\\pwsh.exe", version: "7.2" }, + { kind: "pwsh", path: "C:\\Program Files\\PowerShell\\7.1\\pwsh.exe", version: "7.1" }, + { kind: "cmd", path: "C:\\Windows\\System32\\cmd.exe" }, + ]); + render(); + + const select = screen.getByLabelText("Internal commands and agents"); + await waitFor(() => expect(select).toHaveTextContent("PowerShell 7.2")); + fireEvent.click(select); + expect(screen.queryByRole("option", { name: /Command Prompt/u })).toBeNull(); + fireEvent.click(await screen.findByRole("option", { name: /^PowerShell 7\.1/u })); + + expect(useSharedSettings.getState()).toMatchObject({ + windowsShellPath: WINDOWS_SHELL_AUTO, + windowsInternalShellPath: "C:\\Program Files\\PowerShell\\7.1\\pwsh.exe", + }); }); it("hides desktop panel controls in remote sessions", () => { @@ -33,6 +222,10 @@ describe("TerminalSettings", () => { expect(screen.queryByText("Terminal position")).not.toBeInTheDocument(); expect(screen.queryByText("Auto-show terminal panel")).not.toBeInTheDocument(); expect(screen.queryByText("Browser pick target (CLI threads)")).not.toBeInTheDocument(); + expect(screen.queryByText("Windows shells")).not.toBeInTheDocument(); + expect(screen.queryByText("Terminal panel shell")).not.toBeInTheDocument(); + expect(screen.queryByText("Internal commands and agents")).not.toBeInTheDocument(); + expect(screen.queryByText("Terminal shell arguments")).not.toBeInTheDocument(); expect(screen.getByText("Collapse terminal composer")).toBeInTheDocument(); expect(screen.getByText("Agent terminal font size")).toBeInTheDocument(); expect(screen.getByText("Terminal panel font size")).toBeInTheDocument(); diff --git a/src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx b/src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx index b7cd930c0..f7abfdaa3 100644 --- a/src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx +++ b/src/renderer/views/SettingsOverlay/parts/TerminalSettings.tsx @@ -1,10 +1,23 @@ -import { startTransition } from "react"; +import { startTransition, useEffect, useState } from "react"; import { Trans, useLingui } from "@lingui/react/macro"; import type { TerminalPosition } from "@/shared/contracts"; -import type { CliPickerTarget } from "@/shared/settings"; -import { isRemoteSession } from "@/renderer/bridge"; +import { + WINDOWS_SHELL_AUTO, + type AvailableWindowsShell, + type CliPickerTarget, +} from "@/shared/settings"; +import { isRemoteSession, isWindows, readBridge } from "@/renderer/bridge"; import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; -import { Select, ToggleSwitch } from "@/renderer/components/common"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Input, + Select, + ToggleSwitch, +} from "@/renderer/components/common"; import { SettingRow, SettingsPage } from "./SettingsForm"; import { cliPickerTargetOptions, @@ -18,6 +31,14 @@ export function TerminalSettings() { const { t } = useLingui(); const terminalPosition = useSharedSettings((state) => state.terminalPosition); const setTerminalPosition = useSharedSettings((state) => state.setTerminalPosition); + const windowsShellPath = useSharedSettings((state) => state.windowsShellPath); + const setWindowsShellPath = useSharedSettings((state) => state.setWindowsShellPath); + const windowsInternalShellPath = useSharedSettings((state) => state.windowsInternalShellPath); + const setWindowsInternalShellPath = useSharedSettings( + (state) => state.setWindowsInternalShellPath, + ); + const windowsShellArguments = useSharedSettings((state) => state.windowsShellArguments); + const setWindowsShellArguments = useSharedSettings((state) => state.setWindowsShellArguments); const collapseTerminalComposer = useSharedSettings((state) => state.collapseTerminalComposer); const setCollapseTerminalComposer = useSharedSettings( (state) => state.setCollapseTerminalComposer, @@ -36,6 +57,69 @@ export function TerminalSettings() { const terminalPositionOpts = useLocalizedOptions(terminalPositionOptions); const cliPickerTargetOpts = useLocalizedOptions(cliPickerTargetOptions); const remote = isRemoteSession(); + const windows = !remote && isWindows(); + const [availableWindowsShells, setAvailableWindowsShells] = useState< + AvailableWindowsShell[] | null + >(null); + + useEffect(() => { + if (!windows) return; + let cancelled = false; + void readBridge() + .getAvailableWindowsShells() + .then((shells) => { + if (!cancelled) setAvailableWindowsShells(shells); + }) + .catch(() => { + if (!cancelled) setAvailableWindowsShells([]); + }); + return () => { + cancelled = true; + }; + }, [windows]); + + const shellOptionLabel = (shell: AvailableWindowsShell, recommended: boolean) => { + switch (shell.kind) { + case "pwsh": { + const version = shell.version ?? "7"; + return recommended ? t`PowerShell ${version} (recommended)` : t`PowerShell ${version}`; + } + case "powershell": + return t`Windows PowerShell 5.1`; + case "cmd": + return t`Command Prompt`; + } + }; + const detectedWindowsShells = availableWindowsShells ?? []; + const shellsReady = availableWindowsShells !== null; + const buildShellControl = (configuredPath: string, shells: AvailableWindowsShell[]) => { + const selectedShell = shells.find( + (shell) => shell.path.toLowerCase() === configuredPath.toLowerCase(), + ); + const savedOption = + shellsReady && configuredPath !== WINDOWS_SHELL_AUTO && !selectedShell + ? { id: configuredPath, label: t`Saved shell`, detail: configuredPath } + : undefined; + return { + options: [ + ...shells.map((shell, index) => ({ + id: shell.path, + label: shellOptionLabel(shell, index === 0 && shell.kind === "pwsh"), + detail: shell.path, + })), + ...(savedOption ? [savedOption] : []), + ], + value: + selectedShell?.path ?? + (configuredPath === WINDOWS_SHELL_AUTO ? shells[0]?.path : configuredPath) ?? + null, + }; + }; + const terminalShellControl = buildShellControl(windowsShellPath, detectedWindowsShells); + const internalShellControl = buildShellControl( + windowsInternalShellPath, + detectedWindowsShells.filter((shell) => shell.kind !== "cmd"), + ); return ( @@ -59,6 +143,76 @@ export function TerminalSettings() { )} + {windows && ( + + + + {t`Windows shells`} + + + Choose shells for the Terminal panel and Poracode's internal commands. + + + + Used for new interactive Terminal-panel sessions.} + > + + + + + Additional arguments passed to each new Terminal-panel shell. Quote values + containing spaces. + + } + > + setWindowsShellArguments(event.currentTarget.value)} + /> + + + + )} + {!remote && ( { ).not.toContain("browser.allowDataAccess"); }); + it("hides Windows-only settings on other desktop platforms", () => { + const nonWindows = searchSettings("windows terminal shell", t, { windows: false }).map( + (result) => result.anchor, + ); + const windows = searchSettings("windows terminal shell", t, { windows: true }).map( + (result) => result.anchor, + ); + + expect(nonWindows).not.toContain("terminal.windowsShell"); + expect(nonWindows).not.toContain("terminal.windowsInternalShell"); + expect(nonWindows).not.toContain("terminal.windowsShellArguments"); + expect(windows).toContain("terminal.windowsShell"); + expect(windows).toContain("terminal.windowsInternalShell"); + expect(windows).toContain("terminal.windowsShellArguments"); + expect(searchSettings("powershell", t).map((result) => result.anchor)).not.toContain( + "terminal.windowsShell", + ); + }); + it("truncates long description snippets", () => { const results = searchSettings("subscription plans", t); const hit = results.find((r) => r.anchor === "usage.showEstimatedCost"); diff --git a/src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts b/src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts index 558c9949d..318eacbbc 100644 --- a/src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts +++ b/src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts @@ -34,6 +34,8 @@ export interface SettingsSearchEntry { devOnly?: boolean; /** Only rendered in the desktop app, not remote/PWA sessions. */ desktopOnly?: boolean; + /** Renders only in the native Windows desktop app. */ + windowsOnly?: boolean; /** Renders only under a runtime/platform condition; the drift test skips it. */ conditional?: boolean; } @@ -220,6 +222,37 @@ export const SETTINGS_SEARCH_INDEX: readonly SettingsSearchEntry[] = [ keywords: "auto open reveal terminal panel commands worktree automatic", desktopOnly: true, }, + { + section: "terminal", + anchor: "terminal.windowsShell", + title: msg`Terminal panel shell`, + description: msg`Used for new interactive Terminal-panel sessions.`, + keywords: "windows terminal shell pwsh powershell command prompt cmd version executable", + desktopOnly: true, + windowsOnly: true, + conditional: true, + }, + { + section: "terminal", + anchor: "terminal.windowsInternalShell", + title: msg`Internal commands and agents`, + description: msg`Used for agents, authentication, installs, and Poracode's internal commands.`, + keywords: + "windows terminal shell internal commands agents authentication install pwsh powershell executable", + desktopOnly: true, + windowsOnly: true, + conditional: true, + }, + { + section: "terminal", + anchor: "terminal.windowsShellArguments", + title: msg`Terminal shell arguments`, + description: msg`Additional arguments passed to each new Terminal-panel shell. Quote values containing spaces.`, + keywords: "windows terminal shell arguments argv flags options profile logo", + desktopOnly: true, + windowsOnly: true, + conditional: true, + }, { section: "terminal", anchor: "terminal.collapseTerminalComposer", @@ -587,7 +620,12 @@ function truncate(text: string, max = SNIPPET_MAX): string { export function searchSettings( query: string, t: Translate, - opts?: { devMode?: boolean; remoteSession?: boolean; index?: readonly SettingsSearchEntry[] }, + opts?: { + devMode?: boolean; + remoteSession?: boolean; + windows?: boolean; + index?: readonly SettingsSearchEntry[]; + }, ): SettingsSearchResult[] { const needle = query.trim().toLowerCase(); if (needle === "") return []; @@ -596,6 +634,7 @@ export function searchSettings( for (const entry of index) { if (entry.devOnly && !opts?.devMode) continue; if (entry.desktopOnly && opts?.remoteSession) continue; + if (entry.windowsOnly && !opts?.windows) continue; const title = t(entry.title); const description = entry.description ? t(entry.description) : ""; const titleMatch = title.toLowerCase().includes(needle); diff --git a/src/shared/contracts/thread.ts b/src/shared/contracts/thread.ts index 4f2f39e1b..e2c9fce6b 100644 --- a/src/shared/contracts/thread.ts +++ b/src/shared/contracts/thread.ts @@ -399,5 +399,11 @@ export const startShellPayloadSchema = z.object({ * ephemeral) worktree. */ startInHome: z.boolean().optional(), + /** + * Native Windows shells default to the user's interactive preference. + * Login/install overlays emit PowerShell, so they request a PowerShell host + * even when the preferred interactive shell is cmd. + */ + windowsShellRuntime: z.enum(["preferred", "powershell"]).optional(), }); export type StartShellPayload = z.infer; diff --git a/src/shared/ipc/procedures/thread.ts b/src/shared/ipc/procedures/thread.ts index 5f56c5ccc..fc7cd1182 100644 --- a/src/shared/ipc/procedures/thread.ts +++ b/src/shared/ipc/procedures/thread.ts @@ -69,6 +69,7 @@ import type { WriteTerminalPayload, } from "../../contracts"; import type { CrossagentRoutingState } from "../../crossagentRanking"; +import type { AvailableWindowsShell } from "../../settings"; import { defineIpcProcedure, defineNoArgProcedure, definePayloadProcedure } from "../core"; import { readThreadPayloadSchema, @@ -179,6 +180,10 @@ export const threadProcedures = { "getTerminalShellSnapshots", "supervisor", ), + getAvailableWindowsShells: defineNoArgProcedure( + "getAvailableWindowsShells", + "supervisor", + ), startThread: definePayloadProcedure( "startThread", "supervisor", diff --git a/src/shared/settings.test.ts b/src/shared/settings.test.ts index 4c8bfc491..e52609128 100644 --- a/src/shared/settings.test.ts +++ b/src/shared/settings.test.ts @@ -25,6 +25,16 @@ describe("shared settings defaults", () => { expect(normalizeSharedSettings({}).preventSleep).toBe("while-remote-access"); }); + it("adds automatic Windows shell settings to a previous settings shape", () => { + const normalized = normalizeSharedSettings({ terminalPosition: "right" }); + expect(normalized).toMatchObject({ + terminalPosition: "right", + windowsShellPath: "auto", + windowsInternalShellPath: "auto", + windowsShellArguments: "", + }); + }); + it("migrates legacy sleep booleans into preventSleep", () => { expect( normalizeSharedSettings({ diff --git a/src/shared/settings.ts b/src/shared/settings.ts index 25f319655..018217286 100644 --- a/src/shared/settings.ts +++ b/src/shared/settings.ts @@ -26,6 +26,17 @@ import { DEFAULT_SEARCH_EXCLUDE } from "./searchExclude"; import { AI_LANGUAGE_VALUES, LOCALE_SETTING_VALUES } from "./locale"; import { QWEN_DEFAULT_MODEL_ID, QWEN_RETIRED_PREVIEW_MODEL_ID } from "./agents/qwenModels"; +export const WINDOWS_SHELL_AUTO = "auto"; +export const WINDOWS_SHELL_ARGUMENTS_MAX = 8_192; +export type WindowsShellKind = "pwsh" | "powershell" | "cmd"; + +export interface AvailableWindowsShell { + path: string; + kind: WindowsShellKind; + /** Product/folder version when known, e.g. "7.2" or "7.6.1". */ + version?: string; +} + const modelPickerEntrySchema = z.object({ agentKind: z.string().min(1), modelId: z.string().min(1), @@ -246,6 +257,12 @@ export const sharedSettingsSchema = z.object({ */ gitTextLanguage: z.enum(AI_LANGUAGE_VALUES).default("en"), terminalPosition: terminalPositionSchema, + /** Absolute detected executable path, or "auto" for preferred-shell detection. */ + windowsShellPath: z.string(), + /** PowerShell host used for internal commands and agent launch wrappers. */ + windowsInternalShellPath: z.string(), + /** Additional argv passed directly to each interactive Windows shell. */ + windowsShellArguments: z.string().max(WINDOWS_SHELL_ARGUMENTS_MAX), commitGenProvider: z.string(), commitGenModel: z.string(), commitGenEffort: z.string(), @@ -599,6 +616,9 @@ export const defaultSharedSettings: SharedSettings = { locale: "system", gitTextLanguage: "en", terminalPosition: "bottom", + windowsShellPath: WINDOWS_SHELL_AUTO, + windowsInternalShellPath: WINDOWS_SHELL_AUTO, + windowsShellArguments: "", commitGenProvider: "auto", commitGenModel: "", commitGenEffort: "", diff --git a/src/supervisor/agents/base/index.ts b/src/supervisor/agents/base/index.ts index 880bda0bf..dbe94704b 100644 --- a/src/supervisor/agents/base/index.ts +++ b/src/supervisor/agents/base/index.ts @@ -31,6 +31,7 @@ import { quotePosixShellArg, quotePowerShellLiteral, } from "./shellBasics"; +import { detectPowerShell, type DetectedPowerShell } from "../../shellPreference"; import type { AgentArgvSpec, AgentEnvContext, @@ -92,6 +93,7 @@ export * from "./expectedRuntimeError"; export * from "./promptSession"; export * from "./processRuntime"; export * from "./shellBasics"; +export type { DetectedPowerShell } from "../../shellPreference"; export * from "./sessionFs"; export function buildWindowsCmdCommand(cwd: string, command: string, args: string[]): CommandSpec { return { @@ -170,12 +172,6 @@ function isWindowsDirectExecutable(command: string): boolean { return /^(?:[a-zA-Z]:[\\/]|\\\\)/.test(command) && /\.(?:exe|com)$/i.test(command); } -/** A resolved Windows PowerShell host: modern pwsh 7+ or legacy 5.1. */ -export interface DetectedPowerShell { - path: string; - kind: "pwsh" | "powershell"; -} - /** * Detect the best available PowerShell host, preserving which one matched * (pwsh > powershell). Callers fall back to the platform shell when none resolves. @@ -183,12 +179,7 @@ export interface DetectedPowerShell { export function detectShell( resolvePath: ResolveExecutablePath = resolveExecutablePath, ): DetectedPowerShell | undefined { - if (process.platform !== "win32") return undefined; - const pwsh = resolvePath("pwsh.exe") ?? resolvePath("pwsh"); - if (pwsh) return { path: pwsh, kind: "pwsh" }; - const powershell = resolvePath("powershell.exe") ?? resolvePath("powershell"); - if (powershell) return { path: powershell, kind: "powershell" }; - return undefined; + return detectPowerShell(resolvePath); } /** diff --git a/src/supervisor/agents/base/processRuntime.ts b/src/supervisor/agents/base/processRuntime.ts index ff08fc40d..afdbe87c3 100644 --- a/src/supervisor/agents/base/processRuntime.ts +++ b/src/supervisor/agents/base/processRuntime.ts @@ -12,6 +12,7 @@ import { quotePosixShellArg, } from "./shellBasics"; import type { WslBridgeClient, WslLocation, WslProcessExecResult } from "../../wsl/bridge/client"; +import { detectPowerShell, isWindowsAppExecutionAlias } from "../../shellPreference"; const execFileAsync = promisify(execFile); @@ -284,8 +285,11 @@ function parseWindowsExecutablePath(stdout: string): string | undefined { .split(/\r?\n/g) .map((line) => line.trim()) .filter((line) => line.length > 0); + const executables = lines.filter((line) => /\.(?:bat|cmd|com|exe|ps1)$/i.test(line)); const resolved = - lines.findLast((line) => /\.(?:bat|cmd|com|exe|ps1)$/i.test(line)) ?? lines.at(-1); + executables.findLast((line) => !isWindowsAppExecutionAlias(line)) ?? + executables.at(-1) ?? + lines.at(-1); return ( resolveWindowsScoopShimTarget(resolved) ?? resolveWindowsCmdExeTarget(resolved) ?? resolved ); @@ -540,11 +544,11 @@ function encodePowerShellCommand(script: string): string { } function resolveWindowsProfileShellPath(): string { + const resolveProfileShell = (name: string) => + resolveWindowsExecutablePath(name) ?? + resolveWindowsExecutablePath(name, buildWindowsPathOverride()); return ( - resolveWindowsExecutablePath("pwsh.exe") ?? - resolveWindowsExecutablePath("pwsh", buildWindowsPathOverride()) ?? - resolveWindowsExecutablePath("powershell.exe") ?? - resolveWindowsExecutablePath("powershell", buildWindowsPathOverride()) ?? + detectPowerShell(resolveProfileShell)?.path ?? getWindowsSystemCommand("WindowsPowerShell\\v1.0\\powershell.exe") ); } diff --git a/src/supervisor/agents/plugin/installerBase.test.ts b/src/supervisor/agents/plugin/installerBase.test.ts index 20fc50753..fb1c78f1d 100644 --- a/src/supervisor/agents/plugin/installerBase.test.ts +++ b/src/supervisor/agents/plugin/installerBase.test.ts @@ -345,7 +345,7 @@ describe("buildNativeHookCommandHead", () => { ); const expected = process.platform === "win32" - ? 'pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"' + ? '"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"' : "'C:\\Users\\u\\poracode-hook.cmd'"; expect(commandHead).toBe(expected); }); @@ -358,7 +358,18 @@ describe("buildNativeHookCommandHead", () => { ); const expected = process.platform === "win32" - ? 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"' + ? '"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"' + : "'C:\\Users\\u\\poracode-hook.cmd'"; + expect(commandHead).toBe(expected); + }); + + it("preserves an extensionless resolved PowerShell command", () => { + const commandHead = buildNativeHookCommandHead("C:\\Users\\u\\poracode-hook.cmd", (name) => + name === "pwsh" ? "C:\\Tools\\pwsh" : undefined, + ); + const expected = + process.platform === "win32" + ? '"C:\\Tools\\pwsh" -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"' : "'C:\\Users\\u\\poracode-hook.cmd'"; expect(commandHead).toBe(expected); }); @@ -378,7 +389,7 @@ describe("buildNativeHookCommandHead", () => { ); const expected = process.platform === "win32" - ? 'pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\a\\"b\\poracode-hook.ps1"' + ? '"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\a\\"b\\poracode-hook.ps1"' : "'C:\\Users\\a\"b\\poracode-hook.cmd'"; expect(commandHead).toBe(expected); }); @@ -411,7 +422,7 @@ describe("buildNativeHookCommandHeads", () => { process.platform === "win32" ? { command: - 'pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"', + '"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -ExecutionPolicy Bypass -File "C:\\Users\\u\\poracode-hook.ps1"', bashCommand: "'C:\\Users\\u\\poracode-hook.cmd'", powershellCommand: "& 'C:\\Users\\u\\poracode-hook.ps1'", } diff --git a/src/supervisor/agents/plugin/installerBase.ts b/src/supervisor/agents/plugin/installerBase.ts index 7a337516a..0a96de2c8 100644 --- a/src/supervisor/agents/plugin/installerBase.ts +++ b/src/supervisor/agents/plugin/installerBase.ts @@ -17,6 +17,7 @@ import { import { dirname, join, resolve } from "node:path"; import { resolvePoracodePaths } from "@/shared/poracodePaths"; import { toWslUncPath } from "@/shared/wsl"; +import { detectPowerShell } from "../../shellPreference"; import { getCachedWslHomeDirectory, resolveExecutablePath, @@ -184,11 +185,8 @@ export function buildNativeHookCommandHeads( if (process.platform === "win32") { const ps1Path = join(dirname(wrapperPath), getNativeHookPowerShellWrapperFilename()); const powershellCommand = `& ${quotePowerShellSingleQuoted(ps1Path)}`; - const shell = - (resolvePath("pwsh.exe") && "pwsh.exe") || - (resolvePath("pwsh") && "pwsh") || - (resolvePath("powershell.exe") && "powershell.exe") || - (resolvePath("powershell") && "powershell"); + const detectedShell = detectPowerShell(resolvePath); + const shell = detectedShell && quoteHookCommandArg(detectedShell.path, "native"); if (shell) { return { command: `${shell} -NoProfile -ExecutionPolicy Bypass -File ${quoteHookCommandArg(ps1Path, "native")}`, diff --git a/src/supervisor/ipcHandlers.ts b/src/supervisor/ipcHandlers.ts index 33d08990f..616ad2279 100644 --- a/src/supervisor/ipcHandlers.ts +++ b/src/supervisor/ipcHandlers.ts @@ -52,6 +52,7 @@ export function createSupervisorIpcHandlers(runtime: SupervisorRuntime): Supervi logoutAcpAgent: (payload) => registry.logoutAcpAgent(payload), getThreadSnapshots: () => threads.getThreadSnapshots(), getTerminalShellSnapshots: () => threads.getTerminalShellSnapshots(), + getAvailableWindowsShells: () => runtime.getAvailableWindowsShells(), startThread: (payload) => threads.startThread(payload), sendThreadInput: (payload) => threads.sendThreadInput(payload), interruptThread: (payload) => threads.interruptThread(payload), diff --git a/src/supervisor/runtime/threadSession/managerOptions.ts b/src/supervisor/runtime/threadSession/managerOptions.ts index 20226ef02..3d30fa740 100644 --- a/src/supervisor/runtime/threadSession/managerOptions.ts +++ b/src/supervisor/runtime/threadSession/managerOptions.ts @@ -21,7 +21,7 @@ export interface ThreadSessionManagerOptions { settingsPath: string; readDisableCliHookPlugin(): boolean; adapters: Map; - windowsShell: WindowsShellPreference; + resolveWindowsShell(runtime?: "preferred" | "powershell"): WindowsShellPreference; /** * Optional: provides CLI hook plugin ingress env vars + extra CLI args injected * into every agent PTY spawn. The supervisor boots a single diff --git a/src/supervisor/runtime/threadSessionManager.restartTerminal.test.ts b/src/supervisor/runtime/threadSessionManager.restartTerminal.test.ts index 75e4a9e99..606c2a7f2 100644 --- a/src/supervisor/runtime/threadSessionManager.restartTerminal.test.ts +++ b/src/supervisor/runtime/threadSessionManager.restartTerminal.test.ts @@ -100,7 +100,11 @@ function createManager( settingsPath: join(tempDir, "settings.json"), readDisableCliHookPlugin: () => false, adapters: new Map([[AGENT_KIND, adapter]]), - windowsShell: { shell: "powershell.exe", kind: "powershell", args: ["-NoLogo"] }, + resolveWindowsShell: () => ({ + shell: "powershell.exe", + kind: "powershell", + args: ["-NoLogo"], + }), ...extraOptions, }); managersToDispose.push(manager); diff --git a/src/supervisor/runtime/threadSessionManager.stageInput.test.ts b/src/supervisor/runtime/threadSessionManager.stageInput.test.ts index 2c7b8bbef..8b245d250 100644 --- a/src/supervisor/runtime/threadSessionManager.stageInput.test.ts +++ b/src/supervisor/runtime/threadSessionManager.stageInput.test.ts @@ -78,7 +78,11 @@ function createManager(adapter: AgentAdapter): ThreadSessionManager { settingsPath: join(tempDir, "settings.json"), readDisableCliHookPlugin: () => false, adapters: new Map([[AGENT_KIND, adapter]]), - windowsShell: { shell: "powershell.exe", kind: "powershell", args: ["-NoLogo"] }, + resolveWindowsShell: () => ({ + shell: "powershell.exe", + kind: "powershell", + args: ["-NoLogo"], + }), }); managersToDispose.push(manager); return manager; diff --git a/src/supervisor/runtime/threadSessionManager.staleInterrupt.test.ts b/src/supervisor/runtime/threadSessionManager.staleInterrupt.test.ts index 53cd03bf0..204125107 100644 --- a/src/supervisor/runtime/threadSessionManager.staleInterrupt.test.ts +++ b/src/supervisor/runtime/threadSessionManager.staleInterrupt.test.ts @@ -129,7 +129,11 @@ function createManager(adapter: AgentAdapter): { settingsPath: join(tempDir, "settings.json"), readDisableCliHookPlugin: () => false, adapters: new Map([[AGENT_KIND, adapter]]), - windowsShell: { shell: "powershell.exe", kind: "powershell", args: ["-NoLogo"] }, + resolveWindowsShell: () => ({ + shell: "powershell.exe", + kind: "powershell", + args: ["-NoLogo"], + }), }); managersToDispose.push(manager); return { manager, events }; diff --git a/src/supervisor/runtime/threadSessionManager.startClose.test.ts b/src/supervisor/runtime/threadSessionManager.startClose.test.ts index f1a81dc91..efe712a16 100644 --- a/src/supervisor/runtime/threadSessionManager.startClose.test.ts +++ b/src/supervisor/runtime/threadSessionManager.startClose.test.ts @@ -1,10 +1,11 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { AgentKind } from "@/shared/contracts"; import type { SupervisorEvent } from "@/shared/ipc"; import type { AgentAdapter, StructuredSessionHandle } from "../agents/base"; +import type { WindowsShellPreference } from "../shellPreference"; import type { SessionRuntime } from "./sessionTypes"; const captureSupervisorException = vi.hoisted(() => @@ -20,6 +21,7 @@ vi.mock("../agents/base", async (importActual) => { const actual = await importActual(); return { ...actual, + getRefreshedWindowsPath: vi.fn<() => string | undefined>(() => undefined), primeProjectShellEnv: vi.fn<(cwd: string) => Promise | undefined>>(() => Promise.resolve(undefined), ), @@ -37,6 +39,7 @@ vi.mock("node:timers/promises", async (importActual) => { }); import { ThreadSessionManager } from "./threadSessionManager"; +import { spawn as spawnPty } from "node-pty"; vi.mock("node-pty", () => ({ spawn: vi.fn< @@ -74,6 +77,11 @@ function createManager( agentKind: AgentKind, adapter: AgentAdapter, emit: (event: SupervisorEvent) => void = vi.fn<(event: SupervisorEvent) => void>(), + resolveWindowsShell: () => WindowsShellPreference = () => ({ + shell: "powershell.exe", + kind: "powershell" as const, + args: ["-NoLogo"], + }), ): ThreadSessionManager { const tempDir = mkdtempSync(join(tmpdir(), "poracode-start-close-")); tempDirs.push(tempDir); @@ -84,7 +92,7 @@ function createManager( settingsPath: join(tempDir, "settings.json"), readDisableCliHookPlugin: () => false, adapters: new Map([[agentKind, adapter]]), - windowsShell: { shell: "powershell.exe", kind: "powershell", args: ["-NoLogo"] }, + resolveWindowsShell, }); managersToDispose.push(manager); return manager; @@ -205,6 +213,68 @@ describe("ThreadSessionManager provider-session routing", () => { }); }); +describe("ThreadSessionManager Windows shells", () => { + const originalPlatform = process.platform; + + beforeEach(() => { + Object.defineProperty(process, "platform", { value: "win32", configurable: true }); + vi.mocked(spawnPty).mockClear(); + }); + + afterEach(() => { + Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true }); + }); + + it("resolves the current shell preference for every shell launch", async () => { + const structuredSession = createStructuredSession(Promise.resolve()); + const adapter = createAdapter("codex", structuredSession); + const resolveWindowsShell = vi.fn<() => WindowsShellPreference>(() => ({ + shell: "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe", + kind: "pwsh" as const, + args: ["-NoLogo", "-NoProfile"], + })); + const manager = createManager("codex", adapter, undefined, resolveWindowsShell); + + await manager.startShell({ + shellId: "shell:preferred", + projectLocation: { kind: "windows", path: process.cwd() }, + }); + + expect(resolveWindowsShell).toHaveBeenCalledWith("preferred"); + expect(spawnPty).toHaveBeenCalledWith( + "C:\\Program Files\\WindowsApps\\PowerShell\\pwsh.exe", + ["-NoLogo", "-NoProfile"], + expect.objectContaining({ cwd: process.cwd() }), + ); + }); + + it("requests a PowerShell host for login and install overlays", async () => { + const structuredSession = createStructuredSession(Promise.resolve()); + const adapter = createAdapter("codex", structuredSession); + const resolveWindowsShell = vi.fn< + (runtime?: "preferred" | "powershell") => WindowsShellPreference + >(() => ({ + shell: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + kind: "powershell" as const, + args: ["-NoLogo"], + })); + const manager = createManager("codex", adapter, undefined, resolveWindowsShell); + + await manager.startShell({ + shellId: "login:preferred", + projectLocation: { kind: "windows", path: process.cwd() }, + windowsShellRuntime: "powershell", + }); + + expect(resolveWindowsShell).toHaveBeenCalledWith("powershell"); + expect(spawnPty).toHaveBeenCalledWith( + "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + ["-NoLogo"], + expect.objectContaining({ cwd: process.cwd() }), + ); + }); +}); + describe("ThreadSessionManager start guards", () => { it("waits for a reconnect before delivering input to the new live session", async () => { const activation = deferred(); diff --git a/src/supervisor/runtime/threadSessionManager.subAgentBuffer.test.ts b/src/supervisor/runtime/threadSessionManager.subAgentBuffer.test.ts index 1f3de0085..d61c2606c 100644 --- a/src/supervisor/runtime/threadSessionManager.subAgentBuffer.test.ts +++ b/src/supervisor/runtime/threadSessionManager.subAgentBuffer.test.ts @@ -20,7 +20,7 @@ function makeManager() { settingsPath: "", readDisableCliHookPlugin: () => false, adapters: new Map(), - windowsShell: { shell: "cmd", kind: "cmd", args: [] }, + resolveWindowsShell: () => ({ shell: "cmd", kind: "cmd", args: [] }), }); return { manager, emit }; } diff --git a/src/supervisor/runtime/threadSessionManager.ts b/src/supervisor/runtime/threadSessionManager.ts index 417cfdab4..e0903b0b7 100644 --- a/src/supervisor/runtime/threadSessionManager.ts +++ b/src/supervisor/runtime/threadSessionManager.ts @@ -971,7 +971,13 @@ export class ThreadSessionManager { await primeProjectShellEnv(payload.projectLocation.path); } - const shellCommand = buildShellCommand(payload.projectLocation, this.options.windowsShell, { + const windowsShell = + process.platform === "win32" && payload.projectLocation.kind === "windows" + ? this.options.resolveWindowsShell( + payload.windowsShellRuntime === "powershell" ? "powershell" : "preferred", + ) + : { shell: "", kind: "cmd" as const, args: [] }; + const shellCommand = buildShellCommand(payload.projectLocation, windowsShell, { startInHome: payload.startInHome === true, }); this.options.emit({ type: "thread-reset", threadId: payload.shellId }); diff --git a/src/supervisor/shellPreference.test.ts b/src/supervisor/shellPreference.test.ts new file mode 100644 index 000000000..9ff380a6d --- /dev/null +++ b/src/supervisor/shellPreference.test.ts @@ -0,0 +1,283 @@ +import { existsSync } from "node:fs"; +import { homedir } from "node:os"; +import { win32 } from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { WINDOWS_SHELL_AUTO } from "@/shared/settings"; +import { + detectPowerShell, + detectWindowsShells, + inferPwshVersion, + inferWindowsShellKind, + isWindowsAppExecutionAlias, + parseAppxInstallLocations, + parsePwshVersion, + parseWindowsShellArguments, + selectWindowsPowerShell, + selectWindowsShell, + setWindowsPowerShellPreferenceResolver, + windowsPwshWellKnownPaths, +} from "./shellPreference"; + +describe("Windows shell preference", () => { + const originalPlatform = process.platform; + + beforeEach(() => { + Object.defineProperty(process, "platform", { value: "win32", configurable: true }); + }); + + afterEach(() => { + Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true }); + }); + + const storeAlias = "C:\\Users\\a\\AppData\\Local\\Microsoft\\WindowsApps\\pwsh.exe"; + const msixImage = + "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.6.1.0_x64__8wekyb3d8bbwe\\pwsh.exe"; + const msiImage = "C:\\Program Files\\PowerShell\\7\\pwsh.exe"; + const scoopImage = "C:\\Users\\a\\scoop\\apps\\pwsh\\current\\pwsh.exe"; + + it("detects MSIX package images in automatic preference order", () => { + const shells = detectWindowsShells( + (name) => { + if (name === "pwsh.exe") return msixImage; + if (name === "powershell.exe") { + return "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"; + } + if (name === "cmd.exe") return "C:\\Windows\\System32\\cmd.exe"; + return undefined; + }, + () => false, + ); + + expect(shells.map((shell) => shell.kind)).toEqual(["pwsh", "powershell", "cmd"]); + expect(shells[0]?.path).toBe(msixImage); + }); + + it("replaces a Windows Store execution alias with a launchable MSI or MSIX image", () => { + const fromMsi = detectWindowsShells( + (name) => (name === "pwsh.exe" ? storeAlias : undefined), + (path) => path === msiImage, + { extraPwshPaths: [msiImage, storeAlias], resolvePwshVersion: () => undefined }, + ); + expect(fromMsi[0]).toEqual({ kind: "pwsh", path: msiImage, version: "7" }); + + const fromMsix = detectWindowsShells( + (name) => (name === "pwsh.exe" ? storeAlias : undefined), + (path) => path === msixImage, + { extraPwshPaths: [msixImage] }, + ); + expect(fromMsix[0]).toEqual({ kind: "pwsh", path: msixImage, version: "7.6.1" }); + }); + + it("finds installer-layout pwsh when PATH has no hit", () => { + const shells = detectWindowsShells( + () => undefined, + (path) => path === scoopImage, + { + extraPwshPaths: [scoopImage], + }, + ); + expect(shells[0]).toEqual({ kind: "pwsh", path: scoopImage }); + }); + + it("lists every launchable pwsh location so side-by-side versions can be chosen", () => { + const v71 = "C:\\Program Files\\PowerShell\\7.1\\pwsh.exe"; + const v72 = "C:\\Program Files\\PowerShell\\7.2\\pwsh.exe"; + const shells = detectWindowsShells( + (name) => (name === "pwsh.exe" ? v72 : undefined), + (path) => path === v71 || path === v72, + { extraPwshPaths: [v71, v72] }, + ); + expect(shells.filter((shell) => shell.kind === "pwsh")).toEqual([ + { kind: "pwsh", path: v72, version: "7.2" }, + { kind: "pwsh", path: v71, version: "7.1" }, + ]); + }); + + it("probes the product version when the install folder only identifies PowerShell 7", () => { + const resolvePwshVersion = vi.fn<(path: string) => string>(() => "7.6.5"); + const shells = detectWindowsShells( + (name) => (name === "pwsh.exe" ? msiImage : undefined), + (path) => path === msiImage, + { extraPwshPaths: [msiImage], resolvePwshVersion }, + ); + + expect(shells[0]).toEqual({ kind: "pwsh", path: msiImage, version: "7.6.5" }); + expect(resolvePwshVersion).toHaveBeenCalledWith(msiImage); + }); + + it("probes Preview MSIX images instead of presenting their package version as stable", () => { + const previewImage = + "C:\\Program Files\\WindowsApps\\Microsoft.PowerShellPreview_7.6.0.0_x64__8wekyb3d8bbwe\\pwsh.exe"; + const resolvePwshVersion = vi.fn<(path: string) => string>(() => "7.6.0-preview.3"); + const shells = detectWindowsShells( + (name) => (name === "pwsh.exe" ? previewImage : undefined), + (path) => path === previewImage, + { extraPwshPaths: [previewImage], resolvePwshVersion }, + ); + + expect(shells[0]).toEqual({ + kind: "pwsh", + path: previewImage, + version: "7.6.0-preview.3", + }); + expect(resolvePwshVersion).toHaveBeenCalledWith(previewImage); + }); + + it("parses stable and preview PowerShell version output", () => { + expect(parsePwshVersion("7.6.5\r\n")).toBe("7.6.5"); + expect(parsePwshVersion("warning\n7.7.0-preview.3\n")).toBe("7.7.0-preview.3"); + expect(parsePwshVersion("warning only")).toBeUndefined(); + }); + + it("selects a configured internal PowerShell and falls back to the preferred detected version", () => { + const v71 = { kind: "pwsh" as const, path: "C:\\PowerShell\\7.1\\pwsh.exe", version: "7.1" }; + const v72 = { kind: "pwsh" as const, path: "C:\\PowerShell\\7.2\\pwsh.exe", version: "7.2" }; + const cmd = { kind: "cmd" as const, path: "C:\\Windows\\System32\\cmd.exe" }; + + expect(selectWindowsPowerShell(v71.path.toLowerCase(), [v72, v71, cmd])).toEqual(v71); + expect(selectWindowsPowerShell(WINDOWS_SHELL_AUTO, [v72, v71, cmd])).toEqual(v72); + expect(selectWindowsPowerShell("C:\\removed\\pwsh.exe", [v72, v71, cmd])).toEqual(v72); + }); + + it("uses the shared configured PowerShell resolver for internal command builders", () => { + const configured = { kind: "pwsh" as const, path: "C:\\PowerShell\\7.1\\pwsh.exe" }; + const dispose = setWindowsPowerShellPreferenceResolver(() => configured); + try { + expect(detectPowerShell(() => "C:\\PowerShell\\7.2\\pwsh.exe")).toEqual(configured); + } finally { + dispose(); + } + + expect(detectPowerShell(() => "C:\\PowerShell\\7.2\\pwsh.exe")).toEqual({ + kind: "pwsh", + path: "C:\\PowerShell\\7.2\\pwsh.exe", + version: "7.2", + }); + }); + + it("keeps the Store alias only when no launchable pwsh image exists", () => { + const shells = detectWindowsShells( + (name) => (name === "pwsh.exe" ? storeAlias : undefined), + () => false, + { extraPwshPaths: [] }, + ); + expect(shells[0]).toEqual({ kind: "pwsh", path: storeAlias }); + }); + + it("lists MSI, preview, Scoop, Chocolatey, and WinGet well-known layouts", () => { + const previous = { + ProgramW6432: process.env.ProgramW6432, + ProgramFiles: process.env.ProgramFiles, + LOCALAPPDATA: process.env.LOCALAPPDATA, + ChocolateyInstall: process.env.ChocolateyInstall, + }; + process.env.ProgramW6432 = "C:\\Program Files"; + process.env.ProgramFiles = "C:\\Program Files"; + process.env.LOCALAPPDATA = "C:\\Users\\a\\AppData\\Local"; + process.env.ChocolateyInstall = "C:\\ProgramData\\chocolatey"; + try { + expect(windowsPwshWellKnownPaths()).toEqual( + expect.arrayContaining([ + "C:\\Program Files\\PowerShell\\7\\pwsh.exe", + "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe", + win32.join(homedir(), "scoop", "apps", "pwsh", "current", "pwsh.exe"), + "C:\\ProgramData\\chocolatey\\bin\\pwsh.exe", + "C:\\Users\\a\\AppData\\Local\\Microsoft\\WinGet\\Links\\pwsh.exe", + ]), + ); + } finally { + for (const [key, value] of Object.entries(previous)) { + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + } + }); + + it("parses Get-AppxPackage install locations and ignores alias stubs", () => { + expect( + parseAppxInstallLocations( + `${msixImage.replace(/\\pwsh\.exe$/u, "")}\r\nC:\\Program Files\\WindowsApps\\Microsoft.PowerShellPreview_7.6.0.0_x64__8wekyb3d8bbwe\r\n`, + ), + ).toEqual([ + "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.6.1.0_x64__8wekyb3d8bbwe", + "C:\\Program Files\\WindowsApps\\Microsoft.PowerShellPreview_7.6.0.0_x64__8wekyb3d8bbwe", + ]); + expect(isWindowsAppExecutionAlias(storeAlias)).toBe(true); + expect(isWindowsAppExecutionAlias(msixImage)).toBe(false); + expect(isWindowsAppExecutionAlias(msiImage)).toBe(false); + }); + + it("falls back from a stale override without applying that override's arguments", () => { + expect( + selectWindowsShell( + { + windowsShellPath: "C:\\removed\\pwsh.exe", + windowsShellArguments: '-NoProfile -File "C:\\profile scripts\\init.ps1"', + }, + [ + { kind: "powershell", path: "C:\\Windows\\powershell.exe" }, + { kind: "cmd", path: "C:\\Windows\\cmd.exe" }, + ], + ), + ).toEqual({ + shell: "C:\\Windows\\powershell.exe", + kind: "powershell", + args: ["-NoLogo"], + }); + }); + + it("honors an explicit detected shell and falls back to cmd when none are detected", () => { + const cmd = { kind: "cmd" as const, path: "C:\\Windows\\System32\\cmd.exe" }; + expect( + selectWindowsShell( + { windowsShellPath: cmd.path, windowsShellArguments: '/q /k "echo ready"' }, + [{ kind: "pwsh", path: "C:\\PowerShell\\pwsh.exe" }, cmd], + ), + ).toEqual({ + shell: cmd.path, + kind: "cmd", + args: ["/q", "/k", "echo ready"], + }); + + expect( + selectWindowsShell({ windowsShellPath: WINDOWS_SHELL_AUTO, windowsShellArguments: "/q" }, []), + ).toMatchObject({ kind: "cmd", args: ["/q"] }); + }); + + it("parses quoted and escaped arguments without invoking a shell", () => { + expect( + parseWindowsShellArguments(`-NoProfile "two words" 'three words' "say \\"hi\\""`), + ).toEqual(["-NoProfile", "two words", "three words", 'say "hi"']); + expect(parseWindowsShellArguments("")).toEqual([]); + expect(parseWindowsShellArguments(" \t ")).toEqual([]); + expect(parseWindowsShellArguments(`-Flag\t"unclosed`)).toEqual(["-Flag", "unclosed"]); + expect(parseWindowsShellArguments(`""`)).toEqual([""]); + }); + + it("infers pwsh versions from install folders and MSIX package names", () => { + expect(inferPwshVersion(msiImage)).toBe("7"); + expect(inferPwshVersion("C:\\Program Files\\PowerShell\\7.2\\pwsh.exe")).toBe("7.2"); + expect(inferPwshVersion(msixImage)).toBe("7.6.1"); + expect(inferPwshVersion(scoopImage)).toBeUndefined(); + expect(inferPwshVersion("C:\\Users\\a\\scoop\\apps\\pwsh\\7.4.6\\pwsh.exe")).toBe("7.4.6"); + }); + + it("infers shell kind from the executable basename", () => { + expect(inferWindowsShellKind("C:\\Program Files\\PowerShell\\7\\pwsh.exe")).toBe("pwsh"); + expect( + inferWindowsShellKind("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"), + ).toBe("powershell"); + expect(inferWindowsShellKind("C:\\Windows\\System32\\cmd.exe")).toBe("cmd"); + }); +}); + +describe.skipIf(process.platform !== "win32")("installed Windows pwsh 7", () => { + it("discovers MSI, Store/MSIX, or installer pwsh without returning the execution alias", () => { + const wellKnown = windowsPwshWellKnownPaths().find((path) => existsSync(path)); + const pwsh = detectWindowsShells(() => undefined).find((shell) => shell.kind === "pwsh"); + expect(wellKnown ? pwsh?.path.toLowerCase() : wellKnown).toBe(wellKnown?.toLowerCase()); + expect(pwsh ? isWindowsAppExecutionAlias(pwsh.path) : false).toBe(false); + expect(pwsh ? existsSync(pwsh.path) : true).toBe(true); + expect(pwsh?.version ?? "7.0").toMatch(/^7\.\d+/u); + }); +}); diff --git a/src/supervisor/shellPreference.ts b/src/supervisor/shellPreference.ts index c268f1276..f9ceed5aa 100644 --- a/src/supervisor/shellPreference.ts +++ b/src/supervisor/shellPreference.ts @@ -1,35 +1,446 @@ import { spawnSync } from "node:child_process"; +import { existsSync, readdirSync, realpathSync } from "node:fs"; +import { homedir } from "node:os"; +import { win32 } from "node:path"; +import type { AvailableWindowsShell, SharedSettings, WindowsShellKind } from "@/shared/settings"; +import { WINDOWS_SHELL_AUTO } from "@/shared/settings"; +import { getWindowsSystemCommand } from "./agents/base/shellBasics"; export interface WindowsShellPreference { shell: string; - kind: "pwsh" | "powershell" | "cmd"; + kind: WindowsShellKind; args: string[]; } -function resolveShell(name: string): string | undefined { - const result = spawnSync(process.platform === "win32" ? "where.exe" : "which", [name], { - encoding: "utf8", - timeout: 5_000, - windowsHide: true, - }); - if (result.status !== 0) return undefined; - return result.stdout.trim().split(/\r?\n/)[0]; +export interface DetectedPowerShell { + path: string; + kind: "pwsh" | "powershell"; } -export function detectWindowsShell(): WindowsShellPreference { - const pwsh = resolveShell("pwsh.exe") ?? resolveShell("pwsh"); - if (pwsh) { - return { shell: pwsh, kind: "pwsh", args: ["-NoLogo"] }; +export type ResolveWindowsExecutable = (name: string) => string | undefined; +type ResolveConfiguredPowerShell = () => DetectedPowerShell | undefined; + +let resolveConfiguredPowerShell: ResolveConfiguredPowerShell | undefined; + +/** + * The supervisor is a singleton process, so agent command builders can consult + * the same persisted internal-shell selection without threading it through + * every provider adapter. The disposer prevents test/runtime instances from + * leaving a stale resolver behind. + */ +export function setWindowsPowerShellPreferenceResolver( + resolver: ResolveConfiguredPowerShell, +): () => void { + resolveConfiguredPowerShell = resolver; + return () => { + if (resolveConfiguredPowerShell === resolver) resolveConfiguredPowerShell = undefined; + }; +} + +export interface DetectWindowsShellsOptions { + /** Test seam: installed pwsh paths that are not PATH-resolved. */ + extraPwshPaths?: readonly string[]; + /** Test seam for resolving the product version of generic install paths. */ + resolvePwshVersion?: (path: string) => string | undefined; +} + +/** Store/MSIX App Execution Alias, deprioritized when a real package image is discoverable. */ +export function isWindowsAppExecutionAlias(path: string): boolean { + return /\\appdata\\local\\microsoft\\windowsapps\\[^\\]+$/i.test(path.replaceAll("/", "\\")); +} + +function uniqueExistingPaths( + paths: readonly (string | undefined)[], + pathExists: (path: string) => boolean, +): string[] { + const seen = new Set(); + const existing: string[] = []; + for (const path of paths) { + if (!path || !pathExists(path)) continue; + const key = path.toLowerCase(); + if (seen.has(key)) continue; + seen.add(key); + existing.push(path); + } + return existing; +} + +function windowsProgramFilesDirs(): string[] { + return uniqueExistingPaths( + [process.env.ProgramW6432, process.env.ProgramFiles, process.env["ProgramFiles(x86)"]], + (path) => path.length > 0, + ); +} + +/** MSI, winget, Scoop, Chocolatey, and user-scope WinGet package layouts. */ +export function windowsPwshWellKnownPaths(): string[] { + const home = homedir(); + const localAppData = process.env.LOCALAPPDATA; + const chocolatey = process.env.ChocolateyInstall ?? "C:\\ProgramData\\chocolatey"; + const paths = windowsProgramFilesDirs().flatMap((programFiles) => [ + win32.join(programFiles, "PowerShell", "7", "pwsh.exe"), + win32.join(programFiles, "PowerShell", "7-preview", "pwsh.exe"), + ]); + paths.push( + win32.join(home, "scoop", "apps", "pwsh", "current", "pwsh.exe"), + win32.join(chocolatey, "bin", "pwsh.exe"), + ); + if (localAppData) { + paths.push( + win32.join(localAppData, "Microsoft", "WinGet", "Links", "pwsh.exe"), + win32.join(localAppData, "Microsoft", "powershell", "pwsh.exe"), + ); + } + return paths; +} + +function listPwshInChildFolders(root: string, pathExists: (path: string) => boolean): string[] { + try { + const found: string[] = []; + for (const name of readdirSync(root)) { + if (name.toLowerCase() === "current") continue; + const candidate = win32.join(root, name, "pwsh.exe"); + if (pathExists(candidate)) found.push(candidate); + } + return found; + } catch { + return []; + } +} + +function listPwshUnderPackageRoot(root: string, pathExists: (path: string) => boolean): string[] { + try { + const found: string[] = []; + for (const name of readdirSync(root)) { + if (!/^Microsoft\.PowerShell/i.test(name)) continue; + const packageDir = win32.join(root, name); + const direct = win32.join(packageDir, "pwsh.exe"); + if (pathExists(direct)) { + found.push(direct); + continue; + } + try { + for (const child of readdirSync(packageDir)) { + const nested = win32.join(packageDir, child, "pwsh.exe"); + if (pathExists(nested)) found.push(nested); + } + } catch { + // Nested version folders are optional (WinGet package roots). + } + } + return found; + } catch { + return []; + } +} + +export function parseAppxInstallLocations(stdout: string): string[] { + return stdout + .split(/\r?\n/g) + .map((line) => line.trim()) + .filter((line) => /^[A-Za-z]:[\\/]/u.test(line)); +} + +function listMsixPwshFromAppx(pathExists: (path: string) => boolean): string[] { + const powershell = getWindowsSystemCommand("WindowsPowerShell\\v1.0\\powershell.exe"); + const result = spawnSync( + powershell, + [ + "-NoProfile", + "-NonInteractive", + "-Command", + "Get-AppxPackage -Name Microsoft.PowerShell* -ErrorAction SilentlyContinue | Sort-Object { if ($_.Name -eq 'Microsoft.PowerShell') { 0 } else { 1 } } | ForEach-Object { $_.InstallLocation }", + ], + { encoding: "utf8", shell: false, timeout: 5_000, windowsHide: true }, + ); + if (result.error || result.status !== 0) return []; + return parseAppxInstallLocations(`${result.stdout ?? ""}`) + .map((dir) => win32.join(dir, "pwsh.exe")) + .filter(pathExists); +} + +/** + * Real Store/MSIX images, not the Local\Microsoft\WindowsApps execution alias. + * Filesystem probes first (WinGet package roots, readable WindowsApps dirs); + * Appx is the fallback when the package directory is not listable. + */ +export function findInstalledWindowsPwsh( + pathExists: (path: string) => boolean = existsSync, + options?: { probeAppx?: boolean }, +): string[] { + const localAppData = process.env.LOCALAPPDATA; + const scoopRoot = win32.join(homedir(), "scoop", "apps", "pwsh"); + const msixFromFs = [ + ...(localAppData + ? listPwshUnderPackageRoot( + win32.join(localAppData, "Microsoft", "WinGet", "Packages"), + pathExists, + ) + : []), + ...windowsProgramFilesDirs().flatMap((programFiles) => + listPwshUnderPackageRoot(win32.join(programFiles, "WindowsApps"), pathExists), + ), + ]; + const found = [ + ...windowsProgramFilesDirs().flatMap((programFiles) => + listPwshInChildFolders(win32.join(programFiles, "PowerShell"), pathExists), + ), + ...windowsPwshWellKnownPaths().filter(pathExists), + ...listPwshInChildFolders(scoopRoot, pathExists), + ...msixFromFs, + ]; + if (msixFromFs.length === 0 && options?.probeAppx !== false && pathExists === existsSync) { + found.push(...listMsixPwshFromAppx(pathExists)); + } + return uniqueExistingPaths(found, pathExists); +} + +function canonicalizeWindowsPath(path: string): string { + try { + return realpathSync(path); + } catch { + return path; + } +} + +export function inferPwshVersion(path: string): string | undefined { + const normalized = path.replaceAll("/", "\\"); + const msix = /Microsoft\.PowerShell(?:Preview)?_(\d+(?:\.\d+){1,3})/i.exec(normalized)?.[1]; + if (msix) { + return /^\d+\.\d+\.\d+\.0$/u.test(msix) ? msix.slice(0, -2) : msix; } + const folder = /\\(?:PowerShell|pwsh)\\([^\\]+)\\pwsh\.exe$/i.exec(normalized)?.[1]; + if (folder && folder.toLowerCase() !== "current") return folder; + return undefined; +} + +export function parsePwshVersion(stdout: string): string | undefined { + return stdout + .split(/\r?\n/gu) + .map((line) => line.trim()) + .find((line) => /^\d+(?:\.\d+){1,3}(?:-[0-9A-Za-z.-]+)?$/u.test(line)); +} + +export function probePwshVersion(path: string): string | undefined { + const result = spawnSync( + path, + [ + "-NoLogo", + "-NoProfile", + "-NonInteractive", + "-Command", + "$PSVersionTable.PSVersion.ToString()", + ], + { encoding: "utf8", shell: false, timeout: 5_000, windowsHide: true }, + ); + if (result.error || result.status !== 0) return undefined; + return parsePwshVersion(`${result.stdout ?? ""}`); +} + +function toPwshShell( + path: string, + resolvePwshVersion: (path: string) => string | undefined, +): AvailableWindowsShell { + const inferredVersion = inferPwshVersion(path) ?? inferPwshVersion(canonicalizeWindowsPath(path)); + const previewPackage = /Microsoft\.PowerShellPreview_/iu.test(path); + const version = + inferredVersion && inferredVersion !== "7" && inferredVersion !== "7-preview" && !previewPackage + ? inferredVersion + : (resolvePwshVersion(path) ?? + (previewPackage && inferredVersion ? `${inferredVersion}-preview` : inferredVersion)); + return version ? { path, kind: "pwsh", version } : { path, kind: "pwsh" }; +} + +function collectLaunchablePwsh( + resolved: string | undefined, + installed: readonly string[], +): string[] { + const ordered: string[] = []; + const seen = new Set(); + const add = (path: string | undefined, allowAlias = false) => { + if (!path || (!allowAlias && isWindowsAppExecutionAlias(path))) return; + const key = canonicalizeWindowsPath(path).toLowerCase(); + if (seen.has(key)) return; + seen.add(key); + ordered.push(path); + }; + add(resolved); + for (const path of installed) add(path); + if (ordered.length === 0) add(resolved, true); + return ordered; +} + +export function inferWindowsShellKind(path: string): WindowsShellKind { + const name = win32.basename(path).toLowerCase(); + if (name === "pwsh" || name === "pwsh.exe") return "pwsh"; + if (name === "powershell" || name === "powershell.exe") return "powershell"; + return "cmd"; +} + +function pushShell( + shells: AvailableWindowsShell[], + seenPaths: Set, + path: string | undefined, + kind: WindowsShellKind, +): void { + if (!path) return; + const key = path.toLowerCase(); + if (seenPaths.has(key)) return; + seenPaths.add(key); + shells.push({ path, kind }); +} + +/** + * Detect every supported Windows shell in automatic preference order. PATH + * resolution is tried first; Store/MSIX aliases and installer layouts are + * then resolved to a real pwsh.exe so node-pty can spawn it. + */ +export function detectWindowsShells( + resolveExecutable: ResolveWindowsExecutable, + pathExists: (path: string) => boolean = existsSync, + options?: DetectWindowsShellsOptions, +): AvailableWindowsShell[] { + if (process.platform !== "win32") return []; + + const shells: AvailableWindowsShell[] = []; + const seenPaths = new Set(); + const resolvedPwsh = resolveExecutable("pwsh.exe") ?? resolveExecutable("pwsh"); + const installedPwsh = + options?.extraPwshPaths !== undefined + ? uniqueExistingPaths(options.extraPwshPaths, pathExists) + : findInstalledWindowsPwsh(pathExists); + const resolvePwshVersion = options?.resolvePwshVersion ?? probePwshVersion; + for (const path of collectLaunchablePwsh(resolvedPwsh, installedPwsh)) { + const shell = toPwshShell(path, resolvePwshVersion); + const key = canonicalizeWindowsPath(shell.path).toLowerCase(); + if (seenPaths.has(key)) continue; + seenPaths.add(key); + shells.push(shell); + } + + const legacyPowerShell = getWindowsSystemCommand("WindowsPowerShell\\v1.0\\powershell.exe"); + pushShell( + shells, + seenPaths, + resolveExecutable("powershell.exe") ?? + resolveExecutable("powershell") ?? + (pathExists(legacyPowerShell) ? legacyPowerShell : undefined), + "powershell", + ); + + const commandPrompt = getWindowsSystemCommand("cmd.exe"); + pushShell( + shells, + seenPaths, + resolveExecutable("cmd.exe") ?? resolveExecutable("cmd") ?? commandPrompt, + "cmd", + ); + return shells; +} + +export function detectPowerShell( + resolveExecutable: ResolveWindowsExecutable, +): DetectedPowerShell | undefined { + if (process.platform !== "win32") return undefined; + return ( + resolveConfiguredPowerShell?.() ?? + detectWindowsShells(resolveExecutable, () => false, { + extraPwshPaths: [], + resolvePwshVersion: () => undefined, + }).find((shell): shell is DetectedPowerShell => shell.kind !== "cmd") + ); +} + +export function selectWindowsPowerShell( + configuredPath: string, + availableShells: readonly AvailableWindowsShell[], +): DetectedPowerShell | undefined { + const powerShells = availableShells.filter( + (shell): shell is DetectedPowerShell => shell.kind !== "cmd", + ); + const selected = + configuredPath === WINDOWS_SHELL_AUTO + ? undefined + : powerShells.find((shell) => shell.path.toLowerCase() === configuredPath.toLowerCase()); + return selected ?? powerShells[0]; +} + +/** Parse a compact argument field into argv without invoking a shell. */ +export function parseWindowsShellArguments(value: string): string[] { + const args: string[] = []; + let current = ""; + let quote: '"' | "'" | undefined; + let tokenStarted = false; + + const push = () => { + if (tokenStarted) args.push(current); + current = ""; + tokenStarted = false; + }; + + for (let index = 0; index < value.length; index += 1) { + const character = value[index]!; + if (quote === '"' && character === "\\") { + const escaped = value[index + 1]; + if (escaped === '"' || escaped === "\\") { + current += escaped; + index += 1; + } else { + current += "\\"; + } + tokenStarted = true; + continue; + } + if (quote) { + if (character === quote) quote = undefined; + else current += character; + tokenStarted = true; + continue; + } + if (character === '"' || character === "'") { + quote = character; + tokenStarted = true; + continue; + } + if (/\s/u.test(character)) { + push(); + continue; + } + current += character; + tokenStarted = true; + } + push(); + return args; +} + +function preferredWindowsShellArgs(kind: WindowsShellKind, argumentsText: string): string[] { + return [...(kind === "cmd" ? [] : ["-NoLogo"]), ...parseWindowsShellArguments(argumentsText)]; +} - const powershell = resolveShell("powershell.exe") ?? resolveShell("powershell"); - if (powershell) { - return { shell: powershell, kind: "powershell", args: ["-NoLogo"] }; +export function selectWindowsShell( + settings: Pick, + availableShells: readonly AvailableWindowsShell[], +): WindowsShellPreference { + const explicitOverride = settings.windowsShellPath !== WINDOWS_SHELL_AUTO; + const selected = explicitOverride + ? availableShells.find( + (shell) => shell.path.toLowerCase() === settings.windowsShellPath.toLowerCase(), + ) + : undefined; + const staleOverride = explicitOverride && !selected; + const shell = selected ?? availableShells[0]; + const argumentsText = staleOverride ? "" : settings.windowsShellArguments; + if (!shell) { + return { + shell: getWindowsSystemCommand("cmd.exe"), + kind: "cmd", + args: preferredWindowsShellArgs("cmd", argumentsText), + }; } return { - shell: "C:\\Windows\\System32\\cmd.exe", - kind: "cmd", - args: ["/k"], + shell: shell.path, + kind: shell.kind, + args: preferredWindowsShellArgs(shell.kind, argumentsText), }; } diff --git a/src/supervisor/supervisorRuntime.ts b/src/supervisor/supervisorRuntime.ts index 00202bff0..c9f8b5905 100644 --- a/src/supervisor/supervisorRuntime.ts +++ b/src/supervisor/supervisorRuntime.ts @@ -46,7 +46,17 @@ import { GitHubService } from "./github"; import { ProjectWatcher } from "./projectWatcher"; import { LanguageServerManager } from "./lsp"; import { ProjectTreeService } from "./projectTree"; -import { detectWindowsShell, type WindowsShellPreference } from "./shellPreference"; +import { WINDOWS_SHELL_AUTO } from "@/shared/settings"; +import { + detectWindowsShells, + inferWindowsShellKind, + selectWindowsPowerShell, + selectWindowsShell, + setWindowsPowerShellPreferenceResolver, + type WindowsShellPreference, +} from "./shellPreference"; +import { getWindowsSystemCommand } from "./agents/base/shellBasics"; +import { resolveExecutablePath } from "./agents/base/processRuntime"; import { AgentStatusService, detectWslAgentStatuses } from "./runtime/agentStatusService"; import { createLocalUsageCollectors } from "./runtime/localUsageCollectors"; import { UsageService } from "./runtime/usageService"; @@ -112,7 +122,9 @@ export class SupervisorRuntime { readonly fileIndexService = new FileIndexService(); readonly projectTreeService = new ProjectTreeService(); private readonly adapters = new Map(); - private readonly windowsShell: WindowsShellPreference; + private availableWindowsShellsCache: + | { shells: ReturnType; ts: number } + | undefined; readonly agentStatusService: AgentStatusService; readonly usageService: UsageService; readonly agentRegistryService: AgentRegistryService; @@ -130,6 +142,7 @@ export class SupervisorRuntime { private readonly subagentRunManager: SubagentRunManager; private readonly routingOverridePersistence: RoutingOverridePersistence; private readonly disposeWslCredentialProjectScope: () => void; + private readonly disposeWindowsPowerShellPreference: () => void; private wslHookBridge: WslBridgeServer | undefined; readonly sessions: Map; @@ -174,6 +187,12 @@ export class SupervisorRuntime { this.settingsPath = paths.settingsPath; this.acpIconsDir = paths.acpIconsDir; this.sharedSettingsCache = new SupervisorSharedSettingsCache(this.settingsPath); + this.disposeWindowsPowerShellPreference = setWindowsPowerShellPreferenceResolver(() => { + const preference = this.resolveWindowsPowerShell(); + return preference.kind === "cmd" + ? undefined + : { path: preference.shell, kind: preference.kind }; + }); this.routingOverridePersistence = new RoutingOverridePersistence({ emit, invalidateSettings: () => this.sharedSettingsCache.invalidate(), @@ -202,11 +221,6 @@ export class SupervisorRuntime { void prefetchNativeNodeRuntime(baseDir); this.lspManager = new LanguageServerManager(emit); - this.windowsShell = - process.platform === "win32" - ? detectWindowsShell() - : { shell: process.env.SHELL || "/bin/bash", kind: "cmd", args: [] }; - this.agentStatusService = new AgentStatusService({ adapters: this.adapters, settingsPath: this.settingsPath, @@ -408,7 +422,7 @@ export class SupervisorRuntime { settingsPath: this.settingsPath, readDisableCliHookPlugin: () => this.sharedSettingsCache.read().disableCliHookPlugin, adapters: this.adapters, - windowsShell: this.windowsShell, + resolveWindowsShell: (runtime) => this.resolveWindowsShell(runtime), ...(this.wslHookBridge ? { wslBridge: this.wslHookBridge } : {}), resolvePluginEnvForSpawn: (input) => this.cliHookPluginCoordinator.resolvePluginEnvForSpawn(input), @@ -532,6 +546,57 @@ export class SupervisorRuntime { void this.agentRegistryService.cacheLocalAcpIconsOnLaunch(); } + getAvailableWindowsShells() { + return process.platform === "win32" ? this.getCachedAvailableWindowsShells() : []; + } + + private getCachedAvailableWindowsShells() { + const now = Date.now(); + if (this.availableWindowsShellsCache && now - this.availableWindowsShellsCache.ts < 60_000) { + return this.availableWindowsShellsCache.shells; + } + const shells = detectWindowsShells(resolveExecutablePath); + this.availableWindowsShellsCache = { shells, ts: now }; + return shells; + } + + private resolveWindowsPowerShell(): WindowsShellPreference { + const settings = this.sharedSettingsCache.readFresh(); + const detected = selectWindowsPowerShell( + settings.windowsInternalShellPath, + this.getCachedAvailableWindowsShells(), + ); + if (detected) { + return { shell: detected.path, kind: detected.kind, args: ["-NoLogo"] }; + } + const legacy = getWindowsSystemCommand("WindowsPowerShell\\v1.0\\powershell.exe"); + if (existsSync(legacy)) { + return { shell: legacy, kind: "powershell", args: ["-NoLogo"] }; + } + return { shell: getWindowsSystemCommand("cmd.exe"), kind: "cmd", args: [] }; + } + + private resolveWindowsShell( + runtime: "preferred" | "powershell" = "preferred", + ): WindowsShellPreference { + if (process.platform !== "win32") { + return { shell: process.env.SHELL || "/bin/bash", kind: "cmd", args: [] }; + } + if (runtime === "powershell") { + return this.resolveWindowsPowerShell(); + } + const settings = this.sharedSettingsCache.readFresh(); + if (settings.windowsShellPath !== WINDOWS_SHELL_AUTO && existsSync(settings.windowsShellPath)) { + return selectWindowsShell(settings, [ + { + path: settings.windowsShellPath, + kind: inferWindowsShellKind(settings.windowsShellPath), + }, + ]); + } + return selectWindowsShell(settings, this.getCachedAvailableWindowsShells()); + } + async getCrossagentSpawnableAgents(contextTags: readonly string[] = []) { const { windows } = await this.agentStatusService.getAgentStatuses({ wslDistros: [] }); const settings: CrossagentVisibilitySettings = this.sharedSettingsCache.read(); @@ -885,6 +950,7 @@ export class SupervisorRuntime { } async disposeAsync(): Promise { + this.disposeWindowsPowerShellPreference(); this.disposeWslCredentialProjectScope(); this.routingOverridePersistence.dispose(); this.usageService.stop();