-
Notifications
You must be signed in to change notification settings - Fork 23
feat(shell,open): add piping, multi-instance, and cross-platform support #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Enhance brev shell and brev open commands with composability features. Shell improvements: - Add -c flag to run commands non-interactively - Support @filepath syntax to run local scripts remotely - Accept multiple instances or pipe from stdin - Output instance names for chaining Open improvements: - Support multiple instances (opens each in separate window) - Add terminal and tmux editor options - Add Terminal.app support on macOS - Fix WSL exec format error on Windows - Accept instances from stdin for piping Examples: brev shell my-instance -c "nvidia-smi" brev shell my-instance -c @setup.sh brev create my-instance | brev shell -c "nvidia-smi" brev open my-instance tmux brev create my-cluster --count 3 | brev open cursor
- Fix gofumpt formatting - Wrap external package errors with breverrors.WrapAndTrace Co-Authored-By: Claude Opus 4.5 <[email protected]>
Manual QA Results ✅Build & Static Analysis:
Test Plan Verification:
Notes:
|
pkg/cmd/open/open.go
Outdated
| return breverrors.WrapAndTrace(err) | ||
| } | ||
| } | ||
| if lastErr != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the case of an error with more than one instance it doesn't look like we actually ever return the error?
pkg/cmd/open/open.go
Outdated
| setupDoneString = "------ Done running execs ------" | ||
| // Validate editor flag if provided | ||
| if editor != "" && !isEditorType(editor) { | ||
| return breverrors.NewValidationError(fmt.Sprintf("invalid editor: %s. Must be 'code', 'cursor', 'windsurf', or 'tmux'", editor)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
terminal is also valid?
pkg/cmd/shell/shell.go
Outdated
| if (stat.Mode() & os.ModeCharDevice) == 0 { | ||
| // Stdin is piped, read instance names (one per line) | ||
| scanner := bufio.NewScanner(os.Stdin) | ||
| for scanner.Scan() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to check for errors (scanner.Err() != nil) ?
pkg/util/util.go
Outdated
|
|
||
| cmd := exec.Command("cmd.exe", cmdArgs...) // #nosec G204 | ||
| output, err := cmd.CombinedOutput() | ||
| return output, breverrors.WrapAndTrace(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a nil check on err?
pkg/util/util.go
Outdated
|
|
||
| func runWindsurfCommand(windsurfpath string, args []string) ([]byte, error) { | ||
| // In WSL, Windows .exe files need to be run through cmd.exe | ||
| if isWSL() && (strings.HasSuffix(windsurfpath, ".exe") || strings.HasPrefix(windsurfpath, "/mnt/")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runVsCodeCommand, runCursorCommand, and runWindsurfCommand seem to have the same structure, extract a common method that takes the path as an arg?
func runEditorCommand(path string, args []string) ([]byte, error) {
if isWSL() && (strings.HasSuffix(path, ".exe") || strings.HasPrefix(path, "/mnt/")) {
return runWindowsExeInWSL(path, args)
}
cmd := exec.Command(path, args...) // #nosec G204
res, err := cmd.CombinedOutput()
if err != nil {
return nil, breverrors.WrapAndTrace(err)
}
return res, nil
}
pkg/cmd/open/open.go
Outdated
| } | ||
|
|
||
| func openTerminal(sshAlias string, path string, store OpenStore) error { | ||
| _ = store // unused parameter required by interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can move these dashes in to the signature, so you don't need the declaration, e.g.
func openTerminal(sshAlias string, _ string, _ OpenStore) error {
| cmd.Flags().BoolVarP(&runRemoteCMD, "remote", "r", true, "run remote commands") | ||
| cmd.Flags().StringVarP(&directory, "dir", "d", "", "override directory to launch shell") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are safe to remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, these are safe to remove - they were dead code:
--remote/-r: The variablerunRemoteCMDwas declared but never actually used anywhere in the code--dir/-d: Thedirectoryparameter was passed torunSSH, but that function immediately discarded it with_in its signature:func runSSH(_ *entity.Workspace, sshAlias, _ string) error
Neither flag had any functional effect.
Add implemented features for brev shell and brev open: Shell enhancements: - Non-interactive -c flag for scripted execution - @filepath syntax to run local scripts remotely - Multi-instance support (run on multiple instances) - Stdin piping from brev create/ls - Output instance names for command chaining Open enhancements: - Multiple editor options (vscode, cursor, vim, terminal, tmux) - Multi-instance support (opens each in separate window) - Cross-platform fixes (macOS Terminal.app, WSL) - Stdin piping from brev create Also expands skills documentation explaining why they matter for agentic use cases.
- shell: interactive, no stdin/stdout - shell -c: accepts instance names from stdin, outputs command stdout/stderr
Create new 'brev exec' command for non-interactive command execution: - Run commands on one or more instances - Support @filepath syntax to run local scripts remotely - Accept instance names from stdin for piping - Output instance names for command chaining Simplify 'brev shell' to be interactive only: - Remove -c flag (use 'brev exec' instead) - Single instance argument required - Keep --host flag for host SSH Update PRD to document the new command separation.
open.go: - Add 'terminal' to valid editors in error message - Use multierror for better multi-instance error aggregation - Add scanner error check for stdin reading util.go: - Extract common runEditorCommand helper for WSL compatibility - Simplify runVsCodeCommand, runCursorCommand, runWindsurfCommand
Enable chaining with brev open by outputting instance names: brev create my-gpu | brev open cursor | brev exec 'pip install torch' Only outputs when stdout is piped, keeping interactive use clean.
Coding agents prefer CLIs: text-native, self-documenting, composable, and already learned from training data. Positions Brev CLI as the default for autonomous GPU workflows.
Summary
prd: https://docs.google.com/document/d/1pYYR1nfcfJi9kCtcxiY5DINYjGxXwnO6zh7JdtmGBTU/edit?tab=t.0#heading=h.oyak6y4d25ei
Enhance
brev shellandbrev opencommands with composability features for scriptable workflows.Shell Improvements
-cflag to run commands non-interactively@filepathsyntax to run local scripts on remote instancesOpen Improvements
terminalandtmuxeditor optionsExamples
Test plan
brev shell instance -c "cmd"runs command and returnsbrev shell instance -c @script.shruns local script remotelybrev open instance tmuxopens tmux session