Skip to content

fix(cli): interrupt running commands cleanly on Ctrl+C#1370

Merged
Martin Torp (mtorp) merged 2 commits into
v1.xfrom
jfblaa/cli-interrupt-clean-exit
Jun 19, 2026
Merged

fix(cli): interrupt running commands cleanly on Ctrl+C#1370
Martin Torp (mtorp) merged 2 commits into
v1.xfrom
jfblaa/cli-interrupt-clean-exit

Conversation

@jfblaa

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What

The launcher (bin/cli.js) spawns the real CLI as a child process with inherited stdio. Previously the launcher was torn down by SIGINT before that child finished, so on Ctrl+C the child's final status could print after the shell prompt had already returned — and the child could be left running in the background.

This makes the launcher wait for the child to handle the interrupt itself and then mirror its exit, so output stays ordered.

How

  • On the first SIGINT/SIGTERM, stay alive and let the child (which shares our process group and receives the signal directly) run its own teardown; exit by mirroring the child's real exit.
  • The wait is bounded: if the child outlives a short grace period (SHUTDOWN_GRACE_MS, 3s), or on a second Ctrl+C, SIGKILL it and exit with the conventional 128 + signum code.
  • The grace timer is unref'd, so a child that exits on its own is mirrored with no added latency — the cap only applies to a slow or wedged child.

Notes

  • Applies to all commands, not any one in particular.
  • Independent change; no dependency on other repos.
  • No version bump / changelog entry — internal interrupt-handling plumbing, left for the next release to fold in.

Note

Low Risk
Launcher-only process/signal plumbing in bin/cli.js; no auth, data, or business-logic changes, with bounded fallback via SIGKILL.

Overview
The CLI launcher (bin/cli.js) now coordinates shutdown when you hit Ctrl+C or send SIGTERM, instead of exiting before the spawned CLI child finishes.

On the first signal it stays alive so the child (same process group, inherited stdio) can tear down and print final output in order; the parent then mirrors the child’s exit. If the child doesn’t exit within 3s, or you signal again, it SIGKILLs the child and exits with 130 (SIGINT) or 143 (SIGTERM). The grace timer is unref’d so a normal fast exit isn’t delayed.

When the child exits on a signal, the launcher removes its own handlers before re-raising so the parent actually terminates instead of swallowing the signal.

Reviewed by Cursor Bugbot for commit 010d858. Configure here.

The launcher (bin/cli.js) spawns the real CLI as a child with inherited
stdio. Previously it was torn down by SIGINT before that child finished,
so the child's final status printed after the shell prompt had already
returned (and the child could be left running in the background).

Wait briefly for the child to handle the interrupt itself and mirror its
exit, so output stays ordered. The wait is bounded: if the child outlives
a short grace period, or on a second Ctrl+C, SIGKILL it and exit with the
conventional 128+signum code. The grace timer is unref'd, so a child that
exits on its own is mirrored with no added latency.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 010d858. Configure here.

Comment thread bin/cli.js Outdated
Addresses review feedback: re-raising the child's signal on the launcher
raced against `await spawnPromise` resolving and could leave the default
`process.exitCode` of 1, so Ctrl+C could report exit 1 instead of 130.
Exit explicitly with the conventional 128+signum code (resolved from
os.constants.signals) in the on('exit') signal branch instead.
@mtorp Martin Torp (mtorp) merged commit feafd78 into v1.x Jun 19, 2026
12 checks passed
@mtorp Martin Torp (mtorp) deleted the jfblaa/cli-interrupt-clean-exit branch June 19, 2026 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants