diff options
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 +} |
