Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Summary
This implements the external control proposed in #168. It lets an agent running inside a Vibeyard tab open, rename, list and close tabs, with no UI automation:
It is off by default: Preferences → General → "Allow tabs to control tabs (vibeyard-ctl)".
What each command does
new-sessionbehaves like the New Session dialog.--focusswitches to it.--nameis also passed toclaude --name, so the worker appears under that name in Claude Code's own session list and cross-session messaging.--profilemust belong to the provider that will actually run. It never silently falls back to the default account.renamesticks the way a UI rename does. Renaming a tab to its current name is a no-op.closegoes through the existing confirmation path. A working tab shows the usual "still active" dialog, and the command returnsawaiting_confirmation.listreturns each tab's id, name, status (not_started/idle/working/input/exited), owner, creation time and cost.How it works
It reuses the existing
hook-status.tspattern.~/.vibeyard/run/bin/vibeyard-ctl(with a.cmdwrapper on Windows), the same way it installs the statusline script.VIBEYARD_CTL. That folder is also prepended toPATH.~/.vibeyard/commands/.fs.watchplus a polling fallback, validates it, and passes it to the renderer over IPC.addSession/renameSession/closeSessionWithConfirm.vibeyard ctl …in the npm launcher just forwards to the installed client.Keeping it safe
disabled.VIBEYARD_CTL_TOKEN. It lives only in memory, is replaced on every spawn, and is revoked when the process exits or dies. A request without a valid token getsunauthorized. This guards against scripts running outside Vibeyard. It is not a security boundary between processes of the same user: anything a tab runs inherits that tab's token. The docs say so.createdByis persisted, so this survives restarts.0700, be owned by the user, and not be a symlink. Requests are opened withO_NOFOLLOW | O_NONBLOCK(where the platform has them), checked throughfstaton the open file, and read with a byte limit. Results are written to a random temp name withO_EXCL, then renamed. No shell is involved anywhere: args reach node-pty as an argv array.Test plan
npm run buildpasses.npm testpasses: 152 files, 2093 tests, on Node 24 and Node 23 (macOS).main; this PR adds none.--nameshows up in Claude Code's session list, and a message sent by that name gets a reply;not_ownerwhen closing or renaming someone else's tab;unauthorizedwith a missing or forged token;disabledwhen the preference is off.--prompt-filedelivers%,^,!,",&and CRLF byte-for-byte;.cmdwrapper returns the right JSON and exit codes from cmd.exe, PowerShell and Git Bash.Notes
pty-manager.test.ts, "lets user env vars override provider-set vars". A userPATHstill wins over the provider's, but the client folder is now prepended in front of it. That's how agents findvibeyard-ctlwithout an absolute path. Agents are documented to use$VIBEYARD_CTL, which never depends onPATH..cmdwrapper runs through cmd.exe, which cuts an inline--promptat the first newline and expands%VAR%.--prompt-fileis lossless, and the docs and--helprecommend it on Windows.python3is only the Microsoft Store stub,hook-status.test.ts(thebuildStatusLinePythontests) fails with exit 9009. The same happens onmain. The new client tests usepythonBinfromplatform.ts, the same command the app runs, so they work there.send);not_started);ctladded to the preload namespace list.Thanks for considering it! Happy to adjust anything: naming, the file-drop transport, or scope.
🤖 Generated with Claude Code