Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/opencode/src/cli/cmd/tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { writeHeapSnapshot } from "v8"
import { ServerAuth } from "@/server/auth"
import { validateSession } from "../tui/validate-session"
import { win32InstallCtrlCGuard } from "@opencode-ai/tui/terminal-win32"
import { safeExit } from "@/util/exit"

declare global {
const OPENCODE_WORKER_PATH: string
Expand Down Expand Up @@ -303,7 +304,7 @@ export const TuiThreadCommand = cmd({
unguard?.()
} catch {}
}
process.exit()
safeExit()
},
})
// scratch
5 changes: 3 additions & 2 deletions packages/opencode/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { PrCommand } from "./cli/cmd/pr"
import { SessionCommand } from "./cli/cmd/session"
import { DbCommand } from "./cli/cmd/db"
import { errorMessage } from "./util/error"
import { safeExit } from "./util/exit"
import { PluginCommand } from "./cli/cmd/plug"
import { Heap } from "./cli/heap"

Expand Down Expand Up @@ -137,6 +138,6 @@ try {
// Some subprocesses don't react properly to SIGTERM and similar signals.
// Most notably, some docker-container-based MCP servers don't handle such signals unless
// run using `docker run --init`.
// Explicitly exit to avoid any hanging subprocesses.
process.exit()
// Explicitly exit (on non-Windows) to avoid any hanging subprocesses.
safeExit()
}
14 changes: 14 additions & 0 deletions packages/opencode/src/util/exit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function safeExit(code: number = Number(process.exitCode ?? 0)): void {
if (process.platform !== "win32") {
process.exit(code)
return
}

// On Windows process.exit() calls ExitProcess(), which broadcasts
// CTRL_CLOSE_EVENT to the whole console process group. The parent shell
// (pwsh/cmd) is attached to the same console, so it is killed together with
// opencode when it exits from a TUI session (https://github.com/anomalyco/opencode/issues/28673).
// Setting the exit code and returning lets the process end naturally, which
// only detaches opencode from the console and leaves the shell alive.
process.exitCode = code
}
Loading