Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pkg/tui/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ type Item struct {

func builtInSessionCommands() []Item {
cmds := []Item{
{
ID: "session.clear",
Label: "Clear",
SlashCommand: "/clear",
Description: "Clear the current tab and start a new session",
Category: "Session",
Execute: func(string) tea.Cmd {
return core.CmdHandler(messages.ClearSessionMsg{})
},
},
{
ID: "session.attach",
Label: "Attach",
Expand Down
9 changes: 9 additions & 0 deletions pkg/tui/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ func TestParseSlashCommand_OtherCommands(t *testing.T) {
assert.True(t, ok)
})

t.Run("clear command", func(t *testing.T) {
t.Parallel()
cmd := ParseSlashCommand("/clear")
require.NotNil(t, cmd)
msg := cmd()
_, ok := msg.(messages.ClearSessionMsg)
assert.True(t, ok)
})

t.Run("star command", func(t *testing.T) {
t.Parallel()
cmd := ParseSlashCommand("/star")
Expand Down
4 changes: 4 additions & 0 deletions pkg/tui/messages/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type (
// NewSessionMsg requests creation of a new session.
NewSessionMsg struct{}

// ClearSessionMsg resets the current tab and starts a new session
// in the same working directory.
ClearSessionMsg struct{}

// ExitSessionMsg requests exiting the current session.
ExitSessionMsg struct{}

Expand Down
44 changes: 44 additions & 0 deletions pkg/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,10 @@ func (m *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// /new spawns a new tab when a session spawner is configured.
return m.handleSpawnSession("")

case messages.ClearSessionMsg:
// /clear resets the current tab with a fresh session in the same working dir.
return m.handleClearSession()

// --- Exit ---

case messages.ExitSessionMsg:
Expand Down Expand Up @@ -1116,6 +1120,46 @@ func (m *appModel) replaceActiveSession(ctx context.Context, sess *session.Sessi
return m, cmd
}

// handleClearSession resets the current tab by creating a fresh session
// in the same working directory.
func (m *appModel) handleClearSession() (tea.Model, tea.Cmd) {
activeID := m.supervisor.ActiveID()

// Cleanup old editor for the active session.
if ed, ok := m.editors[activeID]; ok {
ed.Cleanup()
}

// Create a fresh session in the same app, preserving the working dir.
m.application.NewSession()
newSess := m.application.Session()

// Rebuild all per-session UI components.
m.initSessionComponents(activeID, m.application, newSess)
m.dialogMgr = dialog.New()
m.supervisor.SetRunnerTitle(activeID, "")
m.sessionState.SetSessionTitle("")
m.sessionState.SetPreviousMessage(nil)

// Update persisted tab to point to the new session.
if m.tuiStore != nil {
ctx := context.Background()
oldPersistedID := m.persistedSessionID(activeID)
if err := m.tuiStore.UpdateTabSessionID(ctx, oldPersistedID, newSess.ID); err != nil {
slog.Warn("Failed to update tab session ID after clear", "error", err)
}
}
m.persistActiveTab(newSess.ID)

m.reapplyKeyboardEnhancements()

return m, tea.Sequence(
m.chatPage.Init(),
m.resizeAll(),
m.editor.Focus(),
)
}

// handleSpawnSession spawns a new session.
func (m *appModel) handleSpawnSession(workingDir string) (tea.Model, tea.Cmd) {
// If no working dir specified, open the picker
Expand Down
Loading