Add backend-specific handlers for clear, compact, resume commands

- Add ClearHandler, CompactHandler, ResumeHandler callbacks to tmux.Config
- kiro-cli: clear/resume need 250ms delay + confirmation key
- claude: clear/compact are simple /command[ENTER], resume not implemented
- Add Resume tag and handler to Assist prompt window
- Update server.go to use new Session methods
This commit is contained in:
Levi Neely 2026-03-05 09:41:05 +01:00
parent e3a63dedd8
commit c0d09125ca
6 changed files with 82 additions and 2 deletions

View File

@ -820,7 +820,7 @@ func openPromptWindow(sess *SessionInfo) (*acme.Win, error) {
return nil, err
}
w.Name(name)
w.Write("tag", []byte("Send Compact Clear "))
w.Write("tag", []byte("Send Compact Clear Resume "))
w.Ctl("clean")
// Track window ID client-side
@ -866,6 +866,10 @@ func handlePromptWindow(w *acme.Win, sess *SessionInfo) {
if err := controlSession(sess.ID, "clear"); err != nil {
fmt.Fprintf(os.Stderr, "Failed to clear: %v\n", err)
}
case "Resume":
if err := controlSession(sess.ID, "resume"); err != nil {
fmt.Fprintf(os.Stderr, "Failed to resume: %v\n", err)
}
default:
w.WriteEvent(e)
}

View File

@ -42,6 +42,8 @@ type Session struct {
model string // Active model override (empty = backend default)
modelResolver ModelResolver // Optional: modifies command to include model selection
clearHandler ClearHandler // Optional: backend-specific /clear handling
resumeHandler ResumeHandler // Optional: backend-specific resume handling
compactHandler CompactHandler // Optional: backend-specific /compact handling
commands backend.CommandHandler
startupHandler StartupHandler
@ -511,6 +513,32 @@ func (s *Session) Clear() error {
return sendKeys(target, "/clear", "C-m")
}
// Resume resumes the most recent conversation using backend-specific handling.
func (s *Session) Resume() error {
s.mu.Lock()
target := s.target()
handler := s.resumeHandler
s.mu.Unlock()
if handler != nil {
return handler(target)
}
return fmt.Errorf("resume not implemented for this backend")
}
// Compact sends the /compact command using backend-specific handling.
func (s *Session) Compact() error {
s.mu.Lock()
target := s.target()
handler := s.compactHandler
s.mu.Unlock()
if handler != nil {
return handler(target)
}
return fmt.Errorf("compact not implemented for this backend")
}
// stopInternal performs the actual stop operation.
// Caller must hold s.mu and have set s.transitioning = true.
// This method will unlock s.mu while performing stop operations.

View File

@ -49,6 +49,16 @@ type ModelResolver func(cmd []string, model string) []string
// Returns nil on success.
type ClearHandler func(target string) error
// ResumeHandler handles the resume command for a specific backend.
// It receives the tmux target and should send the appropriate keys.
// Returns nil on success.
type ResumeHandler func(target string) error
// CompactHandler handles the /compact command for a specific backend.
// It receives the tmux target and should send the appropriate keys.
// Returns nil on success.
type CompactHandler func(target string) error
// Config holds tmux backend configuration
type Config struct {
Name string
@ -62,6 +72,8 @@ type Config struct {
NsSuffix string // Optional: namespace suffix (e.g., "0" for :0)
ModelResolver ModelResolver // Optional: modifies command to include model selection
ClearHandler ClearHandler // Optional: backend-specific /clear handling
ResumeHandler ResumeHandler // Optional: backend-specific resume handling
CompactHandler CompactHandler // Optional: backend-specific /compact handling
}
// Backend implements backend.Backend for tmux-based CLI tools
@ -358,6 +370,8 @@ func (b *Backend) CreateSession(ctx context.Context, opts backend.SessionOptions
model: opts.Model,
modelResolver: b.cfg.ModelResolver,
clearHandler: b.cfg.ClearHandler,
resumeHandler: b.cfg.ResumeHandler,
compactHandler: b.cfg.CompactHandler,
pid: pid,
state: "starting",
createdAt: time.Now(),

View File

@ -46,10 +46,22 @@ func newClaudeWithCommand(command []string, nsSuffix string) backend.Backend {
Commands: &claudeCommands{},
StartupHandler: &claudeStartupHandler{},
StateInspector: &claudeStateInspector{},
ClearHandler: claudeClearHandler,
CompactHandler: claudeCompactHandler,
NsSuffix: nsSuffix,
})
}
// claudeClearHandler sends /clear for claude
func claudeClearHandler(target string) error {
return tmux.SendKeysTo(target, "/clear", "C-m")
}
// claudeCompactHandler sends /compact for claude
func claudeCompactHandler(target string) error {
return tmux.SendKeysTo(target, "/compact", "C-m")
}
// claudeStateInspector implements StateInspector for claude CLI
type claudeStateInspector struct{}

View File

@ -34,6 +34,8 @@ func NewKiroCLI(nsSuffix string) backend.Backend {
StartupTime: 30 * time.Second,
StateInspector: &kiroStateInspector{},
ClearHandler: kiroClearHandler,
ResumeHandler: kiroResumeHandler,
CompactHandler: kiroCompactHandler,
NsSuffix: nsSuffix,
})
}
@ -47,6 +49,20 @@ func kiroClearHandler(target string) error {
return tmux.SendKeysTo(target, "y", "C-m")
}
// kiroResumeHandler sends /chat resume and confirms selection for kiro-cli
func kiroResumeHandler(target string) error {
if err := tmux.SendKeysTo(target, "/chat resume", "C-m"); err != nil {
return err
}
time.Sleep(250 * time.Millisecond)
return tmux.SendKeysTo(target, "C-m")
}
// kiroCompactHandler sends /compact for kiro-cli
func kiroCompactHandler(target string) error {
return tmux.SendKeysTo(target, "/compact", "C-m")
}
// kiroStateInspector implements StateInspector for kiro-cli
type kiroStateInspector struct{}

View File

@ -853,7 +853,13 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
}
case "compact":
if tmuxSess, ok := sess.(*tmux.Session); ok {
if err := tmuxSess.SendRaw("/compact"); err != nil {
if err := tmuxSess.Compact(); err != nil {
return errFcall(fc, err.Error())
}
}
case "resume":
if tmuxSess, ok := sess.(*tmux.Session); ok {
if err := tmuxSess.Resume(); err != nil {
return errFcall(fc, err.Error())
}
}