You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I will write this issue in English (see our Language Policy)
I have searched existing issues to avoid duplicates
I am using the latest version of oh-my-openagent
I have read the documentation or asked an AI coding agent with this project's GitHub URL loaded and couldn't find the answer
Bug Description
On native Windows 11 with Git for Windows (MSYS2/MINGW64), an agent that starts a long-lived dev server and then verifies it (e.g. npm run dev → Playwright) reliably deadlocks. The agent issues the standard POSIX idiom:
nohup npm run dev &
The bash tool call never returns. The dev server itself is running, but the harness blocks forever waiting for EOF on the caller's stdout pipe, so the agent never reaches the Playwright verification step and the whole session stalls.
Root cause: MSYS job control does not extend to native Windows children. MSYS & / nohup are implemented on top of CreateProcess, not fork(), so they provide no real process-group detachment from a native Windows child. The backgrounded child inherits and keeps open the write end of the parent shell's stdout pipe for its entire lifetime, so any consumer waiting for EOF on that pipe waits until the server exits.
This is the same failure class tracked upstream as affaan-m/ECC#2489 ("Git Bash nohup/& reaped immediately"). Note the observable symptom differs: in that issue the detached child dies; here the caller hangs. Both stem from the same root cause — MSYS & does not truly detach a native Windows child.
OMO currently ships zero Windows-specific guidance for backgrounding processes:
packages/omo-opencode/src/hooks/non-interactive-env/constants.ts — SHELL_COMMAND_PATTERNS.banned covers editors, pagers and REPLs, but has no entry for nohup / background / detach patterns, and workarounds has no Windows equivalent.
packages/omo-opencode/src/hooks/non-interactive-env/non-interactive-env-hook.ts:89-92 — the tool.execute.before hook only prepends env vars for commands matching /\bgit\b/; non-git commands get no treatment at all.
Grepping nohup, Start-Process, taskkill across packages/prompts-core/prompts/, packages/shared-skills/, .opencode/ and .agents/ returns nothing relevant (the sole Start-Process hit is packages/shared-skills/skills/ast-grep/tests/smoke.ps1:18). packages/prompts-core/prompts/ contains no win32/windows guidance at all.
So the agent has nothing to steer it away from nohup ... &, and falls into this trap every time on Windows.
Interestingly, packages/git-bash-mcp/src/runner.ts already solved the sibling problem: runGitBashCommand() deliberately spawns with stdio: ["ignore", stdoutFd, stderrFd] using temp-file fds "to avoid Windows pipe-buffer deadlocks". But git-bash-mcp is Codex Light edition only — it is not registered by the OpenCode Ultimate edition, which uses tmux-based interactive_bash. The knowledge exists in the repo but never reaches the OpenCode agent's bash tool.
Steps to Reproduce
Windows 11 native, Git for Windows installed (uname -s → MINGW64_NT-10.0-26200).
Run OMO (OpenCode Ultimate edition) in any project with a long-lived dev server script (npm run dev, Vite/Next/etc.).
Ask the agent to: "start the dev server, then verify the page with Playwright."
The agent emits, via the bash tool:
nohup npm run dev &
Observe: the tool call never returns. The port is listening, but the session is stuck.
Variant that also hangs, and is worth calling out because agents reach for it naturally:
PID=$(powershell -NoProfile -Command "$p = Start-Process ... -PassThru; $p.Id")
Command substitution $(...) waits for pipe EOF, which a detached child never sends — so even the correct launcher blocks when wrapped in $(...).
Expected Behavior
An agent on native Windows should be able to start a long-lived process and immediately continue to the next step (Playwright verification, health check, etc.).
Concretely, I'd expect OMO to do at least one of:
Warn/rewrite at the bash tool boundary. Extend the non-interactive-env hook so that on process.platform === "win32", a command matching nohup ... & (or a bare trailing & on a long-lived command) either gets a output.message warning with the correct Windows recipe, or is rewritten to a Start-Process launcher. This mirrors the existing detectBannedCommand() warning path and the existing Windows-aware branch in detectCommandShellType() (non-interactive-env-hook.ts:43-66), so the plumbing is already there.
Document the platform recipe in the prompts. Add Windows background-process guidance to packages/prompts-core/prompts/ so agents know the correct idiom without having to rediscover it.
The recipe that actually works, measured on MINGW64_NT-10.0-26200 (2026-08-28):
powershell -NoProfile -Command "$p = Start-Process -FilePath 'node'
-ArgumentList 'server.js' -WorkingDirectory 'C:\path\to\dir'
-WindowStyle Hidden -RedirectStandardOutput 'srv.log'
-RedirectStandardError 'srv.err' -PassThru;
$p.Id | Set-Content -Path 'srv.pid'" > launch.out 2>&1
PID=$(cat srv.pid | tr -d '\r') # read the PID from the file, never a pipe
taskkill //F //PID "$PID"
Three rules carry the whole trick:
Never wrap a detached launcher in $(...). Command substitution waits for pipe EOF, which a detached child never sends, so PID=$(powershell ... -PassThru; $p.Id) blocks until the server exits. Redirect the launcher to a file and read the PID back from the file.
timeout N cannot cap a native Windows command. MSYS signal emulation only reaches MSYS processes, so timeout will not rescue you from that block.
-RedirectStandardOutput and -RedirectStandardError must name different files, or Start-Process refuses to start.
To find the PID of an already-running listener, use netstat -ano | grep ":.*LISTENING" — a Bash job id is not the Windows PID.
Additional measured notes:
nohup cmd > log 2>&1 & with an explicit file redirect does work. nohup cmd & without one leaks the child's output back into the caller and can hang a harness waiting for EOF on that pipe.
cmd //c "start /b ..." is not a substitute: MSYS mangles the inline quoting, it does not actually detach (stderr still lands in the caller), and it inherits the MSYS PATH, so timeout resolves to GNU coreutils instead of timeout.exe.
Actual Behavior
The bash tool call for nohup npm run dev & never returns. The agent's turn hangs indefinitely, the Playwright verification step is never reached, and the session must be manually interrupted. No warning, hint, or platform-specific guidance is surfaced anywhere in the flow.
Doctor Output
✓ System OK (opencode 1.18.24 · oh-my-openagent 4.19.4)
Prior art already in this repo: packages/git-bash-mcp/src/runner.ts avoids the same Windows pipe deadlock via temp-file fds instead of pipes — but that package is Codex Light edition only and is not registered by the OpenCode Ultimate edition.
Suggested touch points if you want a patch: packages/omo-opencode/src/hooks/non-interactive-env/constants.ts (add a Windows background-process entry to SHELL_COMMAND_PATTERNS), non-interactive-env-hook.ts (extend the tool.execute.before warning path beyond the /\bgit\b/ gate), and packages/prompts-core/prompts/.
I'm happy to open a PR if you agree with the direction.
Prerequisites
Bug Description
On native Windows 11 with Git for Windows (MSYS2/MINGW64), an agent that starts a long-lived dev server and then verifies it (e.g. npm run dev → Playwright) reliably deadlocks. The agent issues the standard POSIX idiom:
nohup npm run dev &
The bash tool call never returns. The dev server itself is running, but the harness blocks forever waiting for EOF on the caller's stdout pipe, so the agent never reaches the Playwright verification step and the whole session stalls.
Root cause: MSYS job control does not extend to native Windows children. MSYS & / nohup are implemented on top of CreateProcess, not fork(), so they provide no real process-group detachment from a native Windows child. The backgrounded child inherits and keeps open the write end of the parent shell's stdout pipe for its entire lifetime, so any consumer waiting for EOF on that pipe waits until the server exits.
This is the same failure class tracked upstream as affaan-m/ECC#2489 ("Git Bash nohup/& reaped immediately"). Note the observable symptom differs: in that issue the detached child dies; here the caller hangs. Both stem from the same root cause — MSYS & does not truly detach a native Windows child.
OMO currently ships zero Windows-specific guidance for backgrounding processes:
So the agent has nothing to steer it away from nohup ... &, and falls into this trap every time on Windows.
Interestingly, packages/git-bash-mcp/src/runner.ts already solved the sibling problem: runGitBashCommand() deliberately spawns with stdio: ["ignore", stdoutFd, stderrFd] using temp-file fds "to avoid Windows pipe-buffer deadlocks". But git-bash-mcp is Codex Light edition only — it is not registered by the OpenCode Ultimate edition, which uses tmux-based interactive_bash. The knowledge exists in the repo but never reaches the OpenCode agent's bash tool.
Steps to Reproduce
nohup npm run dev &
Variant that also hangs, and is worth calling out because agents reach for it naturally:
PID=$(powershell -NoProfile -Command "$p = Start-Process ... -PassThru; $p.Id")
Command substitution
Expected Behavior
An agent on native Windows should be able to start a long-lived process and immediately continue to the next step (Playwright verification, health check, etc.).
Concretely, I'd expect OMO to do at least one of:
The recipe that actually works, measured on MINGW64_NT-10.0-26200 (2026-08-28):
powershell -NoProfile -Command "$p = Start-Process -FilePath 'node'
-ArgumentList 'server.js' -WorkingDirectory 'C:\path\to\dir'
-WindowStyle Hidden -RedirectStandardOutput 'srv.log'
-RedirectStandardError 'srv.err' -PassThru;
$p.Id | Set-Content -Path 'srv.pid'" > launch.out 2>&1
PID=$(cat srv.pid | tr -d '\r') # read the PID from the file, never a pipe
taskkill //F //PID "$PID"
Three rules carry the whole trick:
To find the PID of an already-running listener, use netstat -ano | grep ":.*LISTENING" — a Bash job id is not the Windows PID.
Additional measured notes:
Actual Behavior
The bash tool call for nohup npm run dev & never returns. The agent's turn hangs indefinitely, the Playwright verification step is never reached, and the session must be manually interrupted. No warning, hint, or platform-specific guidance is surfaced anywhere in the flow.
Doctor Output
Error Logs
Configuration
Additional Context
Operating System
Windows
OpenCode Version
1.18.24