Skip to content

Fix startup commands frequently not running on launch (#14)#16

Open
frd1201 wants to merge 1 commit into
halilc4:devfrom
frd1201:fix/14-startup-command-pty-readiness
Open

Fix startup commands frequently not running on launch (#14)#16
frd1201 wants to merge 1 commit into
halilc4:devfrom
frd1201:fix/14-startup-command-pty-readiness

Conversation

@frd1201

@frd1201 frd1201 commented Jun 22, 2026

Copy link
Copy Markdown

Summary

Fixes #14 — per-pane startup commands frequently don't run when a workspace launches: intermittently for a single workspace, and almost always for the second workspace when two have launchOnStartup: true.

There are three independent timing/lifecycle root causes (full evidence in #14); this PR addresses all three.

Changes

1. Poll for panes instead of a single 300 ms shot — startupCommand.service.ts

onTabOpened() previously did setTimeout(() => processChildTabs(tab), 300) and called getAllTabs() exactly once. If the split panes weren't constructed yet at 300 ms, it found 0 tabs and every command for that workspace was silently skipped.

Now processChildTabs() re-scans the split on a short interval, processing panes as they appear, and stops once all of the split's pending commands are handled (or an attempt cap is hit).

2. Wait for real readiness before sending — startupCommand.service.ts

Commands were sent before the PTY session existed: the code blind-fired setTimeout(sendCommand, 500) into a shell that wasn't ready, so the input was dropped during shell init (clink/conpty in particular drop early input).

Now each command:

  • polls until session.output$ actually exists, then
  • waits for the shell prompt to render — output is ANSI-stripped and checked for a trailing prompt char (> / $ / # / % / / ), with an output-settle timeout and an overall hard cap as fallbacks.

3. Serialize startup launches — toolbar.provider.ts

When two launchOnStartup workspaces opened, only the active tab initialized its PTYs; the workspace that ended up in the background never got a session, so there was nothing to send to.

registerCommands() now returns a promise that resolves when the batch has been delivered (or a safety timeout elapses). openWorkspace() awaits it, so workspaces open one at a time — each stays foreground until its sessions connect and commands are sent before the next opens.

Notes

Follow-up (not in this PR)

#14 also notes a cleaner long-term option: pass the startup command via the shell's own launch args (cmd /k, pwsh -NoExit -Command, bash -c '…; exec bash') so the shell runs it itself, removing the typing-based timing heuristics entirely. Happy to explore that separately if preferred.

Per-pane startup commands often failed to run when workspaces launched,
intermittently for a single workspace and almost always for the second of
two launchOnStartup workspaces. Three timing/lifecycle root causes are
addressed:

1. Pane discovery was a single 300ms scan. If the split panes weren't
   constructed yet, getAllTabs() returned 0 and every command was silently
   skipped with no retry. StartupCommandService now polls, processing panes
   as they appear until all of a split's commands are handled (capped).

2. Commands were sent before the PTY session existed. The code blind-fired
   a 500ms timeout into a shell that wasn't ready, so input was dropped
   during shell init (clink/conpty in particular). It now waits for
   session.output$ to exist, then for the shell prompt to render
   (prompt detection across cmd/clink/PowerShell/bash/zsh, with an
   output-settle timeout and a hard cap as fallbacks).

3. Background-workspace panes never connected their session, so there was
   nothing to send to. Startup launches are now serialized: each workspace
   stays in the foreground until its sessions connect and commands are sent
   before the next one opens. registerCommands() returns a promise that
   resolves when the batch is delivered (or a safety timeout elapses).
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