Skip to content

Commit c655c35

Browse files
committed
fix: improve Windows compatibility and fix event listener issues
Replace platform-specific 'which'/'where' commands with cross-platform Bun.which() API to fix Windows compatibility issues and simplify code. Fixes: - #1027: Comment-checker binary crashes on Windows (missing 'check' subcommand) - #1036: Session-notification listens to non-existent events - #1033: Infinite loop in session notifications - #599: Doctor incorrectly reports OpenCode as not installed on Windows - #1005: PowerShell path detection corruption on Windows Changes: - Use Bun.which() instead of spawning 'which'/'where' commands - Add 'check' subcommand to comment-checker invocation - Remove non-existent event listeners (session.updated, message.created) - Prevent notification commands from resetting their own state - Fix edge case: clear notifiedSessions if activity occurs during notification All changes are cross-platform compatible and tested on Windows/Linux/macOS.
1 parent aead4ae commit c655c35

File tree

5 files changed

+18
-40
lines changed

5 files changed

+18
-40
lines changed

src/cli/doctor/checks/dependencies.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import { CHECK_IDS, CHECK_NAMES } from "../constants"
33

44
async function checkBinaryExists(binary: string): Promise<{ exists: boolean; path: string | null }> {
55
try {
6-
const proc = Bun.spawn(["which", binary], { stdout: "pipe", stderr: "pipe" })
7-
const output = await new Response(proc.stdout).text()
8-
await proc.exited
9-
if (proc.exitCode === 0) {
10-
return { exists: true, path: output.trim() }
6+
const path = Bun.which(binary)
7+
if (path) {
8+
return { exists: true, path }
119
}
1210
} catch {
1311
// intentionally empty - binary not found

src/cli/doctor/checks/opencode.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,9 @@ export function buildVersionCommand(
5555
export async function findOpenCodeBinary(): Promise<{ binary: string; path: string } | null> {
5656
for (const binary of OPENCODE_BINARIES) {
5757
try {
58-
const lookupCommand = getBinaryLookupCommand(process.platform)
59-
const proc = Bun.spawn([lookupCommand, binary], { stdout: "pipe", stderr: "pipe" })
60-
const output = await new Response(proc.stdout).text()
61-
await proc.exited
62-
if (proc.exitCode === 0) {
63-
const paths = parseBinaryPaths(output)
64-
const selectedPath = selectBinaryPath(paths, process.platform)
65-
if (selectedPath) {
66-
return { binary, path: selectedPath }
67-
}
58+
const path = Bun.which(binary)
59+
if (path) {
60+
return { binary, path }
6861
}
6962
} catch {
7063
continue

src/hooks/comment-checker/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export async function runCommentChecker(input: HookInput, cliPath?: string, cust
165165
debugLog("running comment-checker with input:", jsonInput.substring(0, 200))
166166

167167
try {
168-
const args = [binaryPath]
168+
const args = [binaryPath, "check"]
169169
if (customPrompt) {
170170
args.push("--prompt", customPrompt)
171171
}

src/hooks/session-notification-utils.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,8 @@ let aplayPath: string | null = null
2121
let aplayPromise: Promise<string | null> | null = null
2222

2323
async function findCommand(commandName: string): Promise<string | null> {
24-
const isWindows = process.platform === "win32"
25-
const cmd = isWindows ? "where" : "which"
26-
2724
try {
28-
const proc = spawn([cmd, commandName], {
29-
stdout: "pipe",
30-
stderr: "pipe",
31-
})
32-
33-
const exitCode = await proc.exited
34-
if (exitCode !== 0) {
35-
return null
36-
}
37-
38-
const stdout = await new Response(proc.stdout).text()
39-
const path = stdout.trim().split("\n")[0]
40-
41-
if (!path) {
42-
return null
43-
}
44-
45-
return path
25+
return Bun.which(commandName)
4626
} catch {
4727
return null
4828
}

src/hooks/session-notification.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ export function createSessionNotification(
200200

201201
function markSessionActivity(sessionID: string) {
202202
cancelPendingNotification(sessionID)
203-
notifiedSessions.delete(sessionID)
203+
if (!executingNotifications.has(sessionID)) {
204+
notifiedSessions.delete(sessionID)
205+
}
204206
}
205207

206208
async function executeNotification(sessionID: string, version: number) {
@@ -254,6 +256,11 @@ export function createSessionNotification(
254256
} finally {
255257
executingNotifications.delete(sessionID)
256258
pendingTimers.delete(sessionID)
259+
// Clear notified state if there was activity during notification
260+
if (sessionActivitySinceIdle.has(sessionID)) {
261+
notifiedSessions.delete(sessionID)
262+
sessionActivitySinceIdle.delete(sessionID)
263+
}
257264
}
258265
}
259266

@@ -262,7 +269,7 @@ export function createSessionNotification(
262269

263270
const props = event.properties as Record<string, unknown> | undefined
264271

265-
if (event.type === "session.updated" || event.type === "session.created") {
272+
if (event.type === "session.created") {
266273
const info = props?.info as Record<string, unknown> | undefined
267274
const sessionID = info?.id as string | undefined
268275
if (sessionID) {
@@ -299,7 +306,7 @@ export function createSessionNotification(
299306
return
300307
}
301308

302-
if (event.type === "message.updated" || event.type === "message.created") {
309+
if (event.type === "message.updated") {
303310
const info = props?.info as Record<string, unknown> | undefined
304311
const sessionID = info?.sessionID as string | undefined
305312
if (sessionID) {

0 commit comments

Comments
 (0)