From 232514a5d357c709ed82951918d5b0e9421557bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HOUZ=C3=89?= Date: Mon, 24 Aug 2026 01:01:38 +0200 Subject: [PATCH 1/2] Unref stdin on TUI exit to avoid a delay before the process returns control --- src/tui.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tui.ts b/src/tui.ts index 850fc3a..a629768 100644 --- a/src/tui.ts +++ b/src/tui.ts @@ -308,6 +308,9 @@ export async function runInteractive( if (statsDebounceTimer !== null) clearTimeout(statsDebounceTimer); process.stdin.setRawMode(false); process.off("SIGWINCH", onResize); + // Unref stdin so the pending `for await` read can't keep the event loop + // alive while breaking out of the loop awaits the stream's teardown. + process.stdin.unref(); // Set flag to break out of the event loop and allow stdout buffer to flush // before process termination. process.exit() may terminate too early. shouldExit = true; @@ -659,6 +662,9 @@ export async function runInteractive( process.stdout.write(ANSI_CLEAR); process.stdin.setRawMode(false); process.off("SIGWINCH", onResize); + // Unref stdin so the pending `for await` read can't keep the event loop + // alive while breaking out of the loop awaits the stream's teardown. + process.stdin.unref(); if (statsDebounceTimer !== null) clearTimeout(statsDebounceTimer); console.log( buildOutput(groups, query, org, excludedRepos, excludedExtractRefs, format, outputType, { From 6623cdc3933812f347a63f8480cc00a838e257bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HOUZ=C3=89?= Date: Mon, 24 Aug 2026 01:08:48 +0200 Subject: [PATCH 2/2] Unref the update-check race timer to avoid a ~2s exit delay in non-interactive mode --- github-code-search.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/github-code-search.ts b/github-code-search.ts index 902aac8..3625dc0 100644 --- a/github-code-search.ts +++ b/github-code-search.ts @@ -465,15 +465,22 @@ async function searchAction( // Race against a 2 s timeout so slow networks never delay the exit. // Fix: use AbortController so the in-flight fetch is actually cancelled on timeout. const updateAbortController = new AbortController(); + // Fix: the losing Promise.race branch's timer is never cleared, and by default + // keeps the process alive until it fires — causing a ~2 s exit delay on every + // non-interactive run even when checkForUpdate resolves instantly. unref() lets + // the process exit as soon as the real work is done; clearTimeout on the winning + // path avoids a stray abort() firing after we've already moved on. + let updateCheckTimer!: ReturnType; const latestTag = await Promise.race([ checkForUpdate(VERSION, GITHUB_TOKEN, updateAbortController.signal), - new Promise((res) => - setTimeout(() => { + new Promise((res) => { + updateCheckTimer = setTimeout(() => { updateAbortController.abort(); res(null); - }, 2000), - ), + }, 2000).unref(); + }), ]).catch(() => null); + clearTimeout(updateCheckTimer); if (latestTag) { const w = 55; // Fix: compute all widths from totalWidth so corners always align.