summaryrefslogtreecommitdiff
path: root/internal/hexaiaction/run.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-08 09:50:38 +0300
committerPaul Buetow <paul@buetow.org>2025-09-08 09:50:38 +0300
commitcead3ebde8f3aee0ef8677158d37f4d04c6629dc (patch)
treeeadf4928c13e4f1fd782e8e0955116a24cef1d27 /internal/hexaiaction/run.go
parent29b0da31acf02816ee9e8f1d5a1b9a0ad5993593 (diff)
tmux: colored LLM status with provider + stats; add start heartbeat for LSP/CLI/TUI; theme support via HEXAI_TMUX_STATUS_THEME and HEXAI_TMUX_STATUS_FG/BG; docs: update tmux options and add Helix+tmux quickstart
Diffstat (limited to 'internal/hexaiaction/run.go')
-rw-r--r--internal/hexaiaction/run.go131
1 files changed, 66 insertions, 65 deletions
diff --git a/internal/hexaiaction/run.go b/internal/hexaiaction/run.go
index c8cfcac..4958642 100644
--- a/internal/hexaiaction/run.go
+++ b/internal/hexaiaction/run.go
@@ -1,63 +1,64 @@
package hexaiaction
import (
- "context"
- "fmt"
- "io"
- "log"
- "strings"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "strings"
- "codeberg.org/snonux/hexai/internal/appconfig"
- "codeberg.org/snonux/hexai/internal/editor"
- "codeberg.org/snonux/hexai/internal/logging"
- "codeberg.org/snonux/hexai/internal/llmutils"
- "codeberg.org/snonux/hexai/internal/tmux"
+ "codeberg.org/snonux/hexai/internal/appconfig"
+ "codeberg.org/snonux/hexai/internal/editor"
+ "codeberg.org/snonux/hexai/internal/llmutils"
+ "codeberg.org/snonux/hexai/internal/logging"
+ "codeberg.org/snonux/hexai/internal/tmux"
)
// Run executes the hexai-tmux-action command flow.
// seams for testability
-var chooseActionFn = RunTUI
-var newClientFromApp = llmutils.NewClientFromApp
+var (
+ chooseActionFn = RunTUI
+ newClientFromApp = llmutils.NewClientFromApp
+)
func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error {
- logger := log.New(stderr, "hexai-tmux-action ", log.LstdFlags|log.Lmsgprefix)
- cfg := appconfig.Load(logger)
- cli, err := newClientFromApp(cfg)
- if err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: LLM disabled: %v"+logging.AnsiReset+"\n", err)
- return err
- }
- _ = tmux.SetStatus("hexai action ready " + cli.DefaultModel())
- var client chatDoer = cli
- parts, err := ParseInput(stdin)
+ logger := log.New(stderr, "hexai-tmux-action ", log.LstdFlags|log.Lmsgprefix)
+ cfg := appconfig.Load(logger)
+ cli, err := newClientFromApp(cfg)
if err != nil {
- fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: failed to read input"+logging.AnsiReset)
+ fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: LLM disabled: %v"+logging.AnsiReset+"\n", err)
+ return err
+ }
+ _ = tmux.SetStatus(tmux.FormatLLMStartStatus(cli.Name(), cli.DefaultModel()))
+ var client chatDoer = cli
+ parts, err := ParseInput(stdin)
+ if err != nil {
+ fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: failed to read input"+logging.AnsiReset)
return err
}
if strings.TrimSpace(parts.Selection) == "" {
- return fmt.Errorf("hexai-tmux-action: no input provided on stdin")
+ return fmt.Errorf("hexai-tmux-action: no input provided on stdin")
+ }
+ kind, err := chooseActionFn()
+ if err != nil {
+ return err
+ }
+ out, err := executeAction(ctx, kind, parts, cfg, client, stderr)
+ if err != nil {
+ return err
}
- kind, err := chooseActionFn()
- if err != nil {
- return err
- }
- out, err := executeAction(ctx, kind, parts, cfg, client, stderr)
- if err != nil {
- return err
- }
- io.WriteString(stdout, out)
- _ = tmux.SetStatus("✅ " + cli.DefaultModel())
- return nil
+ io.WriteString(stdout, out)
+ return nil
}
func executeAction(ctx context.Context, kind ActionKind, parts InputParts, cfg appconfig.App, client chatDoer, stderr io.Writer) (string, error) {
- switch kind {
- case ActionSkip:
- return parts.Selection, nil
- case ActionRewrite:
+ switch kind {
+ case ActionSkip:
+ return parts.Selection, nil
+ case ActionRewrite:
instr, cleaned := ExtractInstruction(parts.Selection)
if strings.TrimSpace(instr) == "" {
- fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: no inline instruction found; echoing input"+logging.AnsiReset)
+ fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: no inline instruction found; echoing input"+logging.AnsiReset)
return parts.Selection, nil
}
cctx, cancel := timeout10s(ctx)
@@ -67,31 +68,31 @@ func executeAction(ctx context.Context, kind ActionKind, parts InputParts, cfg a
cctx, cancel := timeout10s(ctx)
defer cancel()
return runDiagnostics(cctx, cfg, client, parts.Diagnostics, parts.Selection)
- case ActionDocument:
- cctx, cancel := timeout10s(ctx)
- defer cancel()
- return runDocument(cctx, cfg, client, parts.Selection)
- case ActionGoTest:
- cctx, cancel := timeout8s(ctx)
- defer cancel()
- return runGoTest(cctx, cfg, client, parts.Selection)
- case ActionSimplify:
- cctx, cancel := timeout10s(ctx)
- defer cancel()
- return runSimplify(cctx, cfg, client, parts.Selection)
- case ActionCustom:
- 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)
- return parts.Selection, nil
- }
- return runRewrite(cctx, cfg, client, prompt, parts.Selection)
- default:
- return parts.Selection, nil
- }
+ case ActionDocument:
+ cctx, cancel := timeout10s(ctx)
+ defer cancel()
+ return runDocument(cctx, cfg, client, parts.Selection)
+ case ActionGoTest:
+ cctx, cancel := timeout8s(ctx)
+ defer cancel()
+ return runGoTest(cctx, cfg, client, parts.Selection)
+ case ActionSimplify:
+ cctx, cancel := timeout10s(ctx)
+ defer cancel()
+ return runSimplify(cctx, cfg, client, parts.Selection)
+ case ActionCustom:
+ 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)
+ return parts.Selection, nil
+ }
+ return runRewrite(cctx, cfg, client, prompt, parts.Selection)
+ default:
+ return parts.Selection, nil
+ }
}
// client construction is shared via internal/llmutils