Skip to content

Shell support

Kangentic spawns agent CLI processes in platform-native shells. This page documents how shells are detected, adapted, and configured across platforms.

Kangentic auto-detects the user’s shell in the following order:

PlatformDetection Order
WindowsPowerShell 7 (pwsh) > PowerShell 5 (powershell) > Git Bash (bash) > cmd.exe
macOSzsh > bash > fish > nushell (nu) > sh (default: $SHELL or zsh)
Linuxbash > zsh > fish > dash > nushell (nu) > ksh > sh (default: $SHELL or bash)

Override the detected shell in Settings under the Terminal tab, or set terminal.shell in the config file.

Kangentic adapts command execution for each shell:

ShellAdaptation
PowerShellLaunched with -NoLogo. The typed agent command is prefixed with the & call operator so a quoted executable path runs instead of echoing, on every platform since v0.46.0, because pwsh can be the login shell on macOS and Linux too. A Set-Location -LiteralPath prelude, written raw without that prefix, corrects the working directory when the path contains [ or ], which PowerShell would otherwise treat as wildcards
WSL BashSpawned as wsl.exe plus its distro arguments. The agent command’s leading executable path is converted to /mnt/c/... format. Invoked with the arguments already written into the shell string, so no --login is added
cmd.exeNo --login flag (it has no such concept). It cannot start in a UNC path, so a session whose working directory is one starts in your home directory and gets a pushd "<unc path>" prelude, which maps the share to a temporary drive letter before the command runs
fishNo --login flag (fish doesn’t support it). Arguments get fish’s own single-quoted form (see quoteArg())
nushell (nu)No --login flag. A quoted command head gets a ^ prefix, the only way nushell runs one, and arguments are double-quoted
bash/zsh/dash/ksh/shStandard --login invocation. --login is the fallback, applied to every shell that is not WSL, cmd.exe, PowerShell, fish, or nushell

An agent spawn prefixes the command it types with the shell’s own clear: Clear-Host; for the PowerShell family, cls & for cmd.exe, and clear; for everything else (bash, zsh, fish, nu, dash, ksh, sh, WSL, Git Bash). The shell then erases its own startup preamble and the command echo the instant the command runs, so what the terminal, the scrollback ring, the headless parser, replays, and the paired phone all receive is a real clear in the real byte stream rather than a heuristic guess about which startup bytes to strip. That matters because those bytes move: pwsh 7.6 started emitting cursor-hide and erase sequences inside its escape-only startup preamble, which broke every detector keyed on them.

Command Terminals are deliberately exempt. They are your own shell, with no spawn echo to strip and no reason to wipe what you already typed.

npm .cmd shims under PowerShell and Git Bash

Section titled “npm .cmd shims under PowerShell and Git Bash”

An agent installed with npm i -g resolves on Windows to its .cmd shim, and a .cmd runs through cmd.exe, whose command line ends at the first newline. Typed into a PowerShell or Git Bash session, a multi-line task prompt reached the agent as its first line and nothing else. Since v0.39.0 the spawn launches the sibling shim npm writes beside every .cmd instead, so the host shell hands the prompt to node directly:

Host shellLaunchesCondition
PowerShell (pwsh or powershell)<name>.ps1The host’s effective execution policy allows scripts (RemoteSigned, Unrestricted, or Bypass), probed once per shell per app run. Windows PowerShell 5.1 defaults to Restricted and pwsh 7 to RemoteSigned, and the two keep separate policies
Git BashThe extensionless #!/bin/sh shimThe file exists and starts with #!
cmd.exeThe .cmd itselfThe prompt is already flattened for a cmd host
WSLUnchangedWSL cannot launch a .cmd at all (see WSL runs the Windows binary), and the sh shim would run a Windows bundle under a Linux node

With no usable sibling, or under a Restricted or AllSigned policy, the .cmd stays and the prompt is flattened to one line, so the whole title and description still arrive. The cause is logged once per shell; for the policy case the fix is Set-ExecutionPolicy RemoteSigned -Scope CurrentUser in that host, then an app restart. The process tree becomes pwsh launching node directly rather than through cmd.exe.

Detection reads the same shims, and the lookup checks that a shim file exists, never that its target does. On Windows it searches the working directory before PATH, so a shim left in a project root by an uninstalled local package used to shadow the real global install. Detection now walks every match in order and skips a .cmd, .bat, or .ps1 shim whose node_modules target is missing without spending a probe on it.

Cross-platform path handling uses two key utilities:

Converts Windows backslashes to forward slashes for consistent path handling across platforms. Used when passing paths to shells and CLI tools.

Properly escapes and quotes arguments for the target shell: single quotes for POSIX shells (bash, zsh, sh, WSL), double quotes for PowerShell and cmd.exe, and a form of their own for fish and nushell. The target shell is passed explicitly at every spawn call site, so quoting always matches the shell the command is about to run in. A multi-line prompt is kept on one physical input line: newlines stay literal inside the single quotes of POSIX shells and fish, become \n escapes for nushell, and become backtick-n escapes inside PowerShell’s double quotes, which PowerShell expands back into real newlines before the agent sees the argument.

v0.46.0 fixed five ways a character in a prompt or an override could break a spawn, each measured on the real shell:

ShellCharacterWhat happenedWhat Kangentic writes now
nushell'nushell’s single quotes have no escapes, so POSIX’s '\'' was a parse error and every prompt holding an apostrophe failed to startA double-quoted string, escaping only \ and "
fish\ and 'fish reads \\ and \' inside single quotes, so the POSIX form lost a backslash and could leave the quote openEvery \ and ' backslash-escaped
cmd.exe%cmd expands %NAME% even inside double quotes, and %% is a batch-file escape that does not apply at a promptEach % as "^%": close the quote, caret-escape, reopen. Also applied to the head of every Windows command that runs a detected agent CLI
PowerShellcurly double quotesPowerShell ends a double-quoted string at U+201C, U+201D, and U+201E too, so a description holding one split the argument, and a ; after it ran as a second commandEach one doubled
PowerShell and cmd.exe" in a model or effort overrideAn override set from kangentic.json, a board profile, or an MCP call could hold a quote and a separator and run a second commandThe quote written as ', the trade the prompt builders already make, because no escaped quote is safe on all three Windows shells

Aider spawns under PowerShell work again: Aider’s notification command carried quotes the outer shell could not hold. Under PowerShell or cmd.exe on Windows, a path that is not one bare word now drops the notification flags, and Aider’s prompt regex stays the idle signal.

Rewrites the leading executable path of a command into POSIX form for unix-like shells. It recognizes all three quote forms a command builder can emit - a bare path, a double-quoted path, and the single-quoted path quoteArg produces for unix-like shells - plus UNC paths (\\server\share\...). A single-quoted token stays single-quoted so shell-active characters in a legal Windows path remain inert. Before this handled the single-quoted case, selecting Git Bash or WSL as the terminal shell broke every agent spawn with “command not found”.

When running under Git Bash on Windows, paths like /c/Users/... are used instead of C:\Users\.... Kangentic detects Git Bash and adjusts path formatting accordingly.

For WSL shells, Windows paths are converted using the /mnt/ prefix:

  • C:\Users\project > /mnt/c/Users/project

Kangentic detects WSL shells and applies special handling:

  • Detection: wsl --list --quiet, with a 5-second timeout
  • Docker filtering: WSL Docker Desktop distributions (distros whose name starts with docker-) are filtered out of the shell list
  • Spawn: the stored shell spec (wsl -d Ubuntu) is split into an executable and its arguments, and the executable is spawned as wsl.exe. The extension is deliberate: node-pty’s ConPTY executable search cannot resolve the bare name wsl, and a session spawned that way exits immediately with no output
  • Path conversion: the agent command’s leading executable path is converted to /mnt/c/.... The command itself is written into the PTY after the shell starts, never passed in argv

Converting the CLI path to /mnt/c/... launches the Windows agent binary through WSL’s interop layer, which can only execute PE .exe files. Two consequences follow, both documented limitations rather than bugs:

  • npm-installed .cmd / .bat shims cannot be launched from inside WSL. Git Bash can run them; WSL interop cannot. On WSL you need the agent’s native .exe install.
  • A CLI installed inside the distro is not discovered. Detection resolves on the Windows host, identically for every shell. Preferring a distro-native binary would also mean converting every path argument the spawn passes (--settings, --mcp-config are Windows paths, correct for a Windows binary and wrong for a Linux one), so it is a possible future feature, not current behavior.

When spawning an agent, Kangentic strips the parent Claude Code session’s markers from the child environment: CLAUDECODE and every CLAUDE_CODE_* variable (entrypoint, session ID, execpath, and the nested-session flag). This keeps a Kangentic-spawned agent a clean top-level session. Without it, a nested Claude Code child would refuse to start or inherit the parent’s session identity and lose its own transcript on resume. The stripping is harmless for non-Claude agents, which ignore these variables, and ANTHROPIC_* keys (BYOK / API auth) are left untouched.

Two CLAUDE_CODE_* keys survive the strip, both renderer tuning flags rather than session-identity markers:

KeyBehavior
CLAUDE_CODE_ALT_SCREEN_FULL_REPAINTKept, and on Windows defaulted to 1 when you have not set it yourself, working around an upstream alternate-screen repaint defect. Set it yourself to override that default
CLAUDE_CODE_SCROLL_SPEEDKept so your own exported value reaches the agent, but deliberately never defaulted. A default of 3 was shipped and reverted the same day: wheel reports coalesce into single large jumps, and a 3x multiplier pushed those past the threshold where the fullscreen TUI’s differential renderer mis-assembles frames. The CLI’s own default of 1 matches the native terminals verified clean

Two more adjustments happen at the same chokepoint, both so agent TUIs render in color:

  • TERM is defaulted to xterm-256color when the merged environment has none, treating an empty string as absent the way capability detectors do. This only ever fills a gap: an inherited or caller-supplied TERM passes through untouched. It matters on Windows specifically, because node-pty turns its spawn name option into the child’s TERM only on POSIX, so a PowerShell-launched Kangentic previously gave its children no TERM at all and CLIs that key color off it (Antigravity’s agy is the clear case) rendered monochrome.
  • NO_COLOR is stripped, but only when the merged environment also carries CLAUDECODE. Claude Code exports NO_COLOR=1 into its tool shells, so a Kangentic launched from inside a Claude Code session was force-dimming every color-capable CLI in every agent terminal. A NO_COLOR without that parentage marker is a deliberate preference and survives, as does one set per spawn by a caller.

On Windows, Kangentic sets the Application User Model ID (AUMID) for proper taskbar grouping and notification association:

ContextAUMID
Developmentcom.kangentic.dev
Productioncom.kangentic.app

This keeps development and production instances on separate taskbar entries and notification channels.

On macOS, the custom title bar positions the traffic light buttons (close, minimize, maximize) with specific offsets to align with the app’s header layout. The position is defined in the main process window configuration.

Kangentic is free and open source. A star helps other people find it.

Star on GitHub