Skip to content

fix(desktop): resolve shell environment for macOS GUI apps#720

Merged
Kitenite merged 6 commits into
mainfrom
debug-shell-config
Jan 13, 2026
Merged

fix(desktop): resolve shell environment for macOS GUI apps#720
Kitenite merged 6 commits into
mainfrom
debug-shell-config

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Jan 12, 2026

Summary

  • Adds shell-env package to resolve shell environment variables for macOS GUI apps
  • Creates ensureShellEnvVars() function that spawns a login shell at startup to capture ZDOTDIR and PATH
  • Calls this before setupAgentHooks() so terminal wrappers get the correct config paths

Problem

On macOS, GUI apps launched via Finder/Spotlight/Dock don't inherit environment variables from the user's shell configuration (.zshrc, .zprofile, etc.). This causes:

  1. Custom ZDOTDIR configurations not being detected
  2. Terminal sessions sourcing configs from $HOME instead of the user's actual config directory
  3. PATH missing user-installed tools (homebrew, nvm, etc.)

Solution

Use the battle-tested shell-env package (same approach as VS Code and Hyper) to:

  1. Spawn a login shell with -ilc flags at app startup
  2. Capture the user's full environment
  3. Persist ZDOTDIR and PATH to process.env
  4. Skip if already launched from terminal (env is correct)

Test plan

  • Launch app from Finder on macOS with custom ZDOTDIR set in shell profile
  • Verify terminal sessions source correct config files
  • Launch app from terminal and verify it skips resolution
  • Test on Linux to verify it works there too
  • Test on Windows to verify it's skipped

Fixes #701

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • App now captures and applies the user's shell environment on macOS/Linux during startup so terminal-related settings (like PATH and ZDOTDIR) are available.
  • Bug Fixes

    • Improved handling and merging of PATH entries to prevent terminal initialization issues and preserve ordering/deduplication.
  • Chores

    • Added a runtime dependency to support resolving the user shell environment.
  • Tests

    • Added comprehensive tests covering PATH merge behaviors.

✏️ Tip: You can customize this high-level summary in your review settings.

On macOS, GUI apps launched via Finder/Spotlight/Dock don't inherit
environment variables from the user's shell configuration. This causes
issues with custom ZDOTDIR configurations not being detected and PATH
missing user-installed tools.

This change uses the shell-env package to spawn a login shell at startup
and capture the user's full environment, persisting ZDOTDIR and PATH to
process.env before terminal initialization.

Fixes #701

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 12, 2026

Warning

Rate limit exceeded

@Kitenite has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ef40418 and fc307d9.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • apps/desktop/package.json
  • apps/desktop/src/main/lib/shell-env.test.ts
📝 Walkthrough

Walkthrough

Adds a new dependency and a startup utility that captures the user shell environment (ZDOTDIR and PATH) on macOS/Linux and injects it into the desktop process before agent/terminal hooks are initialized, with defensive error handling and tests for PATH merge behavior.

Changes

Cohort / File(s) Summary
Package Dependencies
apps/desktop/package.json
Added dependency shell-env (^4.0.1).
Startup Initialization
apps/desktop/src/main/index.ts
Imported and invoked ensureShellEnvVars() in startup paths before setupAgentHooks().
Shell Environment Utility
apps/desktop/src/main/lib/shell-env.ts
New module exporting ensureShellEnvVars() and mergePathFromShell(); resolves shell env on macOS/Linux, merges PATH and sets ZDOTDIR when appropriate, with timeout and graceful failure.
Tests
apps/desktop/src/main/lib/shell-env.test.ts
New unit tests covering mergePathFromShell() behaviors: prepending, deduplication, empty-path handling, and ordering.

Sequence Diagram(s)

sequenceDiagram
  participant App as Desktop Main
  participant Auth as AuthService / init
  participant ShellUtil as ensureShellEnvVars
  participant OS as User Shell (macOS/Linux)
  participant Agent as setupAgentHooks

  App->>Auth: initAppState() / authService.initialize()
  App->>ShellUtil: ensureShellEnvVars()
  ShellUtil->>OS: shellEnv() (capture login shell env)
  OS-->>ShellUtil: env (PATH, ZDOTDIR, ...)
  ShellUtil-->>App: set process.env (PATH, ZDOTDIR)
  App->>Agent: setupAgentHooks() (uses PATH/ZDOTDIR)
  Agent-->>App: agent hooks initialized
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped from terminal into the GUI,
gathered PATH and ZDOTDIR just for you.
On macOS cliffs and Linux trails,
I stitched your shell where GUI prevails,
so agents wake with familiar sails.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: resolving shell environment for macOS GUI apps, which is the primary objective of this PR.
Description check ✅ Passed The description covers the key sections from the template (Summary, Problem, Solution, Test plan) with clear context and links to the related issue #701.
Linked Issues check ✅ Passed The PR implements a code-based solution (shell-env package integration) that directly addresses the problem described in issue #701 by resolving shell environment variables for macOS GUI apps.
Out of Scope Changes check ✅ Passed All changes are in scope: adding shell-env dependency, implementing ensureShellEnvVars() function, integrating it into app startup, and adding comprehensive tests for PATH merge logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Kitenite and others added 2 commits January 12, 2026 18:09
- Add 5-second timeout to prevent app hang on slow/broken shell configs
- Merge PATH instead of replacing to preserve Electron runtime paths
- Use TTY detection instead of ZDOTDIR check for terminal launch detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/desktop/src/main/lib/shell-env.test.ts (1)

72-78: Consider adding a clarifying comment for the asymmetric filtering behavior.

This test documents that empty segments are filtered from shell input (for deduplication logic) but preserved in the original PATH. The expected result /new/path:/usr/bin::/bin retains the :: from the original.

This is reasonable behavior (avoid modifying user's PATH structure), but the test name "should filter empty path segments" might be slightly misleading since it only filters shell input, not the original PATH. A brief inline comment would improve clarity.

📝 Suggested clarifying comment
 		it("should filter empty path segments", () => {
+			// Empty segments are filtered from shell input for deduplication,
+			// but original PATH structure (including empty segments) is preserved
 			process.env.PATH = "/usr/bin::/bin";
 			const result = mergePathFromShell("/new/path:::/usr/bin");
 
 			expect(result).toBe(true);
 			expect(process.env.PATH).toBe("/new/path:/usr/bin::/bin");
 		});
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea25013 and ef40418.

📒 Files selected for processing (2)
  • apps/desktop/src/main/lib/shell-env.test.ts
  • apps/desktop/src/main/lib/shell-env.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/lib/shell-env.ts
🧰 Additional context used
📓 Path-based instructions (5)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/main/lib/shell-env.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/main/lib/shell-env.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Co-locate tests with implementation files using .test.ts or .test.tsx suffix

Files:

  • apps/desktop/src/main/lib/shell-env.test.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/main/lib/shell-env.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/main/lib/shell-env.test.ts
🧠 Learnings (2)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/main/index.ts : Load environment variables from monorepo root .env in desktop app with override: true before any imports in src/main/index.ts and electron.vite.config.ts

Applied to files:

  • apps/desktop/src/main/lib/shell-env.test.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/electron-router-dom.ts : Do not import Node.js modules like node:path or dotenv in electron-router-dom.ts and similar shared files - they run in both main and renderer processes

Applied to files:

  • apps/desktop/src/main/lib/shell-env.test.ts
🧬 Code graph analysis (1)
apps/desktop/src/main/lib/shell-env.test.ts (1)
apps/desktop/src/main/lib/shell-env.ts (1)
  • mergePathFromShell (9-21)
🔇 Additional comments (3)
apps/desktop/src/main/lib/shell-env.test.ts (3)

1-14: LGTM!

Clean test setup with proper environment isolation. The beforeEach/afterEach pattern correctly saves and restores process.env.PATH to prevent test pollution.


16-70: Good test coverage for core functionality and edge cases.

The test cases correctly verify:

  • Prepending behavior for new shell paths
  • No-op when all paths already exist
  • Preservation of existing runtime paths
  • Handling of empty/undefined PATH
  • Deduplication logic

All expectations align with the implementation logic.


80-86: LGTM!

Good test for verifying that the order of new entries from the shell is preserved when prepended to the existing PATH.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 13, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Fly.io Electric (Fly.io) View App
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document shell environment configuration for macOS GUI app

1 participant