Allow users to optionally start an AI agent when creating a new shell, using the same agent list and skip-permissions option as worktree creation. Critical constraint: No extra clicks/keystrokes for users who just want a plain shell.
Press 'n' → Type selector (Shell/Workspace) → Select Shell → Name input → Enter → Shell created
- ~3 keystrokes for a named shell, ~2 for unnamed
- No agent involvement
Press 'n' → Type selector → Select Shell → Name input [Enter = done]
→ Tab → Agent list (default: None) [Enter = done]
→ Tab → Skip perms checkbox [Enter = done]
Key insight: Enter on the name field creates the shell immediately (same as today). Only users who Tab forward see agent selection. Default agent is "None", so even if they Tab through and press Enter, they get a plain shell.
- Enter on name field = immediate shell creation (current behavior preserved)
- Agent field only reached via Tab
- Default agent = "None"
Use same AgentTypeOrder from worktree creation:
- Claude Code
- Codex CLI
- Gemini CLI
- Cursor Agent
- OpenCode
- None (default, first in list for shells)
- Checkbox appears when agent with skip-perm flag is selected
- Same flags as worktree:
--dangerously-skip-permissions,--yolo,-f, etc.
- Shells with agents use same prefix:
sidecar-sh-{project}-{index} - Agent started in project root (not worktree, since shells don't have one)
- Agent type stored in
ShellSession.Agent.Typefor reconnection
File: internal/plugins/workspace/plugin.go
Add fields for shell agent selection:
// Type selector modal - shell agent selection
typeSelectorAgentIdx int
typeSelectorAgentType AgentType
typeSelectorSkipPerms bool
typeSelectorFocusField int // 0=name, 1=agent, 2=skipPerms, 3=buttonsFile: internal/plugins/workspace/view_modals.go (around line 1117)
When shell is selected in type selector:
- Show name input (existing)
- Show agent list (new) - styled like worktree create modal
- Show skip perms checkbox if applicable (new)
- Show Create/Cancel buttons
Layout:
┌─ Create ─────────────────────────┐
│ ○ Shell ● Workspace │
│ │
│ Name: [my-shell________] │
│ │
│ Agent: │
│ ▸ None │
│ Claude Code │
│ Codex CLI │
│ ... │
│ │
│ ☐ Skip permissions │
│ │
│ [ Create ] [ Cancel ] │
└──────────────────────────────────┘
File: internal/plugins/workspace/keys.go
Modify handleTypeSelectorKeys():
Current logic (Enter on name):
if p.typeSelectorIdx == 0 { // Shell selected
name := p.typeSelectorNameInput.Value()
p.clearTypeSelectorModal()
return p.createNewShell(name)
}New logic:
if p.typeSelectorIdx == 0 { // Shell selected
switch p.typeSelectorFocusField {
case 0: // Name field - Enter creates shell immediately (no extra steps!)
return p.createShellWithAgent()
case 1: // Agent list - Enter confirms agent, stays in modal
p.typeSelectorFocusField = 2 // move to skip perms or buttons
case 2: // Skip perms or buttons
if onSkipPerms {
p.typeSelectorFocusField = 3
} else {
return p.createShellWithAgent()
}
case 3: // Buttons
return p.createShellWithAgent()
}
}Add Tab/Shift+Tab navigation between fields:
- Tab: name → agent → skipPerms (if visible) → buttons → name
- Shift+Tab: reverse
Add j/k for agent list navigation when focused.
File: internal/plugins/workspace/shell.go
New function createShellWithAgent():
func (p *Plugin) createShellWithAgent() (tea.Model, tea.Cmd) {
name := p.typeSelectorNameInput.Value()
agentType := p.typeSelectorAgentType
skipPerms := p.typeSelectorSkipPerms
p.clearTypeSelectorModal()
// Create shell session
cmd := p.createNewShell(name)
// If agent selected (not None), start it after shell creation
if agentType != AgentNone && agentType != "" {
// Return batch: create shell, then start agent in that shell
return p, tea.Batch(cmd, p.startAgentInShellCmd(name, agentType, skipPerms))
}
return p, cmd
}File: internal/plugins/workspace/agent.go
New function startAgentInShell():
func (p *Plugin) startAgentInShell(shell *ShellSession, agentType AgentType, skipPerms bool) tea.Cmd {
// Build agent command (reuse existing buildAgentCommand logic)
baseCmd := AgentCommands[agentType]
if skipPerms {
if flag := SkipPermissionsFlags[agentType]; flag != "" {
baseCmd += " " + flag
}
}
// Send to existing tmux session
cmd := exec.Command("tmux", "send-keys", "-t", shell.TmuxName, baseCmd, "Enter")
return func() tea.Msg {
if err := cmd.Run(); err != nil {
return ShellAgentErrorMsg{Shell: shell, Err: err}
}
return ShellAgentStartedMsg{Shell: shell, AgentType: agentType}
}
}File: internal/plugins/workspace/types.go
Add agent tracking to ShellSession:
type ShellSession struct {
Name string
TmuxName string
Agent Agent
ChosenAgent AgentType // NEW: track which agent was selected
SkipPerms bool // NEW: track skip permissions setting
}File: internal/plugins/workspace/update.go
Add handlers:
case ShellAgentStartedMsg:
for i, shell := range p.shells {
if shell.TmuxName == msg.Shell.TmuxName {
p.shells[i].ChosenAgent = msg.AgentType
p.shells[i].Agent.Type = msg.AgentType
break
}
}
return p, nil
case ShellAgentErrorMsg:
p.setError(fmt.Sprintf("Failed to start agent: %v", msg.Err))
return p, nilFile: internal/plugins/workspace/shell.go
Modify detectShellStatus() to handle agent-specific status patterns (waiting, done, error) similar to worktree agent detection.
Update shell list rendering to show:
- Agent type icon/name when agent is running
- Status indicator (Active/Waiting/Done) like worktrees
- Skip perms indicator if enabled
- Press
n→ Shell → Enter on empty name → unnamed shell created (2 keystrokes same as before) - Press
n→ Shell → type name → Enter → named shell created (same as before) - Press
A→ shell created immediately (same as before)
- Press
n→ Shell → Tab → select Claude → Enter → shell with Claude starts - Press
n→ Shell → Tab → select Claude → Tab → toggle skip perms → Enter → shell with Claude + skip perms - Shell with agent shows correct status (Active/Waiting/Done)
- Reconnect to shell with agent after restart preserves agent type
- Mouse: click agent in list selects it
- Mouse: click skip perms checkbox toggles it
- Agent CLI not installed → graceful error
- Shell creation fails → no agent start attempted
- Agent start fails → shell still usable
| File | Changes |
|---|---|
internal/plugins/workspace/plugin.go |
Add state fields for shell agent selection |
internal/plugins/workspace/view_modals.go |
Extend type selector modal rendering |
internal/plugins/workspace/keys.go |
Add Tab/j/k navigation, modify Enter behavior |
internal/plugins/workspace/shell.go |
New createShellWithAgent(), update status detection |
internal/plugins/workspace/agent.go |
New startAgentInShell() |
internal/plugins/workspace/types.go |
Add fields to ShellSession |
internal/plugins/workspace/update.go |
Handle ShellAgentStartedMsg |
- State management: Low - reuses existing patterns
- UI changes: Medium - extends type selector modal
- Agent integration: Low - reuses existing agent start logic
- Risk: Low - preserves existing behavior, adds opt-in feature
Could add "Shell with Agent" as third option in type selector. Rejected because:
- Makes type selector busier
- Less discoverable (user might not notice the option)
- Duplicates UI (two "shell" options)
The Tab-to-reveal approach is cleaner and matches how advanced options work in the worktree modal.