Skip to content

[codex] fix remote agent ssh working directories#1119

Open
jtalborough wants to merge 4 commits into
RunMaestro:mainfrom
jtalborough:codex/fix-ssh-remote-cwd
Open

[codex] fix remote agent ssh working directories#1119
jtalborough wants to merge 4 commits into
RunMaestro:mainfrom
jtalborough:codex/fix-ssh-remote-cwd

Conversation

@jtalborough

@jtalborough jtalborough commented Jun 22, 2026

Copy link
Copy Markdown

Summary

Fixes SSH remote-agent working directory handling so remote agents use the configured remote cwd instead of a locally expanded path. This covers both the generic SSH spawn wrapper and the desktop process:spawn SSH path, plus the new/edit agent UI paths that persist remote cwd overrides.

Root Cause

The desktop spawn handler still passed the local cwd into the SSH command builder even when sessionSshRemoteConfig.workingDirOverride was set. New remote-agent creation also stored the locally expanded working directory as workingDirOverride, so inputs like ~/git-projects could become /Users/.../git-projects and fail on the remote host.

Changes

  • Honor workingDirOverride in process:spawn and wrapSpawnWithSsh.
  • Expand remote ~/... paths through $HOME when building SSH commands.
  • Preserve the raw remote working directory from the new-agent form.
  • Allow editing the remote working directory for SSH agents in EditAgentModal.
  • Add regressions for command building, spawn wrapping, process IPC, new-agent creation, and edit-agent saves.

Validation

  • git diff --check
  • npm test -- NewInstanceModal.test.tsx process.test.ts ssh-command-builder.test.ts ssh-spawn-wrapper.test.ts
  • npm run build:main && npm run build:preload && npm run build:renderer && npm run build:cli
  • Packaged and codesigned locally before this PR prep, then user confirmed the rebuilt app fixed their remote-agent launch issue.

Notes

npm run build:renderer still emits the existing Vite CSS/chunk warnings; the build exits successfully.

Summary by CodeRabbit

  • New Features

    • Added an editable Remote Working Directory for SSH-enabled sessions, including required validation and support for tilde (~) paths.
  • Bug Fixes

    • Improved SSH process:spawn working-directory handling so remote commands/cd scripts use the resolved SSH working directory (with correct ~/$HOME expansion) and avoid leaking the local cwd.
    • Normalized local -C / --cwd=<path> behavior to preserve remote semantics.
    • File tree and git refresh operations now target the SSH remote directory.
    • When SSH is enabled but no matching remote is resolved, the operation now fails with a clear error.
  • Tests

    • Expanded SSH coverage for tilde expansion and remote cwd behavior across spawn and wrapper logic.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bb61b607-bfc9-4884-9919-b2f53bdfa803

📥 Commits

Reviewing files that changed from the base of the PR and between ecf0c76 and 48c4f82.

📒 Files selected for processing (1)
  • src/__tests__/renderer/components/NewInstanceModal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tests/renderer/components/NewInstanceModal.test.tsx

📝 Walkthrough

Walkthrough

Adds tilde-aware path escaping (shellEscapeRemotePath) in SSH command building, a normalizeSshWorkingDirArgs helper that replaces local cwd values with . in agent arguments, and remoteCwd propagation from workingDirOverride through the SSH spawn path. The renderer's EditAgentModal gains an editable remote working directory input with validation, and useFileTreeManagement gains SSH-aware root selection helpers.

Changes

SSH Remote Working Directory Override

Layer / File(s) Summary
Tilde-escape helper in ssh-command-builder
src/main/utils/ssh-command-builder.ts, src/__tests__/main/utils/ssh-command-builder.test.ts
Adds shellEscapeRemotePath() translating ~ to "$HOME" and ~/path to "$HOME"/path; wires it into buildRemoteCommand and buildSshCommandWithStdin for the cd line; two new test cases assert tilde-to-$HOME expansion for both execution modes.
remoteCwd and arg normalization in ssh-spawn-wrapper
src/main/utils/ssh-spawn-wrapper.ts, src/__tests__/main/utils/ssh-spawn-wrapper.test.ts
Adds WORKING_DIR_VALUE_FLAGS, WORKING_DIR_INLINE_PREFIXES, and normalizeSshWorkingDirArgs to rewrite local cwd flag arguments to .; computes remoteCwd from trimmed workingDirOverride; threads remoteArgs and remoteCwd into both SSH execution paths; new test module covers all four behavioral cases: override propagation and local cwd argument normalization.
process:spawn handler SSH cwd wiring
src/main/ipc/handlers/process.ts, src/__tests__/main/ipc/handlers/process.test.ts
Introduces isSshRemoteSpawn and agentWorkingDirArgCwd (. for SSH non-terminal) passed to buildAgentArgs; computes and passes remoteCwd to the SSH stdin-script builder and debug logging; adds hard failure when SSH remote resolution yields no effective config; three new SSH handler tests verify unresolved remote error, workingDirOverride routing, and no local cwd leakage in Codex spawns.
Renderer modals: editable remote working directory
src/renderer/components/NewInstanceModal/EditAgentModal.tsx, src/renderer/components/NewInstanceModal/NewInstanceModal.tsx, src/__tests__/renderer/components/NewInstanceModal.test.tsx, src/__tests__/renderer/components/NewInstanceModal/EditAgentModal.test.tsx
EditAgentModal derives remoteWorkingDir, validates non-empty trimmed value, and replaces read-only working directory with SSH-conditional editable FormInput for workingDirOverride; NewInstanceModal updates workingDirOverride fallback to trimmed remoteWorkingDir; tests update SSH regression expectations to tilde paths and add EditAgentModal tests for override editing and validation.
useFileTreeManagement SSH remote cwd root selection
src/renderer/hooks/git/useFileTreeManagement.ts, src/__tests__/renderer/hooks/useFileTreeManagement.test.ts
Adds getTreeRoot and getGitRoot helpers preferring sshContext.remoteCwd; applies them across refreshFileTree, refreshGitFileState, initial auto-load, and stats migration; tests add a refreshGitFileState SSH case and update existing SSH expectations to /remote/project.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EditAgentModal
  participant Handler as process:spawn<br/>Handler
  participant SshSpawn as wrapSpawnWithSsh
  participant Builder as buildSshCommandWithStdin
  User->>EditAgentModal: Enter remote working dir
  EditAgentModal->>EditAgentModal: Trim & validate input
  EditAgentModal->>Handler: Save with workingDirOverride
  Handler->>Handler: Compute remoteCwd from override
  Handler->>SshSpawn: Pass remoteArgs, remoteCwd
  SshSpawn->>Builder: Build SSH command with remoteCwd
  Builder->>Builder: Apply shellEscapeRemotePath
  Builder->>SshSpawn: Return sshStdinScript with $HOME expansion
  SshSpawn->>Handler: Return SSH-wrapped spawn config
  Handler->>Handler: Execute via SSH with remote cd
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • RunMaestro/Maestro#793: Both PRs modify SSH working-directory override handling to ensure the remote cd uses the overridden/remote path with correct tilde expansion/escaping, with added tests around workingDirOverride containing ~.
  • RunMaestro/Maestro#553: Both PRs modify the SSH stdin-script path generation—this PR adjusts remote cwd/tilde escaping in buildSshCommandWithStdin, while the related PR changes the same function to run non-interactive bash to prevent control-sequence leaks.

Suggested labels

ready to merge

🐇 A tilde is not just a squiggle, you see,
It blossoms to $HOME when hopping remote with glee!
The local cwd stays home, all snug in its burrow,
While remoteCwd leads the SSH trail straight and narrow.
No leaky paths, no tangled roots — just clean directory hops! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix remote agent ssh working directories' directly reflects the main change of the PR—correcting SSH remote working directory handling to use configured overrides instead of local expanded paths.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@jtalborough jtalborough force-pushed the codex/fix-ssh-remote-cwd branch from 8ac3b0b to f5cacd9 Compare June 22, 2026 13:19
@jtalborough jtalborough marked this pull request as ready for review June 22, 2026 13:45
@jtalborough jtalborough force-pushed the codex/fix-ssh-remote-cwd branch from f5cacd9 to fbba3d1 Compare June 22, 2026 13:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/utils/ssh-spawn-wrapper.ts (1)

20-29: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Handle inline working-dir flag forms in SSH arg normalization.

normalizeSshWorkingDirArgs() only rewrites two-token forms (e.g., --cwd /path). Inline forms like --cwd=/path or --working-directory=/path bypass this and can still leak local paths into remote commands.

Suggested patch
 const WORKING_DIR_VALUE_FLAGS = new Set(['-C', '--cwd', '--working-directory', '--directory']);
+const WORKING_DIR_INLINE_PREFIXES = ['--cwd=', '--working-directory=', '--directory='];

 function normalizeSshWorkingDirArgs(args: string[], localCwd: string): string[] {
 	return args.map((arg, index) => {
 		if (index > 0 && WORKING_DIR_VALUE_FLAGS.has(args[index - 1]) && arg === localCwd) {
 			return '.';
 		}
+		for (const prefix of WORKING_DIR_INLINE_PREFIXES) {
+			if (arg.startsWith(prefix) && arg.slice(prefix.length) === localCwd) {
+				return `${prefix}.`;
+			}
+		}
 		return arg;
 	});
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/utils/ssh-spawn-wrapper.ts` around lines 20 - 29, The
normalizeSshWorkingDirArgs function currently only handles two-token forms (flag
and value as separate arguments) but misses inline forms where the flag and
value are combined with an equals sign (e.g., --cwd=/path). Update the function
to also detect inline flag-value pairs by checking if the current argument
contains one of the WORKING_DIR_VALUE_FLAGS followed by an equals sign, extract
the path portion after the equals sign, and if it matches localCwd, replace it
with a dot while preserving the flag portion of the argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/main/ipc/handlers/process.test.ts`:
- Around line 2181-2184: The test assertion for the SSH stdin script is checking
for the old behavior where tilde paths remain as written (cd '~/git-projects'),
but the SSH command builder has been updated to expand tilde to the $HOME
environment variable. Update the .toContain() assertion that checks
spawnCall.sshStdinScript to expect the new format where ~/git-projects is
rendered as "$HOME"/git-projects instead of the literal tilde path.

In `@src/main/ipc/handlers/process.ts`:
- Around line 920-933: The code silently falls through when getSshRemoteConfig
returns a null config, allowing the spawn to run locally despite SSH being
explicitly enabled at the session level. After calling getSshRemoteConfig with
the sshStoreAdapter and sessionSshRemoteConfig parameters, add a validation
check to ensure sshResult.config is not null. If the config is null while SSH is
enabled (isSshRemoteSpawn is true and sessionSshRemoteConfig is present), throw
an error or log a fatal error and exit the process to fail loudly as per the
coding guidelines, rather than silently falling back to local execution.

---

Nitpick comments:
In `@src/main/utils/ssh-spawn-wrapper.ts`:
- Around line 20-29: The normalizeSshWorkingDirArgs function currently only
handles two-token forms (flag and value as separate arguments) but misses inline
forms where the flag and value are combined with an equals sign (e.g.,
--cwd=/path). Update the function to also detect inline flag-value pairs by
checking if the current argument contains one of the WORKING_DIR_VALUE_FLAGS
followed by an equals sign, extract the path portion after the equals sign, and
if it matches localCwd, replace it with a dot while preserving the flag portion
of the argument.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 83988e9c-727d-463c-9c2d-7f06913443ac

📥 Commits

Reviewing files that changed from the base of the PR and between 8a4cccf and fbba3d1.

📒 Files selected for processing (12)
  • src/__tests__/main/ipc/handlers/process.test.ts
  • src/__tests__/main/utils/ssh-command-builder.test.ts
  • src/__tests__/main/utils/ssh-spawn-wrapper.test.ts
  • src/__tests__/renderer/components/NewInstanceModal.test.tsx
  • src/__tests__/renderer/components/NewInstanceModal/EditAgentModal.test.tsx
  • src/__tests__/renderer/hooks/useFileTreeManagement.test.ts
  • src/main/ipc/handlers/process.ts
  • src/main/utils/ssh-command-builder.ts
  • src/main/utils/ssh-spawn-wrapper.ts
  • src/renderer/components/NewInstanceModal/EditAgentModal.tsx
  • src/renderer/components/NewInstanceModal/NewInstanceModal.tsx
  • src/renderer/hooks/git/useFileTreeManagement.ts

Comment thread src/__tests__/main/ipc/handlers/process.test.ts
Comment thread src/main/ipc/handlers/process.ts
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes SSH remote-agent working directory handling across the spawn IPC handler, the generic spawn wrapper, and the new/edit agent UI, so that workingDirOverride (including raw ~/... tilde paths) is used for the remote cd instead of the locally-expanded path.

  • Core fix (process.ts / ssh-spawn-wrapper.ts): remoteCwd is now derived from workingDirOverride and passed to buildSshCommandWithStdin/buildSshCommand; agent working-dir args (e.g., Codex -C) are normalized to . so the local path never appears on the remote.
  • Tilde expansion (ssh-command-builder.ts): A new shellEscapeRemotePath helper converts ~/… paths to \"$HOME\"/'rest' for safe, portable remote shell expansion.
  • UI (NewInstanceModal.tsx / EditAgentModal.tsx): workingDirOverride is now persisted as the raw user input (not locally expanded); the Edit modal gains an editable "Remote Working Directory" field for SSH agents.

Confidence Score: 3/5

The spawn-path and builder fixes are correct and well-tested, but the new editable Remote Working Directory field in EditAgentModal introduces a save path where clearing the input silently persists the locally-expanded projectRoot as workingDirOverride, causing every subsequent SSH agent spawn to fail with a cd error on the remote host.

The IPC handler and ssh-command-builder changes are solid. The EditAgentModal save logic has a gap: clearing the remote dir field produces a blank workingDirOverride that silently falls back to session.projectRoot (a locally-expanded path), which breaks the next agent spawn on the remote. Until that path is guarded or the empty-field case is handled explicitly, a user editing an SSH agent could unknowingly put it in a broken state with no warning in the UI.

src/renderer/components/NewInstanceModal/EditAgentModal.tsx — the handleSave fallback from empty workingDirOverride to session.projectRoot needs attention.

Important Files Changed

Filename Overview
src/main/ipc/handlers/process.ts Adds isSshRemoteSpawn flag to route working-dir arg and SSH cwd independently; now uses workingDirOverride as remoteCwd. Core logic is correct but the fallback `
src/main/utils/ssh-command-builder.ts Adds shellEscapeRemotePath to convert ~/... paths to "$HOME"/'rest' for correct remote expansion; applied in both buildRemoteCommand and buildSshCommandWithStdin. Logic and escaping are correct.
src/main/utils/ssh-spawn-wrapper.ts Adds normalizeSshWorkingDirArgs to replace local cwd-value flags with '.' and reads remoteCwd from workingDirOverride. The added else branch correctly normalizes args when there is no prompt. No issues found.
src/renderer/components/NewInstanceModal/EditAgentModal.tsx Adds editable Remote Working Directory field for SSH agents. The handleSave fallback sshRemoteConfig.workingDirOverride?.trim()
src/renderer/components/NewInstanceModal/NewInstanceModal.tsx Correctly stores workingDirOverride as the raw trimmed working dir (not locally-expanded), fixing the root bug for new-agent creation.
src/tests/main/ipc/handlers/process.test.ts New tests verify workingDirOverride routing and cwd='.' for agent args. The mock for buildSshCommandWithStdin wraps cwd naively in single quotes, so the tilde-path assertion checks mock behavior (cd '~/git-projects') rather than real behavior (cd "$HOME"/'git-projects').
src/tests/main/utils/ssh-command-builder.test.ts Adds correct assertions for tilde path expansion in both buildRemoteCommand and buildSshCommandWithStdin; assertions match the real shellEscapeRemotePath output.
src/tests/main/utils/ssh-spawn-wrapper.test.ts New test file covering workingDirOverride routing and arg normalization for both stdin and command-line prompt paths. Good coverage.
src/tests/renderer/components/NewInstanceModal/EditAgentModal.test.tsx New test exercises the editable remote working dir field through save; correctly asserts the raw user input is persisted as workingDirOverride.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as NewInstanceModal / EditAgentModal
    participant IPC as process:spawn handler
    participant Wrapper as wrapSpawnWithSsh
    participant Builder as ssh-command-builder
    participant SSH as SSH process

    UI->>IPC: "spawn cwd=localPath workingDirOverride=~/git-projects"
    IPC->>IPC: "agentWorkingDirArgCwd = dot (SSH spawn)"
    IPC->>IPC: "buildAgentArgs with cwd=dot produces args with -C dot"
    IPC->>IPC: "remoteCwd = workingDirOverride.trim() OR config.cwd"
    IPC->>Builder: "buildSshCommandWithStdin cwd=remoteCwd"
    Builder->>Builder: shellEscapeRemotePath produces dollar HOME slash git-projects
    Builder-->>IPC: stdinScript with cd dollar HOME git-projects OR exit 1
    IPC->>SSH: spawn ssh args stdinScript
    SSH->>SSH: bash -s reads stdinScript expands HOME executes agent

    Note over UI,IPC: wrapSpawnWithSsh path used by Group Chat etc.
    UI->>Wrapper: "wrapSpawnWithSsh args include -C localPath workingDirOverride=/remote/path"
    Wrapper->>Wrapper: normalizeSshWorkingDirArgs replaces localPath with dot
    Wrapper->>Wrapper: "remoteCwd = workingDirOverride"
    Wrapper->>Builder: "buildSshCommand cwd=remoteCwd args include -C dot"
    Builder-->>Wrapper: SSH command with cd /remote/path
    Wrapper-->>UI: wrapped spawn config
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as NewInstanceModal / EditAgentModal
    participant IPC as process:spawn handler
    participant Wrapper as wrapSpawnWithSsh
    participant Builder as ssh-command-builder
    participant SSH as SSH process

    UI->>IPC: "spawn cwd=localPath workingDirOverride=~/git-projects"
    IPC->>IPC: "agentWorkingDirArgCwd = dot (SSH spawn)"
    IPC->>IPC: "buildAgentArgs with cwd=dot produces args with -C dot"
    IPC->>IPC: "remoteCwd = workingDirOverride.trim() OR config.cwd"
    IPC->>Builder: "buildSshCommandWithStdin cwd=remoteCwd"
    Builder->>Builder: shellEscapeRemotePath produces dollar HOME slash git-projects
    Builder-->>IPC: stdinScript with cd dollar HOME git-projects OR exit 1
    IPC->>SSH: spawn ssh args stdinScript
    SSH->>SSH: bash -s reads stdinScript expands HOME executes agent

    Note over UI,IPC: wrapSpawnWithSsh path used by Group Chat etc.
    UI->>Wrapper: "wrapSpawnWithSsh args include -C localPath workingDirOverride=/remote/path"
    Wrapper->>Wrapper: normalizeSshWorkingDirArgs replaces localPath with dot
    Wrapper->>Wrapper: "remoteCwd = workingDirOverride"
    Wrapper->>Builder: "buildSshCommand cwd=remoteCwd args include -C dot"
    Builder-->>Wrapper: SSH command with cd /remote/path
    Wrapper-->>UI: wrapped spawn config
Loading

Reviews (1): Last reviewed commit: "fix remote agent ssh working directories" | Re-trigger Greptile

Comment on lines +336 to +337
workingDirOverride:
sshRemoteConfig.workingDirOverride || session?.projectRoot || undefined,
sshRemoteConfig.workingDirOverride?.trim() || session?.projectRoot || undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Clearing the remote dir falls back to the locally-expanded path

When a user opens this modal for an SSH session, edits the Remote Working Directory field, and then clears it, setSshRemoteConfig stores workingDirOverride: ''. At save time ''.trim() is '' (falsy), so the expression falls through to session?.projectRoot. For any session created with a tilde path such as ~/git-projects, session.projectRoot holds the locally-expanded value (e.g., /Users/jta/git-projects). That path is then stored as workingDirOverride, and on the next spawn the SSH script runs cd '/Users/jta/git-projects' || exit 1 on the remote host, which exits 1 and kills the agent.

Consider treating an empty field as a validation error (disable Save) or rejecting an empty submission rather than silently saving with the wrong path.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in ecf0c7690. EditAgentModal now treats an empty SSH remote working directory as invalid, disables Save, shows Remote working directory is required, and handleSave returns before calling onSave. When valid, it stores the trimmed remote directory directly instead of falling back to session.projectRoot, so clearing the field cannot persist a local Mac path. Added regression coverage in EditAgentModal.test.tsx; validation passed with focused tests, npm run lint, npm run lint:eslint, and git diff --check.

});
mockProcessManager.spawn.mockReturnValue({ pid: 12345, success: true });

const handler = handlers.get('process:spawn');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Test assertion diverges from real shellEscapeRemotePath output

The buildSshCommandWithStdin mock (line 113) generates cd '${remoteOptions.cwd}' || exit 1 — bare single-quoted path — rather than calling shellEscapeRemotePath. For a tilde path the real implementation produces cd "$HOME"/'git-projects' || exit 1, not cd '~/git-projects' || exit 1. This test verifies that the handler correctly routes workingDirOverride to the builder (good), but the assertion as written would still pass even if shellEscapeRemotePath were removed or broken. Tilde-expansion correctness is already covered in ssh-command-builder.test.ts.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in ecf0c7690. The buildSshCommandWithStdin mock now matches the real shellEscapeRemotePath tilde output for ~/git-projects, and the assertion expects cd "$HOME"/'git-projects' || exit 1. The production tilde contract remains covered in ssh-command-builder.test.ts; this handler test now verifies routing while staying faithful to the builder output.

@pedramamini

Copy link
Copy Markdown
Collaborator

@jtalborough thanks for the contribution, this is a clean, well-scoped fix for a genuinely annoying SSH remote-cwd bug.

I reviewed the PR alongside the Greptile and CodeRabbit reviews, and verified each finding against the current code rather than just the addressed-in replies:

  • Greptile P1 (EditAgentModal empty field): Resolved. Clearing the Remote Working Directory now disables Save, surfaces a "Remote working directory is required" error, and handleSave bails early. workingDirOverride is persisted as the trimmed value, so the old silent fallback to the locally-expanded session.projectRoot is gone.
  • CodeRabbit (fail loudly when SSH enabled but remote unresolved): Resolved. process:spawn now throws when getSshRemoteConfig returns no config instead of falling through to local execution.
  • Greptile P2 / CodeRabbit (test assertion drift): Resolved. The process.test.ts mock mirrors shellEscapeRemotePath and asserts cd "$HOME"/'git-projects', keeping the tilde-expansion contract covered.
  • Core fix: shellEscapeRemotePath (tilde to $HOME), normalizeSshWorkingDirArgs, the agentWorkingDirArgCwd = '.' routing, and the useFileTreeManagement getTreeRoot/getGitRoot helpers all hang together and target the remote cwd consistently.

I also ran the focused suite locally (process.test.ts, ssh-command-builder.test.ts, ssh-spawn-wrapper.test.ts, EditAgentModal.test.tsx, NewInstanceModal.test.tsx, useFileTreeManagement.test.ts) and all 300 tests pass. No merge conflicts against main.

Happy with this. Approving and tagging approved. Thanks again!

@jSydorowicz21

Copy link
Copy Markdown
Contributor

@codex review please

@jSydorowicz21

jSydorowicz21 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

In the mean time @jtalborough please retarget to rc branch. We give everything cook time in RC before we merge to main to avoid any wide spread issues

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48c4f82856

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// projectRoot (which is the remote path the user originally configured).
workingDirOverride:
sshRemoteConfig.workingDirOverride || session?.projectRoot || undefined,
workingDirOverride: trimmedRemoteWorkingDir,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep shared history on the saved remote cwd

When an SSH agent has syncHistory enabled and the user changes this new Remote Working Directory field, saving only updates sessionSshRemoteConfig.workingDirOverride; handleSaveEditAgent leaves session.cwd unchanged, and buildSharedHistoryContext() still uses session.cwd as the remote history path. The next turn will run the agent in trimmedRemoteWorkingDir, but .maestro/history is read and written under the old cwd, so synced history disappears or goes to the wrong remote project after an edit.

Useful? React with 👍 / 👎.

// projectRoot (which is the remote path the user originally configured).
workingDirOverride:
sshRemoteConfig.workingDirOverride || session?.projectRoot || undefined,
workingDirOverride: trimmedRemoteWorkingDir,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh cached project state after remote cwd edits

When an SSH session already has a loaded file tree or git refs, changing this new remote working directory only saves the override. The existing fileTree, fileTreeStats, isGitRepo, branches, and tags stay attached to the session, and useFileTreeManagement skips its initial load once fileTreeStats is present, so the Files/Git UI keeps showing the old remote directory until a manual refresh or restart. Clear those cached fields or trigger a refresh when the saved remote cwd changes.

Useful? React with 👍 / 👎.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants