Skip to content

Stop restored shell tabs from re-running their command - #292

Merged
frenchie4111 merged 3 commits into
mainfrom
when-reopen-ness-open-previues
Aug 22, 2026
Merged

Stop restored shell tabs from re-running their command#292
frenchie4111 merged 3 commits into
mainfrom
when-reopen-ness-open-previues

Conversation

@frenchie4111

Copy link
Copy Markdown
Collaborator

Summary

  • A shell tab created via create_shell persists the command it was launched with. XTerminal treated that field as an instruction: any mount without a live PTY spawned zsh -ilc <command>. After a restart there's never a live PTY, so opening a previously-created shell tab silently re-ran whatever the agent had launched (npm run dev, a build, anything). Same thing mid-session once the command's shell had exited and the tab remounted.
  • The renderer now always spawns a plain interactive shell. Execution stays where it belongs — the single eager spawn in createShell (src/main/index.ts) at creation time.
  • The persisted command stays as a record of origin: still reported by list_shells, still the tab's default label. Comments on TerminalTab.command / PersistedTab.command updated to say so, since the field name reads like an instruction.

Test plan

  • npm run typecheck
  • npx electron-vite build
  • Agent calls create_shell with a command → command runs immediately in the new tab (unchanged)
  • Quit and reopen Ness → focusing that shell tab gives an idle prompt instead of re-running the command
  • Let a command-backed shell exit, switch away and back → idle prompt, no re-run
  • Plain user-opened shell tabs (+ → Shell) behave as before

🤖 Generated with Claude Code

frenchie4111 and others added 3 commits August 21, 2026 19:24
A shell tab created by an agent persists the command it was launched
with, and XTerminal treated that field as an instruction: any mount
without a live PTY spawned `zsh -ilc <command>`. Reopening Ness (or
touching a tab whose command had already exited) therefore re-ran it
unprompted. The command is only ever meant to run once, in createShell's
eager spawn, so the renderer now always opens a plain interactive shell
and the persisted command stays as a record of origin.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scrollback was already being written to userData/terminal-history/<id>,
but getHistory() only read the in-memory map, and that map is seeded from
disk inside create(). On a cold start the renderer asks for history
before it spawns, so it got '' and skipped the replay — create() then
loaded the file into a buffer nobody ever displayed, which is why a
renderer reload showed scrollback but relaunching the app did not.

Both entry points now go through ensureHistoryBuffer, so the file is read
on first touch (at most once per id) whichever call arrives first.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Shells created via create_shell run their command exactly once, at
creation. Remounting such a tab after a restart handed back an
interactive prompt, which is not how these shells ever behave — once the
command's shell exits mid-session the tab is just a transcript. Drop the
renderer's pty:create for a command shell that has no live PTY.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@frenchie4111
frenchie4111 merged commit 2ec7e8c into main Aug 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant