Fix systematic exit delay after search output (interactive + non-interactive) - #192
Merged
Conversation
|
Coverage after merging fix/tui-exit-delay into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes a consistent “exit lag” after printing results by ensuring that ref’d async handles (stdin stream teardown and a timeout timer) can’t keep the event loop alive after the work is complete. It aligns with the project’s CLI/TUI architecture by keeping the fixes localized to the side-effectful entry points (tui.ts and github-code-search.ts) without altering the pure rendering/output logic.
Changes:
- Interactive mode:
process.stdin.unref()is called during cleanup so breaking out of thefor await (const chunk of process.stdin)loop doesn’t hold the process open while stdin teardown completes. - Non-interactive/CI mode: the 2s update-check timeout is unref’d and explicitly cleared after
Promise.racesettles to prevent the losing timer from delaying process exit.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/tui.ts | Unrefs stdin on both cleanup paths so interactive exit doesn’t wait on stdin async iterator teardown. |
| github-code-search.ts | Unrefs and clears the update-check timeout so non-interactive runs don’t linger due to an orphaned timer. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes a systematic delay between the moment the final output is printed and the moment the process actually returns control to the shell — noticeable in both interactive and non-interactive (
--no-interactive/ CI) mode.Root cause 1 — interactive mode (
src/tui.ts)Keyboard input is read via
for await (const chunk of process.stdin). Breaking out of that loop (onEnter,q, or Ctrl+C) forces the JS engine to await the async iterator's implicitreturn(), which destroys the underlyingReadableand waits for its'close'event before thebreakactually completes. The output is already printed by then (console.logruns before thebreak), so the user sees the result immediately but the shell prompt only comes back once that stream teardown resolves.Fix: call
process.stdin.unref()right aftersetRawMode(false)in both exit paths. An unref'd handle can no longer keep the event loop alive, so the process can exit as soon as the teardown starts instead of waiting for it to fully settle.Root cause 2 — non-interactive mode (
github-code-search.ts)The post-output "check for update" step races
checkForUpdate()against a 2 s timeout viaPromise.race:The losing branch's
setTimeoutis never cleared. Node/Bun timers are ref'd by default, so even whencheckForUpdate()wins the race in a few hundred ms, the orphaned 2 s timer keeps the process alive until it actually fires — a near-constant ~2 s delay on every CI/--no-interactiverun.Fix:
.unref()the timer so it can't keep the process alive on its own, andclearTimeoutit once the race settles (avoids a strayabort()firing after the flow has already moved on). The 2 s network cap behaviour is unchanged.How did you verify your code works?
bun run lint— zero errorsbun run format:check— no diffbun test— 871 tests pass, 0 failures, no regressionstui.tsand the CLI entry point are side-effectful, perAGENTS.md):Enterto confirm selection — shell prompt now returns immediately instead of after a noticeable pause.--no-interactive(orCI=true) — process now exits right after printing output instead of ~2 s later.