diff options
Diffstat (limited to 'internal/hexaiaction')
| -rw-r--r-- | internal/hexaiaction/custom_action_test.go | 2 | ||||
| -rw-r--r-- | internal/hexaiaction/custom_exec_more_test.go | 71 | ||||
| -rw-r--r-- | internal/hexaiaction/run.go | 7 | ||||
| -rw-r--r-- | internal/hexaiaction/tui.go | 2 | ||||
| -rw-r--r-- | internal/hexaiaction/tui_custom.go | 1 | ||||
| -rw-r--r-- | internal/hexaiaction/types.go | 5 |
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. |
