fix(host-service): send initialCommand Enter as a separate delayed write
What
Agent and preset launches could end up typed at the prompt but never executed: the workspace was created, the agent terminal opened, and the full launch command ('claude' '--dangerously-skip-permissions' '<prompt>') sat in the shell's edit buffer with the cursor blinking after it. Reported as "the new workspace page creates a workspace but doesn't start the agent" — but it's page-agnostic and hits every initialCommand consumer (modal creates, automations, setup terminals).
Root cause
The OSC 133;A shell-ready marker is emitted from a precmd hook, which runs before the prompt is drawn and before the line editor starts reading input. Writing command\n in that gap lets shell plugin init (oh-my-zsh vi-mode, syntax-highlighting — worst under first-create load) flush the PTY input queue mid-read: the command text survives into the editor's buffer, the trailing newline is lost.
Confirmed live via CDP against the dev app: on the first workspace create after boot the command was stuck unexecuted; sending one synthetic Enter keypress launched Claude immediately, which then completed the prompt's task — proving only the newline was missing. This is the residual hole in the #4963 → #5774 (first-prompt gate) → #5927 (15s bound) series.
Fix
In queueInitialCommand:
- strip trailing newlines from the command text and write it alone;
- 500ms later, send Enter as its own write — and as
\r(what a real Enter key sends, bound to accept-line in every keymap) instead of\n(relies on the ^J binding).
Exactly one Enter is ever sent, so double-execution is impossible. If startup eats the text itself, the stray \r is a no-op at an empty prompt — every failure mode is strictly no-worse than before. Same technique VS Code shell integration uses; v1 avoided the class entirely by launching via shell -lc, which is why v1 never hit this.
Verification
bun run test:e2e(host-service): 20/20 pass, including the direnv input-consumption simulation and the promptness bound (command executed in 517ms with the new delay).- End-to-end via CDP on the dev desktop app: pre-fix, first create after boot reproduced the stuck command (screenshot evidence, before/after Enter). Post-fix, restarted the app and ran the full
/new-workspaceflow twice with real input events — both creates launched Claude, which replied within ~2s. Setup terminals unaffected. - Biome +
tscclean.
https://claude.ai/code/session_01CwWxQTQW2zLeN9hhRkQNxT
Summary by cubic
Fixes an intermittent issue where the initial launch command was typed but not executed. We now send the command text and Enter separately with a short delay for reliable execution.
- Bug Fixes
- In
packages/host-service/src/terminal/terminal.ts, strip trailing CR/LF frominitialCommandand write only the text. - Send Enter as a separate
\r500 ms after shell-ready to avoid plugin init eating it. - Update
packages/host-service/test/integration/setup-scripts.integration.test.tsto assert split write and delayed\r; tidy assertion formatting. - Applies to all
initialCommandflows (new workspace, modal creates, automations, setup terminals).
- In
Summary by CodeRabbit
- Bug Fixes
- Improved reliability of terminal “initial command” execution by sending the command text and the Enter keystroke as two separate, ordered writes.
- Normalized the command text to remove any trailing CR/LF characters before sending, and added a brief delay for better alignment with shell readiness.
- Tests
- Updated the integration test to assert the command is written without
\n, and that the Enter (\r) write occurs afterward.
- Updated the integration test to assert the command is written without