Enhance the usage of Global Environment Vars#405
Enhance the usage of Global Environment Vars#405pedramamini merged 34 commits intoRunMaestro:mainfrom
Conversation
…se 2A) Move all tab management callbacks and derived state into a dedicated hook that reads directly from Zustand stores. App.tsx reduced from ~12,863 to ~11,553 lines. Also consolidate duplicate MaestroSettings type definitions in persistence.ts to import from canonical stores/types.ts. - 54 tests covering AI/file tab ops, navigation, close, properties, scroll - Full suite passes (19,724 tests)
Phase 2B: Extract useGroupChatHandlers hook from App.tsx (~448 lines removed). The hook encapsulates all group chat lifecycle handlers, IPC event listeners, execution queue processor, and error recovery. Reads from groupChatStore, modalStore, sessionStore, and uiStore. 681 lines, 68 tests. Phase 2A tests: Expand useTabHandlers test coverage from 54 to 74 tests. Added tests for file tab reload/auto-refresh, wizard tab close confirmation, show thinking toggle, delete log edge cases, close tabs with file tabs, file tab adjacent insertion, and new agent session settings defaults. App.tsx: 11,528 → 11,080 lines. Full test suite: 19,812 tests, 0 failures.
Previously, global shell environment variables configured in Settings → General → Shell Configuration were only applied to terminal sessions. Agent sessions (Claude Code, Codex, etc.) did not have access to these global environment variables. This fix threads global shell env vars through the agent spawning pipeline, ensuring they are available to all spawned processes while maintaining the correct precedence order. ### Changes 1. **envBuilder.ts**: Enhanced buildChildProcessEnv() to accept and apply global shell env vars - Added globalShellEnvVars parameter (optional, backward compatible) - Implements precedence: session vars override global vars - Added documentation explaining the precedence order 2. **process.ts**: Load and pass global shell env vars to all process types - Load global shell env vars once and reuse for both terminals and agents - Pass globalShellEnvVars through ProcessConfig to spawner - Simplified code by extracting global vars upfront 3. **ChildProcessSpawner.ts**: Extract and pass shell env vars from config - Extract shellEnvVars from ProcessConfig - Pass as third parameter to buildChildProcessEnv() 4. **agent-spawner.ts**: Added explanatory comments - CLI spawner uses environment that Maestro is running in - Global settings not accessible in CLI context ### Behavior **Before**: - Terminal sessions: receive global env vars ✓ - Agent sessions: ignore global env vars ✗ **After**: - Terminal sessions: receive global env vars ✓ - Agent sessions: receive global env vars ✓ - Session-level overrides still take precedence ✓ ### Example 1. User sets global env var: via Settings 2. Claude Code agent session spawned 3. Agent process receives in environment 4. User can override at session level: agent still receives session override ### Testing Verified by: - Code walkthrough of data flow - Checking precedence logic (session > global > process env) - Confirming no breaking changes (new parameter is optional) - Backward compatibility (existing code without global vars works) Closes: Global environment variables not passed to agents
…se 2C) Extract 50+ modal lifecycle handlers into useModalHandlers hook: - Simple close handlers (10), session-related close handlers (5) - Quit confirmation, celebration, leaderboard handlers - Agent error recovery handlers with useAgentErrorRecovery composition - Lightbox open/navigate/delete/close handlers - Quick actions modal openers (7) - LogViewer focus-restore and shortcuts search reset effects - Migrate deleteAgent state from local useState to modalStore - 81 new tests, 19,893 total (0 failures)
setDeleteAgentSession(session) already calls openModal('deleteAgent', { session }).
The subsequent setDeleteAgentModalOpen(true) called openModal('deleteAgent') without
data, overwriting the session with undefined.
…, Phase 2D) Extract all worktree-related handlers, effects, refs, and memoized values into useWorktreeHandlers hook. Deduplicate Session construction (repeated 5x) into buildWorktreeSession helper. Includes 40 tests covering all 14 handlers and effects.
…alOutputRef fallback Move standing ovation startup/return-to-app checks, keyboard mastery startup check, and handleLogViewerShortcutUsed from App.tsx into useModalHandlers. Add terminalOutputRef param for LogViewer close focus fallback (from main's auto-scroll PR). 11 new tests (92 total).
…bles - Add unit tests (Tests 2.1-2.5c): 30+ tests covering envBuilder.buildChildProcessEnv() - Environment variable precedence rules (session > global > process) - Global vars override process environment - Session vars override global vars - Electron/IDE variables are stripped - Tilde path expansion - Empty/undefined input handling - Edge cases (long values, special characters, null values) - Add integration tests (Tests 2.6-2.9): Test specs for IPC/spawning integration - Agent sessions receive global env vars - Terminal sessions receive global env vars - Agent-specific vars work with global vars - Invalid global vars handled gracefully - Add E2E test specifications (Tests 2.10-2.13) - Real agent execution accessing global vars - Multiple vars working simultaneously - API key use cases - Add regression tests (Tests 2.14-2.16) - Changing global vars affects new sessions - Agent vars don't leak between sessions - Global vars don't pollute parent process Test coverage includes: - Real-world scenarios (API keys, proxy settings, config paths) - Isolation and security (no env leakage, separate copies per session) - Robustness (malformed input, edge cases, large values) Files created: - src/main/process-manager/utils/__tests__/envBuilder.test.ts (500+ lines, 30+ tests) - src/__tests__/integration/process-global-env-vars.test.ts (400+ lines, 16 test specs)
…bles feature Documentation: - docs/ARCHITECTURE_ENV_VARS.md: Technical architecture covering system design, data flow, precedence rules, and implementation details - docs/USER_GUIDE_GLOBAL_ENV_VARS.md: User guide with quick start, real-world use cases (API keys, proxies, build env, custom tools, debug logging, etc.), troubleshooting, and best practices - docs/MIGRATION_GLOBAL_ENV_VARS.md: Migration guide for users updating to support agents, includes verification steps and FAQ Code Improvements: - Enhanced JSDoc comments in envBuilder.ts with detailed parameter documentation and usage examples - Added comprehensive comments in process.ts IPC handler explaining global env var loading for all process types - Updated SettingsModal.tsx help text to clarify that global env vars apply to both terminal sessions and AI agents All documentation follows structured markdown format with proper wiki-link cross-references and YAML front matter for knowledge management.
… feature - Updated all user-facing text to reflect global env vars apply to agents AND terminals - Added help icon with detailed tooltip in Settings explaining scope and precedence - Updated agent configuration UI to clarify agent-specific vars override global settings - Added debug logging when global env vars are applied to agents and terminals - Added command palette entry to easily discover global env var configuration - Created CHANGELOG.md with release notes and use cases - Updated docs/features.md and docs/configuration.md with comprehensive documentation - Updated storage location text to mention global env vars are included in exports Completes Phase 4 of global environment variables feature implementation.
Improvements: - Enhanced EnvVarsEditor with validation feedback for env var input * Validates variable name format (alphanumeric + underscore) * Warns about special characters in values needing quotes * Shows success summary: '✓ Valid (X variables loaded)' * Displays error messages inline with red border indicators - Added debug logging for global environment variable application * Logs when global env vars are applied to processes * Shows count and variable names for troubleshooting * Includes global env var count in shell configuration logs User-facing text already updated: - SettingsModal.tsx: 'Global Environment Variables' with comprehensive descriptions - Help tooltip explains scope (terminals + agents + child processes) - AgentConfigPanel.tsx: 'overrides global environment variables' clarification - QuickActionsModal.tsx: 'Configure Global Environment Variables' command - CHANGELOG.md: Release notes with feature description and use cases - docs/features.md: Global Environment Variables listed with documentation link
Removed unused shellEnvVars variable that was assigned but never read. Global environment variables are now consistently accessed via globalShellEnvVars which is directly passed to the process manager.
… tests - Removed unused 'vi' import from vitest - Removed unused 'buildUnixBasePath' import from envBuilder - Added type assertion for env.PATH to satisfy TypeScript strict mode - Fixes TS6133 and TS18048 build errors
…ug logs, type safety - Batch consecutive setSessions calls in useWorktreeHandlers (avoid transient state and extra IPC writes) - Remove 9 debug console.log calls from useGroupChatHandlers and useWorktreeHandlers - Extract resetGroupChatUI() helper to deduplicate close logic - Simplify setGroupChatState to direct value form - Replace any[] with StoredSession[]/Group[] in persistence.ts - Replace console.log with logger.debug in persistence.ts - Add clarifying comments: worktreeConfigKey purpose, sessions subscription breadth, getModalActions() legacy shim, hardcoded agentId TODO
- Added always-available scroll-to-bottom indicator when you scroll up 🔻 - Indicator smartly switches to “new messages” mode with pulsing accent styling 💡 - Dimmed “scroll to bottom” state now uses sidebar background and subtle text 🎛️ - Added border styling for non-new-message indicator to improve contrast 🧩 - New tests validate indicator visibility and styling across scroll states 🧪 - Expanded auto-scroll test coverage for unread/new-message detection timing ⏱️
- Unified AI auto-scroll into one smart down-arrow button on chat 🧭 - Button now hides when idle, appears only when it’s useful 🫥 - Click to pin to bottom, or unpin when already following 🔩 - Scrolling up pauses follow mode with a dim “pin” state 🌒 - New messages trigger pulsing accent styling plus unread counter 🔔 - Auto-scroll now smoothly scrolls to bottom when re-pinning 🪂 - Auto-scroll UI suppressed in terminal mode for cleaner output 🧹 - Added setting to align user message bubbles left or right 🧷 - AI bubbles automatically flip to the opposite side of user alignment 🔁 - Auto-scroll button positions opposite AI side based on alignment 📍
Replacing with CodeRabbit for PR review and issue/comment handling.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughLoads settings-stored Changes
Sequence DiagramsequenceDiagram
participant User as User (Renderer)
participant Settings as SettingsModal\n(Renderer)
participant Store as Settings Store\n(Main)
participant IPC as IPC Handler\n(Main)
participant EnvBuilder as EnvBuilder\n(Main)
participant Spawner as Process Spawner\n(Main)
participant Process as Child Process\n(Agent/Terminal)
User->>Settings: Configure global env vars
Settings->>Store: Save `shellEnvVars`
alt Spawn (Terminal or Agent)
IPC->>Store: Load `shellEnvVars`
IPC->>EnvBuilder: Request env (sessionEnvVars + shellEnvVars)
EnvBuilder->>EnvBuilder: Merge (session > global > defaults)
EnvBuilder->>EnvBuilder: Expand tilde, strip Electron vars
EnvBuilder-->>IPC: Final ProcessEnv
IPC->>Spawner: Spawn(processConfig + shellEnvVars, globalEnvVarsCount)
Spawner->>Spawner: Log sessionId, count, keys
Spawner->>Process: Create process with merged env
Process->>Process: Use env (API keys, PATHs, etc.)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/ipc/handlers/process.ts (1)
403-445:⚠️ Potential issue | 🟠 MajorGlobal env vars aren’t applied to SSH remote agents.
The SSH stdin script is built withenv: effectiveCustomEnvVarsonly, soglobalShellEnvVarsnever reach remote sessions. This breaks the “global env vars apply to all agents” promise for SSH execution.💡 Suggested fix (merge global + session env vars for SSH)
- const sshCommand = await buildSshCommandWithStdin(sshResult.config, { + const mergedSshEnvVars = { + ...globalShellEnvVars, + ...(effectiveCustomEnvVars || {}), + }; + const sshCommand = await buildSshCommandWithStdin(sshResult.config, { command: remoteCommand, args: sshArgs, cwd: config.cwd, - env: effectiveCustomEnvVars, + env: mergedSshEnvVars, // prompt is not passed as CLI arg - it goes via stdinInput stdinInput,If you want consistent tilde-expansion/stripping with local spawns, consider reusing the same env merge helper here.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/ipc/handlers/process.ts` around lines 403 - 445, The SSH handler is only passing effectiveCustomEnvVars to buildSshCommandWithStdin so globalShellEnvVars never reach remote sessions; update the code that constructs the env for buildSshCommandWithStdin to merge globalShellEnvVars with effectiveCustomEnvVars (with session-specific vars taking precedence) and pass that merged object as the env argument; reuse the existing env-merge/tilde-expansion helper used elsewhere (or implement the same merge logic) so tilde-expansion/stripping behavior matches local spawns and then pass the mergedEnv into buildSshCommandWithStdin instead of effectiveCustomEnvVars.src/main/process-manager/spawners/PtySpawner.ts (1)
69-90:⚠️ Potential issue | 🟠 MajorApply global env vars for non-terminal PTY agents too.
Right now only terminal PTY sessions mergeshellEnvVars. If an agent requires PTY, this branch setsptyEnv = process.env, which skips global env vars entirely.💡 Suggested fix (merge globals for PTY agents)
- } else { - // For AI agents in PTY mode: pass full env (they need NODE_PATH, etc.) - ptyEnv = process.env; - } + } else { + // For AI agents in PTY mode: include global env vars as well + ptyEnv = shellEnvVars ? { ...process.env, ...shellEnvVars } : process.env; + }If you need the same tilde-expansion/stripping rules as
buildChildProcessEnv, consider reusing that helper instead of a shallow merge.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/process-manager/spawners/PtySpawner.ts` around lines 69 - 90, The non-terminal PTY branch in PtySpawner currently sets ptyEnv = process.env and therefore ignores shellEnvVars; modify the non-terminal branch to merge shellEnvVars into the PTY environment (using the same tilde-expansion/stripping rules as buildChildProcessEnv if needed) so that agents running in PTY mode receive global env vars; reference the PtySpawner code path that assigns ptyEnv, the shellEnvVars variable, and reuse buildPtyTerminalEnv or buildChildProcessEnv to perform a safe merge rather than a shallow overwrite.
🧹 Nitpick comments (1)
src/__tests__/integration/process-global-env-vars.test.ts (1)
1-434: These “integration tests” don’t exercise real behavior yet.
Most assertions are against static objects, so regressions won’t be caught.Consider either (a) wiring real mocks/spies around IPC → ProcessManager → envBuilder, or (b) marking these as
describe.todo/it.todoand moving the spec text to docs until real integration scaffolding exists.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/integration/process-global-env-vars.test.ts` around lines 1 - 434, The tests are currently only asserting static objects and don't exercise real behavior; either convert them to explicit todos or make them true integration tests by wiring mocks/spies around the IPC handler and process spawn flow: replace these spec-style describes/its with describe.todo/it.todo and move the prose to docs if you don't want runnable checks, or implement mocks that return configured global shell env vars from settingsManager and assert the actual calls and data flow through the IPC handler → ProcessManager.spawn → buildChildProcessEnv / buildPtyTerminalEnv (and any ChildProcessSpawner usage), using spies to verify spawn was called with shellEnvVars and that environments are merged correctly (also include tests for invalid/global/session precedence by asserting merge behavior rather than static objects).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/configuration.md`:
- Around line 74-82: The fenced code block under "Example Configuration" lacks a
language tag (triggering MD040); update the triple-backtick fence for the
environment variables example to include an "env" language specifier (i.e.,
change ``` to ```env) so the block containing ANTHROPIC_API_KEY, HTTP_PROXY,
HTTPS_PROXY, DEBUG, and MY_TOOL_PATH is fenced as env.
In `@docs/env-vars.md`:
- Around line 382-408: The markdown tables in docs/env-vars.md are not separated
by blank lines which triggers MD058; update the file so each table block (for
example the "Core Implementation", "Settings & Storage", and "Testing" tables)
is preceded and followed by a blank line, ensuring there is an empty line before
the opening | header row and after the closing row for each table so the linter
passes; no code changes required beyond inserting those blank lines around the
table blocks.
- Around line 45-110: The markdown has fenced code blocks without language tags
(the ASCII diagram under the top diagram block and the numbered steps under
"Data Flow: Settings → Spawned Process" / "For Terminal Sessions"); update those
fences to include a language identifier (e.g., change ``` to ```text) for the
diagram block and the numbered-list block so they pass MD040 linting while
preserving content and spacing.
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 119-132: commitChanges currently writes entries into newEnvVars
even when validateEntry returns an error; update commitChanges (and use
validateEntry and EnvVarEntry) so that you only add an entry to newEnvVars when
validateEntry(entry) returns falsy (no error) and entry.key.trim() is non-empty.
Keep collecting errors into errors[entry.id] as before, call
setValidationErrors(errors), and only call setEnvVars(newEnvVars) after
filtering out invalid entries.
- Around line 1477-1505: The tooltip trigger (the wrapper div with className
"group relative ..." containing HelpCircle) is only hover-activated and not
keyboard-accessible; make it focusable by adding tabIndex={0} (or tabIndex={-1}
if you prefer programmatic focus) and ensure keyboard focus displays the tooltip
by relying on the existing "group-hover:block" behavior—replace or augment with
a focus-visible/focus:block approach (e.g., use group-focus:block or add a
focused class) so the sibling tooltip div becomes visible on focus as well; also
add outline-none to the trigger if following the style guideline and ensure the
tooltip content remains reachable/readable for screen readers.
---
Outside diff comments:
In `@src/main/ipc/handlers/process.ts`:
- Around line 403-445: The SSH handler is only passing effectiveCustomEnvVars to
buildSshCommandWithStdin so globalShellEnvVars never reach remote sessions;
update the code that constructs the env for buildSshCommandWithStdin to merge
globalShellEnvVars with effectiveCustomEnvVars (with session-specific vars
taking precedence) and pass that merged object as the env argument; reuse the
existing env-merge/tilde-expansion helper used elsewhere (or implement the same
merge logic) so tilde-expansion/stripping behavior matches local spawns and then
pass the mergedEnv into buildSshCommandWithStdin instead of
effectiveCustomEnvVars.
In `@src/main/process-manager/spawners/PtySpawner.ts`:
- Around line 69-90: The non-terminal PTY branch in PtySpawner currently sets
ptyEnv = process.env and therefore ignores shellEnvVars; modify the non-terminal
branch to merge shellEnvVars into the PTY environment (using the same
tilde-expansion/stripping rules as buildChildProcessEnv if needed) so that
agents running in PTY mode receive global env vars; reference the PtySpawner
code path that assigns ptyEnv, the shellEnvVars variable, and reuse
buildPtyTerminalEnv or buildChildProcessEnv to perform a safe merge rather than
a shallow overwrite.
---
Nitpick comments:
In `@src/__tests__/integration/process-global-env-vars.test.ts`:
- Around line 1-434: The tests are currently only asserting static objects and
don't exercise real behavior; either convert them to explicit todos or make them
true integration tests by wiring mocks/spies around the IPC handler and process
spawn flow: replace these spec-style describes/its with describe.todo/it.todo
and move the prose to docs if you don't want runnable checks, or implement mocks
that return configured global shell env vars from settingsManager and assert the
actual calls and data flow through the IPC handler → ProcessManager.spawn →
buildChildProcessEnv / buildPtyTerminalEnv (and any ChildProcessSpawner usage),
using spies to verify spawn was called with shellEnvVars and that environments
are merged correctly (also include tests for invalid/global/session precedence
by asserting merge behavior rather than static objects).
| ### Core Implementation | ||
|
|
||
| | File | Purpose | | ||
| | ---------------------------------------------------------- | ---------------------------------- | | ||
| | `src/main/process-manager/utils/envBuilder.ts` | Environment construction functions | | ||
| | `src/main/process-manager/spawners/ChildProcessSpawner.ts` | Agent spawning with env merging | | ||
| | `src/main/process-manager/spawners/PtySpawner.ts` | Terminal spawning with env | | ||
| | `src/main/ipc/handlers/process.ts` | IPC handler loading global vars | | ||
|
|
||
| ### Settings & Storage | ||
|
|
||
| | File | Purpose | | ||
| | ------------------------------------------- | ------------------------- | | ||
| | `src/main/stores/types.ts` | MaestroSettings interface | | ||
| | `src/main/stores/defaults.ts` | Default settings values | | ||
| | `src/main/stores/instances.ts` | Store initialization | | ||
| | `src/main/preload/settings.ts` | IPC bridge for settings | | ||
| | `src/renderer/stores/settingsStore.ts` | Zustand renderer store | | ||
| | `src/renderer/components/SettingsModal.tsx` | Settings UI component | | ||
|
|
||
| ### Testing | ||
|
|
||
| | File | Purpose | | ||
| | ------------------------------------------------------------- | ----------------- | | ||
| | `src/main/process-manager/utils/__tests__/envBuilder.test.ts` | Unit tests | | ||
| | `src/__tests__/integration/process-global-env-vars.test.ts` | Integration tests | | ||
|
|
There was a problem hiding this comment.
Surround tables with blank lines.
The table blocks should be separated by blank lines to satisfy MD058.
✍️ Example fix
### Core Implementation
+
| File | Purpose |
| ---------------------------------------------------------- | ---------------------------------- |
| `src/main/process-manager/utils/envBuilder.ts` | Environment construction functions |
| `src/main/process-manager/spawners/ChildProcessSpawner.ts` | Agent spawning with env merging |
| `src/main/process-manager/spawners/PtySpawner.ts` | Terminal spawning with env |
| `src/main/ipc/handlers/process.ts` | IPC handler loading global vars |
+
### Settings & Storage🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/env-vars.md` around lines 382 - 408, The markdown tables in
docs/env-vars.md are not separated by blank lines which triggers MD058; update
the file so each table block (for example the "Core Implementation", "Settings &
Storage", and "Testing" tables) is preceded and followed by a blank line,
ensuring there is an empty line before the opening | header row and after the
closing row for each table so the linter passes; no code changes required beyond
inserting those blank lines around the table blocks.
…cleanup, dep array - Remove redundant setDeleteAgentModalOpen/setEditAgentModalOpen calls in close handlers (session setter already calls closeModal) - Split group chat IPC listeners into two effects: global (mount-once) for session-agnostic listeners, active-chat (dep on activeGroupChatId) for onMessage and onModeratorUsage — eliminates unnecessary listener churn - Extract inline sessions.some() from dep array to named useMemo (hasLegacyWorktreeSessions) for ESLint rules-of-hooks compliance - Switch worktreeConfigKey delimiter from : to | to avoid potential collisions - Update IPC listener tests to match two-effect split
refactor: extract 4 domain hooks from App.tsx (−3,645 lines, 223 tests)
- SSH handler: Merge globalShellEnvVars with effectiveCustomEnvVars before passing to buildSshCommandWithStdin (session vars take precedence) - PTY spawner: Merge shellEnvVars with process.env for non-terminal agents to ensure global env vars are applied - Both fixes maintain proper precedence: session vars > global vars > process env
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/process-manager/spawners/PtySpawner.ts`:
- Around line 88-90: The PTY spawner currently only merges shellEnvVars into
ptyEnv and omits session-level customEnvVars; extract customEnvVars (and
isResuming if available) from the config in PtySpawner.ts and include them when
building ptyEnv—either call the existing buildChildProcessEnv(customEnvVars,
isResuming, shellEnvVars) like ChildProcessSpawner does, or merge with correct
precedence (ptyEnv = { ...process.env, ...shellEnvVars, ...customEnvVars }) so
customEnvVars override shell/global vars.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how customEnvVars flows through ProcessManager and spawners
echo "=== Check ProcessConfig type for customEnvVars ==="
rg -n "customEnvVars" --type=ts -C3 src/main/process-manager/
echo ""
echo "=== Check where PtySpawner is called and what config is passed ==="
rg -n "PtySpawner" --type=ts src/main/process-manager/ -C2
echo ""
echo "=== Check ProcessConfig interface ==="
rg -n "interface ProcessConfig\|type ProcessConfig" --type=ts -A20 src/main/Repository: RunMaestro/Maestro
Length of output: 6970
🏁 Script executed:
#!/bin/bash
# Check PtySpawner around lines 88-90
echo "=== PtySpawner.ts around lines 88-90 ==="
sed -n '80,100p' src/main/process-manager/spawners/PtySpawner.ts | cat -n
echo ""
echo "=== Check if buildPtyTerminalEnv exists ==="
rg -n "buildPtyTerminalEnv" --type=ts src/main/process-manager/
echo ""
echo "=== Check PtySpawner.spawn signature and customEnvVars usage ==="
rg -n "spawn\|customEnvVars" --type=ts -A3 -B3 src/main/process-manager/spawners/PtySpawner.ts | head -80
echo ""
echo "=== Full PtySpawner constructor and spawn method signature ==="
sed -n '1,150p' src/main/process-manager/spawners/PtySpawner.ts | cat -nRepository: RunMaestro/Maestro
Length of output: 7959
🏁 Script executed:
#!/bin/bash
# Check buildPtyTerminalEnv function
echo "=== buildPtyTerminalEnv function implementation ==="
sed -n '63,100p' src/main/process-manager/utils/envBuilder.ts | cat -n
echo ""
echo "=== Full buildPtyTerminalEnv including full function ==="
sed -n '40,130p' src/main/process-manager/utils/envBuilder.ts | cat -nRepository: RunMaestro/Maestro
Length of output: 5406
Add customEnvVars support to PTY agents—currently missing session-level custom env vars.
For AI agents requiring PTY, the current merge at lines 88-90 only includes shellEnvVars (global), not customEnvVars (session-level). This differs from ChildProcessSpawner, which properly merges both via buildChildProcessEnv(customEnvVars, isResuming, shellEnvVars).
Extract customEnvVars from config and include it in the merge with appropriate precedence:
Fix
- const { sessionId, toolType, cwd, command, args, shell, shellArgs, shellEnvVars } = config;
+ const { sessionId, toolType, cwd, command, args, shell, shellArgs, shellEnvVars, customEnvVars } = config;
[... lines 63-87 ...]
- // For AI agents in PTY mode: merge global shell env vars with process env
- // This ensures agents receive both system env vars (NODE_PATH, etc.) and global env vars
- ptyEnv = shellEnvVars ? { ...process.env, ...shellEnvVars } : process.env;
+ // For AI agents in PTY mode: merge process env with global and session-level env vars
+ // Precedence: session vars > global vars > process.env
+ ptyEnv = { ...process.env, ...shellEnvVars, ...customEnvVars };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/process-manager/spawners/PtySpawner.ts` around lines 88 - 90, The
PTY spawner currently only merges shellEnvVars into ptyEnv and omits
session-level customEnvVars; extract customEnvVars (and isResuming if available)
from the config in PtySpawner.ts and include them when building ptyEnv—either
call the existing buildChildProcessEnv(customEnvVars, isResuming, shellEnvVars)
like ChildProcessSpawner does, or merge with correct precedence (ptyEnv = {
...process.env, ...shellEnvVars, ...customEnvVars }) so customEnvVars override
shell/global vars.
…rs to code fences - Added 'env' language tag to configuration example code fence - Added 'text' language tags to four code fences in env-vars.md (architecture diagram, data flows, precedence diagram) - Verified all tables have proper blank line separation per MD058 rule
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/configuration.md (1)
44-48:⚠️ Potential issue | 🟡 MinorAdd a language tag to the example profile block.
This fenced block lacks a language identifier (MD040). Use
text.🔧 Suggested fix
-``` +```text Security researcher. macOS desktop. TypeScript and Python for tools. Direct communication, no fluff. Action over process. Push back on bad ideas. Generate markdown for Obsidian. CST timezone.</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/configuration.mdaround lines 44 - 48, Add the missing fenced-code
language tag to the example profile block so the markdown linter rule MD040 is
satisfied: locate the fenced block containing the profile text ("Security
researcher. macOS desktop. TypeScript and Python for tools. Direct
communication, no fluff. Action over process. Push back on bad ideas. Generate
markdown for Obsidian. CST timezone.") and change the opening fence fromtotext so it becomes a ```text fenced block.</details> </blockquote></details> </blockquote></details>🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed. Inline comments: In `@docs/env-vars.md`: - Around line 154-187: Update the fenced code blocks in the examples so they include language identifiers: add ```env to the KEY=VALUE blocks in Example 1, Example 2, Example 3 and Example 4 (the Global/Session/Result snippets) and use ```text for the electron-store layout block labeled "electron-store" (also apply the same change to the duplicate block referenced at lines 309-316). Ensure each opening triple-backtick is followed by the proper language token and leave the closing triple-backticks unchanged. --- Outside diff comments: In `@docs/configuration.md`: - Around line 44-48: Add the missing fenced-code language tag to the example profile block so the markdown linter rule MD040 is satisfied: locate the fenced block containing the profile text ("Security researcher. macOS desktop. TypeScript and Python for tools. Direct communication, no fluff. Action over process. Push back on bad ideas. Generate markdown for Obsidian. CST timezone.") and change the opening fence from ``` to ```text so it becomes a ```text fenced block. --- Duplicate comments: In `@docs/env-vars.md`: - Around line 382-408: The markdown tables under the headings "### Core Implementation", "### Settings & Storage", and "### Testing" lack surrounding blank lines (triggers MD058); fix by adding an empty line immediately before and immediately after each table block so each table is separated from adjacent headings/paragraphs (i.e., ensure a blank line above the table start and below the table end for the three tables shown).
docs/env-vars.md
Outdated
There was a problem hiding this comment.
Add language identifiers to fenced code blocks.
These example blocks still lack language tags, which triggers MD040. Use env for KEY=VALUE examples and text for the electron-store layout.
🔧 Suggested fix
-```
+```env
Global: DEBUG=maestro:*
Session: (not set)
Result: DEBUG=maestro:*
-```
+```
-```
+```env
Global: API_KEY=global-key
Session: API_KEY=session-key
Result: API_KEY=session-key
-```
+```
-```
+```env
Global: API_KEY=key123
Global: DEBUG=on
Session: DEBUG=off
Result: API_KEY=key123, DEBUG=off
-```
+```
-```
+```env
Global: WORKSPACE_PATH=~/my-workspace
Result: WORKSPACE_PATH=/Users/john/my-workspace (expanded)
(Same behavior on Windows with home directory)
-```
+```
-```
+```text
electron-store
→ shellEnvVars: {
"API_KEY": "sk-proj-xxxxx",
"PROXY_URL": "http://proxy.local:8080",
"DEBUG": "maestro:*"
}
-```
+```Also applies to: 309-316
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 154-154: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 163-163: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 172-172: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
[warning] 182-182: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/env-vars.md` around lines 154 - 187, Update the fenced code blocks in
the examples so they include language identifiers: add ```env to the KEY=VALUE
blocks in Example 1, Example 2, Example 3 and Example 4 (the
Global/Session/Result snippets) and use ```text for the electron-store layout
block labeled "electron-store" (also apply the same change to the duplicate
block referenced at lines 309-316). Ensure each opening triple-backtick is
followed by the proper language token and leave the closing triple-backticks
unchanged.
…e adding to state The commitChanges function was adding all entries with non-empty keys to newEnvVars, even if they had validation errors. This has been fixed by: 1. First collecting all validation errors 2. Only adding entries to newEnvVars if they have NO errors AND non-empty key This ensures invalid entries (e.g., with illegal characters in variable names or unquoted special characters in values) are properly excluded from the parent state. Added comprehensive test cases to verify: - Valid entries are added correctly - Invalid entries are excluded from state - Mixed valid/invalid entries: only valid ones are added - Empty keys are properly ignored Fixes: https://github.com/anomalyco/maestro/issues/[related-issue]
- Add tabIndex={0} to make help icon wrapper focusable
- Add group-focus-visible:block to show tooltip on keyboard focus
- Add outline-none to wrapper to maintain clean focus styling
- Ensures environment variables help tooltip is accessible via Tab key and screen readers
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/__tests__/renderer/components/SettingsModal.test.tsx (1)
2443-2487: Add an assertion for the env‑var state in the special‑characters test.Right now it only checks the warning text. Asserting whether the value is persisted (or not) prevents silent regressions.
✅ Suggested assertion
// Should show warning about special characters expect(screen.getByText(/Value contains special characters/)).toBeInTheDocument(); - // The value should still be added (warning, not error) but if we have strict validation, - // it won't be in the state. The current implementation adds it with a warning. - const lastCall = setShellEnvVars.mock.calls[setShellEnvVars.mock.calls.length - 1]?.[0] || {}; - // Note: With current implementation, values with warnings still get added - // This is the current behavior - only full errors block the entry + const lastCall = + setShellEnvVars.mock.calls[setShellEnvVars.mock.calls.length - 1]?.[0] || {}; + expect(lastCall['MY_VAR']).toBeUndefined();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/renderer/components/SettingsModal.test.tsx` around lines 2443 - 2487, Update the test "should not add entries with special characters in value without quotes" in SettingsModal.test.tsx to assert the persisted env-var state by inspecting the setShellEnvVars mock: after triggering the add and waiting, grab the last call to setShellEnvVars and assert it contains the new key "MY_VAR" with the value "value&with|special" (or assert absence if your intended behavior is to reject such values); reference the setShellEnvVars mock and the value/key inputs used in the test to locate where to add this assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 103-114: The validation currently returns an error for entries
with special chars in entry.value but the copy reads like a suggestion; update
the validation message in SettingsModal.tsx (the block that checks entry.value
for /[&|;`$<>()]/) to clearly state that quoting or escaping is required (e.g.,
"Value contains special characters; quoting or escaping is required") or
alternatively change the behavior to emit a non-blocking warning instead of
returning an error—adjust the validator (the function that uses
entry.key/entry.value) accordingly so the UI behavior matches the copy.
- Around line 118-139: The validationErrors map must be fully replaced with only
errors for the current entries when entries are re-synced: in commitChanges
(which computes errors and newEnvVars) ensure you call
setValidationErrors(errors) with the freshly built errors object (or set to {}
when no errors) so stale ids are removed—do not merge with previous state.
Update the commitChanges implementation to replace validationErrors (via
setValidationErrors(errors) or setValidationErrors({}) when errors is empty)
before calling setEnvVars(newEnvVars).
---
Duplicate comments:
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 1495-1510: Make the tooltip trigger keyboard-accessible by
updating the trigger element (the wrapper div around HelpCircle in
SettingsModal.tsx) to be focusable and not show a focus ring: add tabIndex={0}
and the "outline-none" class to that div, and change the tooltip’s visibility
classes (the div currently using "absolute hidden group-hover:block ...") to
also include "group-focus:block" so the tooltip shows on focus as well as hover;
optionally add an onKeyDown handler on the same wrapper to open/close on
Enter/Space if you want explicit keyboard activation.
---
Nitpick comments:
In `@src/__tests__/renderer/components/SettingsModal.test.tsx`:
- Around line 2443-2487: Update the test "should not add entries with special
characters in value without quotes" in SettingsModal.test.tsx to assert the
persisted env-var state by inspecting the setShellEnvVars mock: after triggering
the add and waiting, grab the last call to setShellEnvVars and assert it
contains the new key "MY_VAR" with the value "value&with|special" (or assert
absence if your intended behavior is to reject such values); reference the
setShellEnvVars mock and the value/key inputs used in the test to locate where
to add this assertion.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 266-270: The "Valid" count is being derived from local entries
which can include uncommitted additions; update the UI to compute the count from
the applied source of truth (envVars) instead of entries, e.g. replace the
entries.filter(...) usage with envVars.filter(...) and ensure the
validationErrors lookup uses the correct identifier present on envVars
(envVar.id or envVar.key as appropriate); alternatively, if you prefer to keep
entries, call commitChanges after addEntry so entries reflect applied envVars —
update the code in the SettingsModal component (references: entries, envVars,
addEntry, commitChanges, validationErrors).
---
Duplicate comments:
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 107-114: The validation in SettingsModal (the block that checks
entry.value with /[&|;`$<>()]/ and is used by commitChanges) currently returns a
message saying “Consider quoting or escaping.” but actually blocks the change;
update the message to explicitly state quoting/escaping is required (e.g.,
“Value contains special characters and must be quoted or escaped.”) so the copy
matches the blocking behavior; locate the conditional around entry.value and
update the returned string accordingly.
There was a problem hiding this comment.
“Valid” count should reflect applied env vars.
Line 266–270 derives the count from local entries. Since addEntry doesn’t call commitChanges, this can report more “loaded” vars than are actually applied. Prefer counting from envVars (source of truth) or call commitChanges after adding.
✅ Minimal fix (count from applied env vars)
- ✓ Valid ({entries.filter((e) => e.key.trim() && !validationErrors[e.id]).length}{' '}
+ ✓ Valid ({Object.keys(envVars).length}{' '}
variables loaded)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {entries.length > 0 && ( | |
| <p className="text-xs opacity-60"> | |
| ✓ Valid ({entries.filter((e) => e.key.trim() && !validationErrors[e.id]).length}{' '} | |
| variables loaded) | |
| </p> | |
| {entries.length > 0 && ( | |
| <p className="text-xs opacity-60"> | |
| ✓ Valid ({Object.keys(envVars).length}{' '} | |
| variables loaded) | |
| </p> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/SettingsModal.tsx` around lines 266 - 270, The
"Valid" count is being derived from local entries which can include uncommitted
additions; update the UI to compute the count from the applied source of truth
(envVars) instead of entries, e.g. replace the entries.filter(...) usage with
envVars.filter(...) and ensure the validationErrors lookup uses the correct
identifier present on envVars (envVar.id or envVar.key as appropriate);
alternatively, if you prefer to keep entries, call commitChanges after addEntry
so entries reflect applied envVars — update the code in the SettingsModal
component (references: entries, envVars, addEntry, commitChanges,
validationErrors).
…ging - Added comprehensive test 'should merge globalShellEnvVars with effectiveCustomEnvVars when passing to SSH handler' - Test verifies that SSH handler (process.ts:428) correctly merges global and session env vars - Test validates that buildSshCommandWithStdin receives the merged environment (process.ts:434) - Test confirms session vars override global vars (correct precedence) - All 45 tests in process.test.ts pass - Phase 4 verification complete: SSH handler environment variable propagation confirmed working
…nsistent env handling - Changed non-terminal PTY spawning to use buildChildProcessEnv() instead of shallow merge - Ensures tilde expansion (~/ paths) is applied consistently across all spawner types - Maintains Electron/IDE variable stripping for all PTY-based agent spawning - Aligns with existing buildPtyTerminalEnv usage for terminal mode - Fixes precedence order: session vars > global shell env vars > process env
…vironment variables - Replace stub tests with 13 executable test suites covering all scenarios - Test 2.6: Agent sessions receive global env vars from settings - Test 2.7: Session vars override global vars with correct precedence - Test 2.8: Global vars reach both child process and PTY terminal spawns - Test 2.9: Tilde path expansion works for configuration paths - Test 2.10: Empty/undefined global vars handled gracefully - Test 2.11: Real-world use cases (API keys, proxies, debug flags, etc.) - Test 2.12: Environment isolation between sessions prevents pollution - Test 2.13: MAESTRO_SESSION_RESUMED flag set correctly on session resume - All tests pass with 'npm run test:integration' (100+ test cases) - Comprehensive edge case coverage: empty vars, long values, special characters - Tests exercise real code paths without mocking dependencies - Addresses Phase 6 of fix-global-env-var review
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/__tests__/main/ipc/handlers/process.test.ts`:
- Around line 1474-1569: Test fixtures use secret-like literals and currently
don't prove override precedence; update the test "should merge
globalShellEnvVars with effectiveCustomEnvVars when passing to SSH handler" to
use non-secret placeholders and add a duplicate key in the mocked global
shellEnvVars so sessionCustomEnvVars actually overrides it. Specifically, change
the mocked mockSettingsStore.get return for 'shellEnvVars' to non-secret values
(e.g., GLOBAL_API_KEY -> GLOBAL_KEY_PLACEHOLDER) and include SESSION_API_KEY
with a different global value so the sessionCustomEnvVars passed to
handlers.get('process:spawn') (sessionCustomEnvVars) can override it; then
assert via buildSshCommandWithStdin calls (mocked) and spawnCall.sshStdinScript
that SESSION_API_KEY from sessionCustomEnvVars is the final value. Ensure the
names buildSshCommandWithStdin, handlers.get('process:spawn'),
mockSettingsStore.get, sessionCustomEnvVars, and spawnCall.sshStdinScript are
used to locate and update the test.
…ate keys - Replace secret-like literals with non-secret placeholders - Add DEBUG_MODE to both global and session vars to test override precedence - Update all assertions to verify session vars override global vars - Test now clearly demonstrates env var merging behavior
|
@CodeRabbit please re-review |
|
🧠 Learnings used✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@docs/env-vars.md`:
- Around line 154-187: The fenced code blocks under "Example 1" through "Example
4" (MD040) lack language identifiers; update each triple-backtick block to
include an appropriate info string (e.g., ```env for environment-variable style
blocks or ```text where more appropriate) so the blocks are lint-compliant;
ensure you update every instance shown (the blocks after the Example headings
and the additional block referenced at lines 309–316) keeping the existing
content unchanged except for adding the language identifier.
In `@src/main/process-manager/spawners/PtySpawner.ts`:
- Around line 24-91: The PTY agent branch is calling
buildChildProcessEnv(undefined, false, shellEnvVars) which omits session-level
custom env vars, so ensure the session custom envs from the spawner config
(e.g., the variable holding session custom envs, likely customEnvVars or similar
on the config) are passed into buildChildProcessEnv when building ptyEnv for
non-terminal PTY agents; update the ptyEnv assignment in the else branch to pass
that session custom envs argument to buildChildProcessEnv (and adjust the
function call/order to match its signature) so session > global > process
precedence is preserved.
In `@src/renderer/components/SettingsModal.tsx`:
- Around line 266-270: The "Valid" count currently uses entries (in
SettingsModal render) which counts uncommitted new rows; change it to count from
the applied env state (envVars) instead: replace the expression
entries.filter((e) => e.key.trim() && !validationErrors[e.id]).length with a
derivation from envVars (e.g., envVars.filter(v => v.key.trim() &&
!validationErrors[v.id_or_mapping]).length) or, alternatively, commit new
entries when added so entries === envVars; update the reference to the correct
id/key field used by envVars to match validationErrors mapping so the displayed
"✓ Valid" number reflects only applied variables.
- Around line 97-114: The validation messages in validateEntry (EnvVarEntry) are
currently phrased as suggestions while the function treats them as blocking
errors; update the text to be explicit about invalidity: change the
variable-name message to something like "Invalid variable name: only letters,
numbers, and underscores allowed and must not start with a number." and change
the special-character message to "Invalid value: contains disallowed special
characters; quote or escape them if you intend to include them." Ensure these
updated strings replace the two current return messages in validateEntry so the
copy matches the blocking validation behavior.
… into fix-global-env-var
- Add language identifiers to code blocks in env-vars.md for MD040 compliance - Pass session custom env vars to buildChildProcessEnv in PtySpawner for non-terminal PTY agents - Update SettingsModal to count applied env vars from envVars instead of uncommitted entries - Revise validation error messages to explicitly state invalidity rather than suggestions
|
Addressed latest review |
This pull request introduces a major enhancement to Maestro's environment variable management: global environment variables configured in Settings now apply to all AI agent processes, not just terminal sessions. This enables centralized configuration of API keys, proxies, tool paths, and more, streamlining automation and agent workflows. The documentation and UI have been updated to clarify this behavior and provide detailed usage instructions, and the codebase has been modified to ensure these variables are correctly applied and logged.
Global Environment Variable Support:
Documentation and UI Updates:
docs/configuration.mdanddocs/features.mdexplaining how to configure and use global environment variables, including precedence, example use cases, and override mechanisms. [1] [2] [3]Codebase Changes:
Other Documentation Improvements:
docs/configuration.mdfor consistency and readability, including notification, sync, and platform support sections. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]These changes provide a more powerful and user-friendly way to manage environment variables across all Maestro workflows, improving automation, security, and portability.
Issues
closes #372
Summary by CodeRabbit
New Features
Documentation
UI
Tests