Start a login shell in the home directory when launched from the desktop - #123
Merged
pg83 merged 1 commit intoSep 28, 2026
Merged
Conversation
A .app started from Finder, the Dock or open(1) is launched by launchd, which hands it a bare PATH (/usr/bin:/bin:/usr/sbin:/sbin) and no inherited shell environment, and starts it in "/". The shell inside then runs non-login (login defaults to false), so it never reads .zprofile, which is where Homebrew's `brew shellenv` normally lives: commands from /opt/homebrew/bin are not found, and the prompt opens at "/". Running the raw binary from another terminal hid this, since the child inherits that terminal's environment. Shipping an app bundle makes the desktop launch the normal path. When launchd is the parent process (macOS only): - login defaults to true, unless the user set it explicitly, in config or on the command line, so +login and login = false still win. - a working directory of "/" becomes $HOME. Launches from a shell are untouched. The parent-pid test is the same one other macOS terminals use to tell a desktop launch from a shell launch; it is taken as a parameter so the predicate is unit-testable. Checked by launching the built binary orphaned to launchd (ppid 1) with a probe as the shell: default gives a login argv[0] and cwd $HOME; +login gives a plain argv[0]; the same launches from a live shell change nothing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
Launching
Shitty.app(orPretty.app) from Finder, the Dock oropengives a shell that can't find Homebrew commands, and a prompt that opens at/:Both come from the launch context, not from the terminal:
PATH=/usr/bin:/bin:/usr/sbin:/sbinand no shell environment to inherit, in/.logindefaults tofalse, so the shell runs non-login and never reads.zprofile, which is where Homebrew'seval "$(brew shellenv)"conventionally lives.Running the raw binary from another terminal hid it, because the child then inherits that terminal's environment. Since #120 ships an app bundle, a desktop launch is now the normal path.
The fix
Only when launchd is the parent process (macOS only):
logindefaults to true, unless the user set it, in the config file or on the command line.+loginandlogin = falsestill win./becomes$HOME.Launches from a shell are untouched. The predicate takes the parent pid as a parameter (
launchedFromDesktop(pid_t)), so it is unit-testable; the login default is read off the option's source (HardDefaultmeans the user never chose).I went with "only when launched from launchd" rather than making every macOS launch a login shell (as Terminal.app and iTerm2 do), because it changes nothing for people who start
stfrom a shell today. Happy to switch if you'd rather follow the other convention.Verified
Launched the built binary orphaned to launchd (
ppid 1) with a small C probe as the shell, so it could report its trueargv[0]and cwd:$HOME(was/)+login$HOME-loginStartupunit test; the whole unit suite passes through./build's own runner.openstarted the app but never spawned the shell, so the launchd case is covered by the orphaned-process test above rather than by an actual Finder launch.🤖 Generated with Claude Code