diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-08 16:31:40 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-08 16:31:40 +0200 |
| commit | c802ba5803de1a53749bb5c4ecbc0159fceb385f (patch) | |
| tree | 02e612286f36bc6c65563bc33cf53639817d2db1 /internal/tmuxedit/claude_agent.go | |
| parent | 887d7bc186db90c3903851b0f1db2d24df5d7a7b (diff) | |
refactor tmuxedit to Agent interface with cursor/claude/config implementations
Replace monolithic AgentConfig struct with an Agent interface backed by
baseAgent defaults and separate implementations for cursor (box-drawing
extraction, bulk backspace clearing) and claude (section-scoped extraction
with continuation lines, vim clearing). Simple agents (amp, aider) and
user-defined agents use configAgent with baseAgent defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/tmuxedit/claude_agent.go')
| -rw-r--r-- | internal/tmuxedit/claude_agent.go | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/internal/tmuxedit/claude_agent.go b/internal/tmuxedit/claude_agent.go new file mode 100644 index 0000000..72ba107 --- /dev/null +++ b/internal/tmuxedit/claude_agent.go @@ -0,0 +1,85 @@ +package tmuxedit + +import ( + "regexp" + "strings" +) + +// claudeAgent handles Claude Code's ❯ prompt between ──── horizontal rules. +// Claude Code runs in actual vim mode, so clearing uses vim commands. +// Wrapped text appears as indented continuation lines without ❯. +type claudeAgent struct{ baseAgent } + +// newClaudeAgent returns a claudeAgent with the default configuration. +// SectionPattern scopes extraction to the last ─── delimited area, avoiding +// false positives from ❯ in previous messages. +func newClaudeAgent() *claudeAgent { + return &claudeAgent{baseAgent{ + name: "claude", + displayName: "Claude Code", + detectPattern: `(❯|(?i)claude code|(?i)anthropic)`, + sectionPat: `^─{5,}`, + promptPat: `(?m)❯\s*(.+)$`, + clearFirst: true, + clearKeys: "Escape gg C-v G d i", + newlineKeys: "S-Enter", + submitKeys: "Enter", + }} +} + +// ExtractPrompt extracts the prompt text from the last section between ───── +// rules. Within the scoped section, all non-empty lines are collected: +// ❯-prefixed lines have the prefix stripped, and indented continuation lines +// (wrapped text without ❯) are included as-is after trimming. +func (c *claudeAgent) ExtractPrompt(paneContent string) string { + if c.promptPat == "" { + return "" + } + re, err := regexp.Compile(c.promptPat) + if err != nil { + return "" + } + // Scope to the last section between ───── delimiters + content := scopeToLastSection(paneContent, c.sectionPat) + // Collect ❯-prefixed lines and their continuation lines (indented + // wrapped text without ❯). Only include non-❯ lines that directly + // follow a ❯-matched line to avoid picking up unrelated content. + paneLines := strings.Split(content, "\n") + var lines []string + inPrompt := false + for _, line := range paneLines { + m := re.FindStringSubmatch(line) + if len(m) >= 2 { + // ❯-prefixed line: use the captured text + cleaned := stripNoise(m[1], c.stripPatterns) + if cleaned != "" { + lines = append(lines, cleaned) + } + inPrompt = true + } else if inPrompt { + // Non-❯ line after a prompt: include indented continuation text + trimmed := strings.TrimSpace(line) + if trimmed != "" { + lines = append(lines, trimmed) + } else { + // Empty line breaks the continuation + inPrompt = false + } + } + } + return strings.Join(lines, "\n") +} + +// ClearInput sends vim commands to clear Claude Code's input: +// Escape to ensure normal mode, gg to go to top, C-v G d to visual-block +// select all and delete, then i to re-enter insert mode. +func (c *claudeAgent) ClearInput(paneID string) error { + if !c.clearFirst || c.clearKeys == "" { + return nil + } + if err := sendClearSequence(paneID, c.clearKeys); err != nil { + return err + } + sleepAfterClear() + return nil +} |
