summaryrefslogtreecommitdiff
path: root/internal/hexaiaction
diff options
context:
space:
mode:
Diffstat (limited to 'internal/hexaiaction')
-rw-r--r--internal/hexaiaction/custom_action_test.go2
-rw-r--r--internal/hexaiaction/custom_exec_more_test.go71
-rw-r--r--internal/hexaiaction/run.go7
-rw-r--r--internal/hexaiaction/tui.go2
-rw-r--r--internal/hexaiaction/tui_custom.go1
-rw-r--r--internal/hexaiaction/types.go5
6 files changed, 51 insertions, 37 deletions
diff --git a/internal/hexaiaction/custom_action_test.go b/internal/hexaiaction/custom_action_test.go
index 72cfbc4..71319f4 100644
--- a/internal/hexaiaction/custom_action_test.go
+++ b/internal/hexaiaction/custom_action_test.go
@@ -23,7 +23,7 @@ func TestActionCustom_UsesEditorPrompt(t *testing.T) {
// Seam: choose custom, fake client, and fake editor
oldChoose := chooseActionFn
oldNew := newClientFromApp
- chooseActionFn = func() (ActionKind, error) { return ActionCustom, nil }
+ chooseActionFn = func() (ActionKind, error) { return ActionCustomPrompt, nil }
newClientFromApp = func(_ appconfig.App) (llm.Client, error) { return llmFake2{}, nil }
t.Cleanup(func() { chooseActionFn = oldChoose; newClientFromApp = oldNew })
diff --git a/internal/hexaiaction/custom_exec_more_test.go b/internal/hexaiaction/custom_exec_more_test.go
index 657d0d8..de45d26 100644
--- a/internal/hexaiaction/custom_exec_more_test.go
+++ b/internal/hexaiaction/custom_exec_more_test.go
@@ -1,48 +1,53 @@
package hexaiaction
import (
- "context"
- "strings"
- "testing"
+ "context"
+ "strings"
+ "testing"
- "codeberg.org/snonux/hexai/internal/appconfig"
- "codeberg.org/snonux/hexai/internal/llm"
+ "codeberg.org/snonux/hexai/internal/appconfig"
+ "codeberg.org/snonux/hexai/internal/llm"
)
// capDoer captures last LLM messages for assertions.
type capDoer struct{ last []llm.Message }
-func (c *capDoer) Chat(_ context.Context, msgs []llm.Message, _ ...llm.RequestOption) (string, error) { c.last = append([]llm.Message{}, msgs...); return "OK", nil }
+
+func (c *capDoer) Chat(_ context.Context, msgs []llm.Message, _ ...llm.RequestOption) (string, error) {
+ c.last = append([]llm.Message{}, msgs...)
+ return "OK", nil
+}
func (*capDoer) DefaultModel() string { return "m" }
func TestExecuteAction_Custom_ClearsSelection(t *testing.T) {
- cfg := appconfig.Load(nil)
- parts := InputParts{Selection: "code"}
- selectedCustom = &appconfig.CustomAction{ID: "x", Title: "X", Instruction: "Do it"}
- _, _ = executeAction(context.Background(), ActionCustom, parts, cfg, fakeDoer{"OK"}, nil)
- if selectedCustom != nil {
- t.Fatalf("expected selectedCustom cleared after execution")
- }
+ cfg := appconfig.Load(nil)
+ parts := InputParts{Selection: "code"}
+ selectedCustom = &appconfig.CustomAction{ID: "x", Title: "X", Instruction: "Do it"}
+ _, _ = executeAction(context.Background(), ActionCustom, parts, cfg, fakeDoer{"OK"}, nil)
+ if selectedCustom != nil {
+ t.Fatalf("expected selectedCustom cleared after execution")
+ }
}
func TestRunCustom_UserTemplate_InjectsDiagnostics(t *testing.T) {
- cfg := appconfig.Load(nil)
- parts := InputParts{Selection: "code", Diagnostics: []string{"L1", "L2"}}
- ca := appconfig.CustomAction{ID: "y", Title: "Y", User: "{{diagnostics}}\n{{selection}}"}
- cap := &capDoer{}
- _, err := runCustom(context.Background(), cfg, cap, ca, parts)
- if err != nil { t.Fatalf("runCustom error: %v", err) }
- if len(cap.last) == 0 {
- t.Fatalf("expected messages captured")
- }
- // user message should contain diagnostics and selection
- found := false
- for _, m := range cap.last {
- if m.Role == "user" && strings.Contains(m.Content, "L1") && strings.Contains(m.Content, "code") {
- found = true
- }
- }
- if !found {
- t.Fatalf("expected diagnostics and selection in user message: %+v", cap.last)
- }
+ cfg := appconfig.Load(nil)
+ parts := InputParts{Selection: "code", Diagnostics: []string{"L1", "L2"}}
+ ca := appconfig.CustomAction{ID: "y", Title: "Y", User: "{{diagnostics}}\n{{selection}}"}
+ cap := &capDoer{}
+ _, err := runCustom(context.Background(), cfg, cap, ca, parts)
+ if err != nil {
+ t.Fatalf("runCustom error: %v", err)
+ }
+ if len(cap.last) == 0 {
+ t.Fatalf("expected messages captured")
+ }
+ // user message should contain diagnostics and selection
+ found := false
+ for _, m := range cap.last {
+ if m.Role == "user" && strings.Contains(m.Content, "L1") && strings.Contains(m.Content, "code") {
+ found = true
+ }
+ }
+ if !found {
+ t.Fatalf("expected diagnostics and selection in user message: %+v", cap.last)
+ }
}
-
diff --git a/internal/hexaiaction/run.go b/internal/hexaiaction/run.go
index d32edbf..b07fbbb 100644
--- a/internal/hexaiaction/run.go
+++ b/internal/hexaiaction/run.go
@@ -101,7 +101,12 @@ func executeAction(ctx context.Context, kind ActionKind, parts InputParts, cfg a
selectedCustom = nil // clear after use
return out, err
}
- // Fallback: open editor for free-form instruction
+ // No selected custom; treat as no-op
+ return parts.Selection, nil
+ case ActionCustomPrompt:
+ cctx, cancel := timeout10s(ctx)
+ defer cancel()
+ // Open editor for free-form instruction
prompt, err := editor.OpenTempAndEdit(nil)
if err != nil || strings.TrimSpace(prompt) == "" {
fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: custom prompt canceled or empty; echoing input"+logging.AnsiReset)
diff --git a/internal/hexaiaction/tui.go b/internal/hexaiaction/tui.go
index d07bb78..549a6ab 100644
--- a/internal/hexaiaction/tui.go
+++ b/internal/hexaiaction/tui.go
@@ -31,7 +31,7 @@ func newModel() model {
item{title: "Simplify and improve", desc: "", kind: ActionSimplify, hotkey: 'i'},
item{title: "Document code", desc: "", kind: ActionDocument, hotkey: 'c'},
item{title: "Generate Go unit test(s)", desc: "", kind: ActionGoTest, hotkey: 't'},
- item{title: "Custom prompt", desc: "", kind: ActionCustom, hotkey: 'p'},
+ item{title: "Custom prompt", desc: "", kind: ActionCustomPrompt, hotkey: 'p'},
item{title: "Skip", desc: "", kind: ActionSkip, hotkey: 's'},
}
l := list.New(items, oneLineDelegate{}, 0, 0)
diff --git a/internal/hexaiaction/tui_custom.go b/internal/hexaiaction/tui_custom.go
index 91d4b81..fe32588 100644
--- a/internal/hexaiaction/tui_custom.go
+++ b/internal/hexaiaction/tui_custom.go
@@ -34,6 +34,7 @@ func RunTUIWithCustom(customs []appconfig.CustomAction, menuHotkey string) (Acti
return ActionSkip, err
}
if mm, ok := md.(model); ok {
+ // If user chose built-in items (including Custom prompt), return immediately.
if mm.chosen != ActionCustom {
return mm.chosen, nil
}
diff --git a/internal/hexaiaction/types.go b/internal/hexaiaction/types.go
index d3cda4e..8c5652b 100644
--- a/internal/hexaiaction/types.go
+++ b/internal/hexaiaction/types.go
@@ -11,7 +11,10 @@ const (
ActionDocument ActionKind = "document"
ActionGoTest ActionKind = "gotest"
ActionSimplify ActionKind = "simplify"
- ActionCustom ActionKind = "custom"
+ // ActionCustom represents a configured custom action from the submenu.
+ ActionCustom ActionKind = "custom"
+ // ActionCustomPrompt is the free-form prompt opened in the editor (hotkey 'p').
+ ActionCustomPrompt ActionKind = "custom_prompt"
)
// InputParts represents parsed stdin input for actions.