tmux backend and p9 server improvements
This commit is contained in:
parent
948e23156a
commit
e3a63dedd8
|
|
@ -820,7 +820,7 @@ func openPromptWindow(sess *SessionInfo) (*acme.Win, error) {
|
|||
return nil, err
|
||||
}
|
||||
w.Name(name)
|
||||
w.Write("tag", []byte("Send "))
|
||||
w.Write("tag", []byte("Send Compact Clear "))
|
||||
w.Ctl("clean")
|
||||
|
||||
// Track window ID client-side
|
||||
|
|
@ -842,19 +842,32 @@ func handlePromptWindow(w *acme.Win, sess *SessionInfo) {
|
|||
|
||||
for e := range w.EventChan() {
|
||||
cmd := string(e.Text)
|
||||
if (e.C2 == 'x' || e.C2 == 'X') && cmd == "Send" {
|
||||
body, err := w.ReadAll("body")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
prompt := strings.TrimSpace(string(body))
|
||||
if prompt != "" {
|
||||
if err := sendPrompt(sess.ID, prompt); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to send: %v\n", err)
|
||||
if e.C2 == 'x' || e.C2 == 'X' {
|
||||
switch cmd {
|
||||
case "Send":
|
||||
body, err := w.ReadAll("body")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
w.Ctl("delete")
|
||||
return
|
||||
prompt := strings.TrimSpace(string(body))
|
||||
if prompt != "" {
|
||||
if err := sendPrompt(sess.ID, prompt); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to send: %v\n", err)
|
||||
continue
|
||||
}
|
||||
w.Ctl("delete")
|
||||
return
|
||||
}
|
||||
case "Compact":
|
||||
if err := controlSession(sess.ID, "compact"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to compact: %v\n", err)
|
||||
}
|
||||
case "Clear":
|
||||
if err := controlSession(sess.ID, "clear"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to clear: %v\n", err)
|
||||
}
|
||||
default:
|
||||
w.WriteEvent(e)
|
||||
}
|
||||
} else {
|
||||
w.WriteEvent(e)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ func sendKeys(target string, keys ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// SendKeysTo is an exported wrapper for sendKeys, for use by backend-specific handlers.
|
||||
func SendKeysTo(target string, keys ...string) error {
|
||||
return sendKeys(target, keys...)
|
||||
}
|
||||
|
||||
// sendLiteral sends literal text to a tmux target (doesn't interpret special chars)
|
||||
func sendLiteral(target, text string) error {
|
||||
_, err := tmuxCmd("send-keys", "-t", target, "-l", text)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ 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
|
||||
|
||||
commands backend.CommandHandler
|
||||
startupHandler StartupHandler
|
||||
|
|
@ -487,6 +488,29 @@ func (s *Session) SendStream(ctx context.Context, prompt string) (io.ReadCloser,
|
|||
return io.NopCloser(strings.NewReader(response)), nil
|
||||
}
|
||||
|
||||
// SendRaw sends literal text followed by Enter to the tmux session.
|
||||
// Used for slash commands like /clear and /compact.
|
||||
func (s *Session) SendRaw(text string) error {
|
||||
s.mu.Lock()
|
||||
target := s.target()
|
||||
s.mu.Unlock()
|
||||
return sendKeys(target, text, "C-m")
|
||||
}
|
||||
|
||||
// Clear sends the /clear command using backend-specific handling if available.
|
||||
func (s *Session) Clear() error {
|
||||
s.mu.Lock()
|
||||
target := s.target()
|
||||
handler := s.clearHandler
|
||||
s.mu.Unlock()
|
||||
|
||||
if handler != nil {
|
||||
return handler(target)
|
||||
}
|
||||
// Default: just send /clear + Enter
|
||||
return sendKeys(target, "/clear", "C-m")
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@ type StateInspector interface {
|
|||
// For positional CLIs: replace the relevant positional argument
|
||||
type ModelResolver func(cmd []string, model string) []string
|
||||
|
||||
// ClearHandler handles the /clear command for a specific backend.
|
||||
// It receives the tmux target and should send the appropriate keys.
|
||||
// Returns nil on success.
|
||||
type ClearHandler func(target string) error
|
||||
|
||||
// Config holds tmux backend configuration
|
||||
type Config struct {
|
||||
Name string
|
||||
|
|
@ -56,6 +61,7 @@ type Config struct {
|
|||
StateInspector StateInspector // Optional: for process tree inspection
|
||||
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
|
||||
}
|
||||
|
||||
// Backend implements backend.Backend for tmux-based CLI tools
|
||||
|
|
@ -351,6 +357,7 @@ func (b *Backend) CreateSession(ctx context.Context, opts backend.SessionOptions
|
|||
sandbox: sbx,
|
||||
model: opts.Model,
|
||||
modelResolver: b.cfg.ModelResolver,
|
||||
clearHandler: b.cfg.ClearHandler,
|
||||
pid: pid,
|
||||
state: "starting",
|
||||
createdAt: time.Now(),
|
||||
|
|
|
|||
|
|
@ -33,10 +33,20 @@ func NewKiroCLI(nsSuffix string) backend.Backend {
|
|||
},
|
||||
StartupTime: 30 * time.Second,
|
||||
StateInspector: &kiroStateInspector{},
|
||||
ClearHandler: kiroClearHandler,
|
||||
NsSuffix: nsSuffix,
|
||||
})
|
||||
}
|
||||
|
||||
// kiroClearHandler sends /clear and confirms the "y" prompt for kiro-cli
|
||||
func kiroClearHandler(target string) error {
|
||||
if err := tmux.SendKeysTo(target, "/clear", "C-m"); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
return tmux.SendKeysTo(target, "y", "C-m")
|
||||
}
|
||||
|
||||
// kiroStateInspector implements StateInspector for kiro-cli
|
||||
type kiroStateInspector struct{}
|
||||
|
||||
|
|
|
|||
|
|
@ -845,6 +845,18 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if err := sess.Refresh(ctx); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
case "clear":
|
||||
if tmuxSess, ok := sess.(*tmux.Session); ok {
|
||||
if err := tmuxSess.Clear(); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
}
|
||||
case "compact":
|
||||
if tmuxSess, ok := sess.(*tmux.Session); ok {
|
||||
if err := tmuxSess.SendRaw("/compact"); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
}
|
||||
case "complete":
|
||||
if len(args) < 2 {
|
||||
return errFcall(fc, "usage: complete <msg-id>")
|
||||
|
|
|
|||
Reference in New Issue