fix(agent-adapter): support fish when loading the project shell environment - #486
fix(agent-adapter): support fish when loading the project shell environment#486adminliu-main wants to merge 1 commit into
Conversation
Greptile SummaryThe PR updates macOS project-environment probing to emit fish-compatible conditional syntax while preserving the existing POSIX command for other login shells.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/host/agent-adapter/src/shell-env.ts | Selects fish-compatible probe syntax for fish login shells while leaving the existing POSIX behavior unchanged elsewhere. |
| packages/host/agent-adapter/src/tests/shell-env.test.ts | Adds focused command-generation coverage for fish and representative POSIX shells. |
Reviews (4): Last reviewed commit: "fix(agent-adapter): support fish when lo..." | Re-trigger Greptile
There was a problem hiding this comment.
ℹ️ No blocking issues — the fix is correct, and I verified both emitted branches against a real fish. Two minor notes.
Reviewed changes — the single commit 1d7032d9, which makes the macOS login-shell environment probe emit fish-compatible syntax.
shellProbeCommand(shell, printEnv)extracted and exported (shell-env.ts:60-65), replacing the POSIX string previously inlined at theexecFilecall site. fish is detected withFISH_SHELL_PATTERN = /(?:^|\/)fish$/against the$SHELLpath.- fish gets
if …; …; else; …; end; every other shell keeps a byte-identical copy of the string it replaces, so zsh/bash/sh behavior is unchanged and the loud-failure contract is preserved. - Golden-string test added (
shell-env.test.ts:26-36) pinning both emitted strings.
ℹ️ The fish string is pinned by a string-equality test, so nothing executable guards it
The new test proves the fish branch is selected, but it compares a string to a string — it cannot prove the string is valid fish, and no CI job runs fish. I closed most of that gap by hand (evidence below), so this is not a request to add fish to CI; it is a note that the one remaining checklist item in the PR description is the part I could not reach, and that a future edit to either string is unguarded.
Technical details
# Verification evidence for the fish probe command
## What I confirmed independently
- **Both branches execute under real fish 3.7.0.** With a stub `direnv` on `PATH`, `command -v direnv` returns 0 and the `exec direnv exec "$PWD" …` branch runs; without it, the `else` branch runs. In both cases the marker-delimited JSON reaches stdout through `fish -i -l -c`.
- **`command -v` is still valid in current fish.** `src/builtins/command.rs` in fish `4.0.0` and in the latest tag `4.8.1` both carry `shortopts = "hasqv"` with `'v' => opts.find_path = true` and the comment `// -s and -v are aliases`. So the flag is not a 3.x-only affordance.
- **`fish -i` emits no stdout greeting under `-c`** — the greeting is driven from the interactive reader loop, which a batch `-c` invocation never enters. Observed output began directly at the marker. `parseShellEnvironment` would tolerate it regardless, since it slices between two occurrences of a random 32-hex marker.
- **`"$PWD"` is safe despite the daemon passing its own stale `PWD` in the child env.** `resolveShellEnvironment` spreads `baseEnv` (which carries the daemon's `PWD`) while setting a different `cwd`. fish guards this at `env/environment.rs` — it honors an inherited `PWD` only when `paths_are_same_file(incoming_pwd, ".")`, else falls back to `getcwd()`. Empirically fish, bash and dash all reported the `execFile` `cwd`, not the injected value. No fish-specific `direnv exec` misdirection.
- **`quoteShellArg`'s POSIX `'\''` escaping is fish-compatible** (fish treats `\'` outside quotes as a literal quote), and in practice never fires — the probe expression contains only double quotes.
- **No sibling site was missed.** `shell-env.ts:22` is the only place `$SHELL` receives a `-c` payload. `apps/daemon/src/pty/sidecar.ts:304-306` reads `$SHELL` but passes only `['-l']` with no synthesized command; `packages/host/engine/src/automation/shell-exec.ts:37-41` (`shell: true`) is Node's `/bin/sh`; `packages/host/engine/src/scripts/script-service.ts:189` hard-codes `/bin/sh`.
## What remains open
- A real Codex or Claude session started under fish *with the user's own `config.fish`* — the unchecked box in the PR description. My runs used a stock fish, so a user config that changes `$PWD`, shadows `direnv`, or exits early on a non-tty is still untested.
## Open questions for the human
- Is a shell-dialect matrix worth encoding anywhere, or is "fish only, everything else fails loudly" the intended long-term shape? The answer determines whether the next non-POSIX shell report is a one-line addition or a redesign.ℹ️ Nitpicks
packages/host/agent-adapter/AGENTS.md:20-24is the canonical prose for this path (the CODE-473 paragraph) and still describes the probe as simply "the user's login shell". One clause noting that the probe command is shell-dialect-specific — fish handled, other non-POSIX shells failing loudly — would save the next agent the trace intoshell-env.ts.
Claude Opus | 𝕏
2d2fbf5 to
1d7032d
Compare
1d7032d to
f71574b
Compare
There was a problem hiding this comment.
ℹ️ No new issues in the code itself — the force-push left the tree byte-identical to what I already reviewed. One reverted doc line inline.
Reviewed changes — the force-push from 2d2fbf5f to f71574b3, which rewrote the branch into a single commit.
- Collapsed the branch into one commit.
f71574b3carries the same parent as1d7032d9(2d66882c), so this was an amend-and-force-push rather than a rebase onto newermaster. - Reverted the
shellProbeCommanddoc line.git diff-tree -r -p 1d7032d9 f71574b3is empty — the tree matches the originally reviewed commit exactly, which means the only net change against the previously reviewed head is the loss of2d2fbf5f's+1/-1. - Left the fix itself untouched.
shellProbeCommand,FISH_SHELL_PATTERNand the golden-string test are byte-for-byte what I verified against a real fish last run, so none of that is re-litigated here.
ℹ️ A resolved review thread no longer matches the code, and GitHub can't be told
The shell-env.ts:59 thread from the previous review is still marked Resolved on GitHub, because clicking Commit suggestion resolved it — but the commit that resolved it is no longer on the branch. There is no un-resolve affordance, so that thread will keep asserting a fix that isn't in the tree. Worth knowing for the next reader more than for this PR: a resolved pullfrog thread is not evidence the code changed.
Technical details
# Resolved-thread state diverged from the tree after the force-push
## Affected sites
- `packages/host/agent-adapter/src/shell-env.ts:59` — holds the pre-suggestion wording again.
- Review thread `PRRT_kwDOS8Iqx86c4KKC` (comment id `3873279444`) — marked `RESOLVED`, asserting a change that is absent from `f71574b3`.
## Required outcome
- The doc line and the resolved thread agree with each other. Either re-apply the wording, or say the wording was rejected on the merits so the disagreement is on the record rather than looking like an accident.
## Open questions for the human
- Still unanswered from the initial review, and it is what this comment's wording actually communicates: is a shell-dialect matrix worth encoding somewhere, or is "fish only, everything else fails loudly" the intended long-term shape? That answer decides whether the next non-POSIX shell report is a one-line addition or a redesign.Claude Opus | 𝕏

Linear: CODE-643
问题(Problem)
macOS 上用 fish 做登录 shell 时,启动 Codex 会话失败,UI 只显示:
daemon 日志(
~/Library/Logs/LinkCode/main.log)里有真实 cause:根因(Root cause)
CODE-473 的 agent 子进程环境解析(
resolveShellEnvironment,packages/host/agent-adapter/src/shell-env.ts)会通过用户的登录 shell($SHELL)执行一段写死的 POSIX 命令来加载项目环境,并按需套一层direnv exec:fish 不认
if/then/else/fi(它要求if …; …; else; …; end),命令在解析阶段就挂了。该解析按设计大声失败、不回退(不回退到 launchd 的 PATH),于是adapter.start()抛错,被 orchestrator 包装成笼统的Agent failed to start。shell-env.ts的 darwin guard);Windows / Linux 直接复用 daemon 自身环境,不 spawn shell,PowerShell 不在此路径;修复(Fix)
把命令构造抽成
shellProbeCommand(shell, printEnv):if …; …; else; …; end);大声失败契约保留,未引入静默回退。
+2 文件,+24/-2。
验证(Verification)
tsc --build --noEmit packages/host/agent-adapter干净;vitest run shell-env.test.ts— 3/3(新增 fish / POSIX 语法断言);fish -i -l -c下都能执行(exec/"$PWD"/direnv exec均正常),并完整模拟 daemon 的调用方式成功解析出环境(PATH / HOME);zsh 实测走 POSIX 分支通过;pnpm check:ci干净(0 error);pnpm test有 4 个既存失败,在packages/foundation/common/src/node/__tests__/release-artifact.test.ts(artifact path escapes its isolated root,macOS/tmp→/private/tmp符号链接问题),在 cleanmaster上同样复现,与本次改动无关(fix(agent-adapter,engine): stream codex rollout reads and evict expired history caches #466 也有同样说明)。Checklist
pnpm check:ci与pnpm test均通过(4 个release-artifact既存失败与本次无关,见验证)WIRE_PROTOCOL_VERSION已 bump(不适用)