switched kiro to use tmux instead of pty, probably still buggy

This commit is contained in:
Levi Neely 2026-02-10 20:36:25 +01:00
parent d33bede0f1
commit 9be10a14de
4 changed files with 90 additions and 29 deletions

BIN
acme-q

Binary file not shown.

View File

@ -3,7 +3,7 @@ package backends
import (
"acme-q/internal/backend"
"acme-q/internal/backend/pty"
"acme-q/internal/backend/tmux"
"context"
"fmt"
"log"
@ -16,7 +16,7 @@ import (
// NewKiroCLI creates a kiro-cli backend
func NewKiroCLI() backend.Backend {
return pty.New(pty.Config{
return tmux.New(tmux.Config{
Name: "kiro-cli",
Command: []string{"kiro-cli", "chat", "--trust-all-tools"},
Environment: map[string]string{
@ -24,7 +24,7 @@ func NewKiroCLI() backend.Backend {
"NO_COLOR": "1",
"COLUMNS": "999",
},
PTYSize: pty.PTYSize{
TmuxSize: tmux.TmuxSize{
Rows: 40,
Cols: 120,
},
@ -89,24 +89,88 @@ func (c *kiroCleaner) cleanResponse(raw string) string {
lines := strings.Split(clean, "\n")
var result []string
foundUserPrompt := false
inResponse := false
for _, line := range lines {
trimmed := strings.TrimSpace(line)
// Check for response marker "> " (not in prompt brackets)
if idx := strings.Index(line, "> "); idx >= 0 {
if !strings.Contains(line, "[") || !strings.Contains(line, "]") {
inResponse = true
text := strings.TrimSpace(line[idx+2:])
if text != "" {
result = append(result, text)
}
continue
}
// Look for user prompt (starts with "!>")
if !foundUserPrompt && strings.HasPrefix(trimmed, "!>") {
foundUserPrompt = true
continue
}
// Skip noise
// Skip echoed user input (the line right after !> that doesn't have markers)
if foundUserPrompt && !inResponse && trimmed != "" &&
!strings.Contains(trimmed, "Thinking...") &&
!strings.Contains(trimmed, "I will run") &&
!strings.Contains(trimmed, "using tool:") &&
!strings.Contains(trimmed, "Purpose:") &&
!strings.Contains(trimmed, ">") {
continue
}
// Stop at next prompt (starts with "!>") - but only after we've started collecting
if foundUserPrompt && inResponse && strings.HasPrefix(trimmed, "!>") {
break
}
// After user prompt, start collecting non-noise content
if foundUserPrompt && trimmed != "" {
// Check if line contains actual content (even if it has noise)
// Important markers: "I will run", "using tool:", "Purpose:", ">", or actual content after spinners
hasContent := false
text := trimmed
// If line contains "Thinking..." followed by content, extract the content
if strings.Contains(line, "Thinking...") {
idx := strings.Index(line, "Thinking...")
afterThinking := strings.TrimSpace(line[idx+11:]) // "Thinking..." is 11 chars
if afterThinking != "" {
text = afterThinking
hasContent = true
}
}
// Check for important markers
if !hasContent && (strings.Contains(trimmed, "I will run") ||
strings.Contains(trimmed, "using tool:") ||
strings.Contains(trimmed, "Purpose:") ||
strings.Contains(trimmed, ">")) {
hasContent = true
}
// If no content markers and it's noise, skip
if !hasContent && c.isNoise(line) {
continue
}
// If we got here, it's not pure noise
if !hasContent {
text = trimmed
}
// Start collecting
if !inResponse {
inResponse = true
}
// If line has ">", strip it (for assistant response lines)
if strings.Contains(text, ">") {
idx := strings.Index(text, ">")
if idx == 0 || (idx > 0 && text[idx-1] != '!') {
text = strings.TrimSpace(text[idx+1:])
}
}
if text != "" {
result = append(result, text)
}
continue
}
// Before user prompt, skip noise and look for hooks
if c.isNoise(line) {
continue
}
@ -116,10 +180,6 @@ func (c *kiroCleaner) cleanResponse(raw string) string {
result = append(result, trimmed)
continue
}
if inResponse && trimmed != "" {
result = append(result, line)
}
}
return strings.Join(result, "\n")
@ -169,6 +229,7 @@ func (c *kiroCleaner) isNoise(line string) bool {
// Check for noise patterns
noisePatterns := []string{
"Thinking...",
"▸ Credits:",
"▸ Time:",
}

View File

@ -3,7 +3,7 @@ package p9
import (
"acme-q/internal/backend"
"acme-q/internal/backend/pty"
"acme-q/internal/backend/tmux"
"acme-q/internal/backends"
"acme-q/internal/session"
"context"
@ -374,9 +374,9 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
if err != nil {
return errFcall(fc, "invalid winid")
}
// Set WinID if it's a PTY session
if ptySess, ok := sess.(*pty.Session); ok {
ptySess.SetWinID(id)
// Set WinID if it's a tmux session
if tmuxSess, ok := sess.(*tmux.Session); ok {
tmuxSess.SetWinID(id)
}
return &plan9.Fcall{Type: plan9.Rwrite, Tag: fc.Tag, Count: uint32(len(fc.Data))}
}
@ -498,11 +498,11 @@ func (s *Server) getSessionFile(sess backend.Session, idx int) string {
switch idx {
case fileOut:
// Output from last command - only available for PTY sessions
if ptySess, ok := sess.(*pty.Session); ok {
// Output from last command - only available for tmux sessions
if tmuxSess, ok := sess.(*tmux.Session); ok {
// We don't have a direct Output() method anymore
// This would need to be stored separately if needed
_ = ptySess
_ = tmuxSess
}
return ""
case fileErr:

View File

@ -3,7 +3,7 @@ package main
import (
"acme-q/internal/backend"
"acme-q/internal/backend/pty"
"acme-q/internal/backend/tmux"
"acme-q/internal/backends"
"acme-q/internal/p9"
"acme-q/internal/session"
@ -270,9 +270,9 @@ func openChatWindow(sess backend.Session) (*acme.Win, error) {
w.Fprintf("body", "# Session %s\n# cwd: %s\n\nUSER:\n", sess.ID(), meta.Cwd)
w.Ctl("clean")
// Set window ID on session (if it's a PTY session)
if ptySess, ok := sess.(*pty.Session); ok {
ptySess.SetWinID(w.ID())
// Set window ID on session (if it's a tmux session)
if tmuxSess, ok := sess.(*tmux.Session); ok {
tmuxSess.SetWinID(w.ID())
}
go handleChatWindow(w, sess)