feat: add worktree command execution via wtp exec and wtp add --exec#85
feat: add worktree command execution via wtp exec and wtp add --exec#85
Conversation
📝 WalkthroughWalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI as CLI Parser
participant Git as Git API
participant Resolver as Worktree Resolver
participant Executor as Command Executor
participant Shell as Shell
User->>CLI: wtp exec feature/auth -- go test ./...
CLI->>CLI: parseExecInput()
CLI->>Git: list worktrees
Git-->>CLI: worktree list
CLI->>Resolver: resolveWorktreePathByName()
Resolver-->>CLI: worktree path
CLI->>Executor: Execute(name,args,workdir,interactive=true)
Executor->>Shell: Execute(name,args,workdir,interactive=true)
Shell->>Shell: hasTerminalIO()
alt TTY Available
Shell->>Shell: wire stdin/stdout/stderr (rgba(0,128,0,0.5))
Shell->>Shell: run command interactively
else No TTY
Shell->>Shell: capture output (rgba(128,128,128,0.5))
end
Shell-->>Executor: result
Executor-->>CLI: output/error
CLI-->>User: command result
sequenceDiagram
actor User
participant CLI as CLI Parser
participant GitOps as Git Operations
participant PostHook as Post-Create Hooks
participant ExecCmd as Exec Helper
participant Shell as Shell
User->>CLI: wtp add -b feature/new --exec "npm test"
CLI->>GitOps: create worktree
GitOps-->>CLI: worktree created
CLI->>PostHook: run post-create hooks
PostHook-->>CLI: hooks completed
CLI->>ExecCmd: executePostCreateCommand("npm test")
ExecCmd->>Shell: Execute("npm", ["test"], workdir, interactive=false)
Shell->>Shell: capture output (rgba(0,0,255,0.5))
Shell-->>ExecCmd: result
ExecCmd-->>CLI: output/error
CLI-->>User: execution complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@cmd/wtp/exec.go`:
- Around line 55-65: The code calls executor.Execute and then directly accesses
result.Results[0].Output; add checks to ensure result.Results is non-empty and
to handle an error stored in result.Results[0].Error before calling
parseWorktreesFromOutput. Specifically, after executor.Execute(...) succeeds,
verify len(result.Results) > 0 and if not return an appropriate
errors.GitCommandFailed("git worktree list", "no command results"); then check
result.Results[0].Error and if non-nil return errors.GitCommandFailed("git
worktree list", result.Results[0].Error.Error()); only then call
parseWorktreesFromOutput, findMainWorktreePath and resolveWorktreePathByName.
- Around line 94-120: The parseExecInput function has unreachable code and magic
numbers; refactor it to (1) use named return values (e.g., worktreeName, cmd,
cmdArgs, err) to satisfy the linter, (2) introduce small named constants for
index semantics (e.g., dashIndex = 1, firstCmdIndex = 2, minArgsWithDash = 3)
and remove the unreachable len(args) < 2 branch, and (3) simplify flow: validate
len(args) == 0 and == 1 early, set worktreeName = strings.TrimSpace(args[0]),
then if args[dashIndex] == "--" ensure len(args) >= minArgsWithDash and return
worktreeName, args[firstCmdIndex], args[firstCmdIndex+1:], nil; otherwise return
worktreeName, args[firstCmdIndex-1], args[firstCmdIndex:], nil (using the named
return values).
In `@cmd/wtp/worktree_resolver.go`:
- Around line 19-40: resolveWorktreePathByName currently ignores the error from
config.LoadConfig which can leave cfg nil and skip unified-name matching; update
it to mirror availableManagedWorktreeNames by capturing the error and falling
back to the same default config used there (so cfg is never nil), then proceed
to call tryDirectWorktreeMatches/tryMainWorktreeMatches as before; reference
resolveWorktreePathByName, availableManagedWorktreeNames,
isWorktreeManagedCommon, tryDirectWorktreeMatches and tryMainWorktreeMatches to
locate where to apply the change.
🧹 Nitpick comments (4)
cmd/wtp/main_test.go (1)
125-149:createApp()test helper diverges fromnewApp()— consider keeping them in sync.The test helper omits
NewHookCommand()andNewShellInitCommand()that are present in the productionnewApp(). While this pre-dates this PR, addingNewExecCommand()here widens the maintenance surface. Consider callingnewApp()directly in tests instead of maintaining a parallel definition, which would prevent future drift.internal/command/types.go (1)
24-26: Consider passingCommanddirectly toShellExecutor.Executeto future-proof the interface.The
Executesignature now has 4 parameters — each time a new execution concern is added (e.g., env vars, timeout), every implementation and mock must be updated. Passing theCommandstruct would make the interface resilient to future additions:Execute(cmd Command) (string, error)This is a minor concern for now since the parameter count is still manageable.
cmd/wtp/add_test.go (1)
911-933:sequencedCommandExecutoraccumulates all commands butmockCommandExecutoroverwrites on each call.
mockCommandExecutor.Execute(Line 936) usesm.executedCommands = commands(assignment), meaning it only records the last call's commands. This is fine for single-call scenarios, but if any test ever calls it twice, earlier commands are silently lost. Consider usingappendfor consistency withsequencedCommandExecutor, or add a comment noting the single-call assumption.internal/command/shell.go (1)
38-42:hasTerminalIOis clean, but consider whether requiring all three FDs to be TTYs is too strict.Some commands only need stdout to be a TTY (e.g., for colored output). Requiring all three means that
echo hello 2>/dev/null | wtp exec feature -- some-cmdwould fall back to non-interactive even if stdout is a TTY. This is the safer default though, so no change needed now.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4617f25d68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR extends the wtp CLI with the ability to execute arbitrary commands inside an existing or newly created worktree, while enhancing the internal command executor to better support interactive (TTY-wired) commands.
Changes:
- Add
wtp exec <worktree> -- <command> [args...]to run commands inside a resolved worktree. - Add
wtp add --exec "<command>"to run a post-create command after hooks complete. - Introduce interactive execution mode in
internal/commandwith TTY-aware fallback, and refactor shared worktree resolution logic for reuse.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/command/types.go | Adds Interactive to Command and extends ShellExecutor.Execute signature. |
| internal/command/shell.go | Implements TTY detection and interactive stdio wiring for command execution. |
| internal/command/executor.go | Passes Interactive through to the shell executor. |
| internal/command/executor_test.go | Updates mocks/tests for the new executor signature and interactive propagation. |
| cmd/wtp/worktree_resolver.go | Extracts shared worktree resolution + suggestion-name logic for cd/exec. |
| cmd/wtp/exec.go | Introduces the exec command and executes a command in a resolved worktree. |
| cmd/wtp/exec_test.go | Adds tests for parsing and exec behavior using a mock executor. |
| cmd/wtp/cd.go | Switches to the shared worktree resolver + shared suggestions function. |
| cmd/wtp/cd_test.go | Updates tests to use the shared resolver function. |
| cmd/wtp/app.go | Registers the new exec command in the application. |
| cmd/wtp/main_test.go | Updates command list expectations to include exec. |
| cmd/wtp/add.go | Adds --exec flag and runs post-create command after hooks. |
| cmd/wtp/add_test.go | Adds coverage for --exec behavior and failure context retention. |
| README.md | Documents wtp exec and wtp add --exec usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
wtp exec <worktree> -- <command> [args...]command--execoption towtp addto run a command in the newly created worktree after hookswtp cdandwtp execChanges
internal/command: add interactive execution mode and TTY detectioncmd/wtp: addexeccommand, resolver extraction, and wire command into appcmd/wtp add: support--execand error context when post-create command failsREADME: document newwtp execandwtp add --execusageTesting
GOCACHE=$(pwd)/.gocache go test ./...Summary by CodeRabbit
New Features
Documentation