diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 3cde4b41f..3233c5088 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -47,6 +47,9 @@ ### Fixed - Fixed a startup crash on Windows when running from a drive root (e.g. `R:\`): `fs.realpath` throws `EISDIR` there, but `canonicalProjectDir` in `launch/presence.ts` and `launch/client.ts` only recovered `ENOENT`. It now also falls back to `path.resolve()` on `EISDIR` ([#5708](https://github.com/can1357/oh-my-pi/issues/5708) by [@ve3xone](https://github.com/ve3xone)). +### Fixed + +- Fixed unknown `__omp_worker_*` CLI selectors exiting 0 with empty output instead of erroring; an unrecognized worker-host selector now writes `Error: unknown worker selector: …` to stderr and exits nonzero, so a stale or mistyped selector can no longer look healthy to a parent process or install smoke path ([#5712](https://github.com/can1357/oh-my-pi/issues/5712)). ## [17.0.1] - 2026-07-16 diff --git a/packages/coding-agent/src/cli.ts b/packages/coding-agent/src/cli.ts index a5c64fc03..996e12fbc 100755 --- a/packages/coding-agent/src/cli.ts +++ b/packages/coding-agent/src/cli.ts @@ -338,7 +338,11 @@ export async function runCli(argv: string[]): Promise { // worker's parked initial messages as soon as the entry module's // top-level evaluation finishes. if (resolvedArgv[0]?.startsWith("__omp_worker_")) { - await runWorkerEntrypoint(resolvedArgv[0]); + const dispatched = await runWorkerEntrypoint(resolvedArgv[0]); + if (!dispatched) { + process.stderr.write(`Error: unknown worker selector: ${resolvedArgv[0]}\n`); + process.exitCode = 1; + } return; } diff --git a/packages/coding-agent/test/worker-selector.test.ts b/packages/coding-agent/test/worker-selector.test.ts new file mode 100644 index 000000000..8dd294e49 --- /dev/null +++ b/packages/coding-agent/test/worker-selector.test.ts @@ -0,0 +1,37 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test"; +import { runCli } from "../src/cli"; + +// The worker-host re-entry seam dispatches any `__omp_worker_*` selector to +// `runWorkerEntrypoint`. An unrecognized selector must fail loudly rather than +// exit 0 with empty output, so a stale/mistyped selector cannot look healthy to +// a parent process or install smoke path (issue #5712). +describe("worker selector dispatch", () => { + beforeEach(() => { + process.exitCode = 0; + }); + + afterEach(() => { + vi.restoreAllMocks(); + process.exitCode = 0; + }); + + it("fails with a nonzero exit and stderr error on an unknown selector", async () => { + const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true); + + await runCli(["__omp_worker_does_not_exist"]); + + expect(process.exitCode).toBe(1); + expect(stderr).toHaveBeenCalledWith("Error: unknown worker selector: __omp_worker_does_not_exist\n"); + }); + + it("leaves normal root flags untouched", async () => { + const stdout = vi.spyOn(process.stdout, "write").mockImplementation(() => true); + const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true); + + await runCli(["--version"]); + + expect(process.exitCode).toBe(0); + expect(stdout).toHaveBeenCalled(); + expect(stderr).not.toHaveBeenCalledWith(expect.stringContaining("unknown worker selector")); + }); +});