summaryrefslogtreecommitdiff
path: root/internal/tmuxedit/config_agent.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-08 16:31:40 +0200
committerPaul Buetow <paul@buetow.org>2026-02-08 16:31:40 +0200
commitc802ba5803de1a53749bb5c4ecbc0159fceb385f (patch)
tree02e612286f36bc6c65563bc33cf53639817d2db1 /internal/tmuxedit/config_agent.go
parent887d7bc186db90c3903851b0f1db2d24df5d7a7b (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/config_agent.go')
-rw-r--r--internal/tmuxedit/config_agent.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/internal/tmuxedit/config_agent.go b/internal/tmuxedit/config_agent.go
new file mode 100644
index 0000000..2773025
--- /dev/null
+++ b/internal/tmuxedit/config_agent.go
@@ -0,0 +1,134 @@
+package tmuxedit
+
+import (
+ "strings"
+
+ "codeberg.org/snonux/hexai/internal/appconfig"
+)
+
+// configAgent uses baseAgent defaults for all operations. It serves
+// user-defined agents from TOML config and simple built-ins (amp, aider)
+// that don't need specialized extraction or clearing logic.
+type configAgent struct{ baseAgent }
+
+// builtinAgents returns the default set of agent implementations. Order
+// matters: agents with distinctive UI elements (box-drawing, etc.) are
+// checked first to avoid false positives from model names like "Claude
+// 4.5 Sonnet" appearing in other agents' panes.
+func builtinAgents() []Agent {
+ return []Agent{
+ newCursorAgent(),
+ newClaudeAgent(),
+ &configAgent{baseAgent{
+ name: "amp",
+ displayName: "Amp",
+ detectPattern: `(?i)(amp|sourcegraph)`,
+ promptPat: `(?m)>\s*(.+)$`,
+ clearFirst: true,
+ clearKeys: "C-u",
+ newlineKeys: "S-Enter",
+ submitKeys: "Enter",
+ }},
+ &configAgent{baseAgent{
+ name: "aider",
+ displayName: "Aider",
+ detectPattern: `(?i)aider`,
+ promptPat: `(?m)>\s*(.+)$`,
+ clearFirst: true,
+ clearKeys: "C-u",
+ newlineKeys: "",
+ submitKeys: "Enter",
+ }},
+ }
+}
+
+// genericAgent returns a fallback agent with no detection or prompt extraction.
+// The user gets a blank editor and text is sent verbatim.
+func genericAgent() Agent {
+ return &configAgent{baseAgent{
+ name: "generic",
+ displayName: "Generic",
+ newlineKeys: "",
+ submitKeys: "Enter",
+ }}
+}
+
+// resolveAgents merges built-in agent defaults with user-provided overrides
+// from config. Agents are matched by name (case-insensitive); user config
+// wins field-by-field over builtins. The Configurable interface provides
+// access to baseAgent fields for merging.
+func resolveAgents(cfgAgents []appconfig.TmuxEditAgentCfg) []Agent {
+ agents := builtinAgents()
+ for _, ca := range cfgAgents {
+ merged := false
+ for i, a := range agents {
+ if !strings.EqualFold(a.Name(), ca.Name) {
+ continue
+ }
+ if c, ok := a.(Configurable); ok {
+ mergeAgentConfig(c.Base(), ca)
+ }
+ merged = true
+ _ = i // index not needed; we modify through the pointer
+ break
+ }
+ if !merged {
+ agents = append(agents, agentFromConfig(ca))
+ }
+ }
+ return agents
+}
+
+// mergeAgentConfig overrides fields in base with non-zero values from cfg.
+// It modifies the baseAgent in place via pointer.
+func mergeAgentConfig(base *baseAgent, cfg appconfig.TmuxEditAgentCfg) {
+ if s := strings.TrimSpace(cfg.DisplayName); s != "" {
+ base.displayName = s
+ }
+ if s := strings.TrimSpace(cfg.DetectPattern); s != "" {
+ base.detectPattern = s
+ }
+ if s := strings.TrimSpace(cfg.SectionPattern); s != "" {
+ base.sectionPat = s
+ }
+ if s := strings.TrimSpace(cfg.PromptPattern); s != "" {
+ base.promptPat = s
+ }
+ if len(cfg.StripPatterns) > 0 {
+ base.stripPatterns = cfg.StripPatterns
+ }
+ if cfg.ClearFirst != nil {
+ base.clearFirst = *cfg.ClearFirst
+ }
+ if s := strings.TrimSpace(cfg.ClearKeys); s != "" {
+ base.clearKeys = s
+ }
+ if s := strings.TrimSpace(cfg.NewlineKeys); s != "" {
+ base.newlineKeys = s
+ }
+ if s := strings.TrimSpace(cfg.SubmitKeys); s != "" {
+ base.submitKeys = s
+ }
+}
+
+// agentFromConfig creates a new configAgent from a user config entry.
+func agentFromConfig(cfg appconfig.TmuxEditAgentCfg) Agent {
+ b := baseAgent{
+ name: strings.TrimSpace(cfg.Name),
+ displayName: strings.TrimSpace(cfg.DisplayName),
+ detectPattern: strings.TrimSpace(cfg.DetectPattern),
+ sectionPat: strings.TrimSpace(cfg.SectionPattern),
+ promptPat: strings.TrimSpace(cfg.PromptPattern),
+ stripPatterns: cfg.StripPatterns,
+ clearKeys: strings.TrimSpace(cfg.ClearKeys),
+ newlineKeys: strings.TrimSpace(cfg.NewlineKeys),
+ submitKeys: strings.TrimSpace(cfg.SubmitKeys),
+ }
+ if cfg.ClearFirst != nil {
+ b.clearFirst = *cfg.ClearFirst
+ }
+ if b.displayName == "" {
+ b.displayName = b.name
+ }
+ return &configAgent{b}
+}