summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PROJECTSTATUS.md5
-rw-r--r--README.md6
-rw-r--r--cmd/internal/hexai-action/main.go42
-rw-r--r--cmd/internal/hexai-action/main_test.go44
-rw-r--r--docs/testing.md14
-rw-r--r--docs/usage.md22
6 files changed, 129 insertions, 4 deletions
diff --git a/PROJECTSTATUS.md b/PROJECTSTATUS.md
index 1f61d5e..5959f31 100644
--- a/PROJECTSTATUS.md
+++ b/PROJECTSTATUS.md
@@ -4,7 +4,7 @@ This documents shows future items and in progress items. Already completed ones
## Features
-### AI menu
+### [/] AI menu
```
[keys.normal]
@@ -23,8 +23,7 @@ And then generate a menu with all the code actions hexai-lsp knows of and includ
### More features
-* [ ] Have all text LLM prompts be configurable. With defaults as of now.
-* [ ] implement a code action for selected code block the way via a unix pipe as faster access in helix
+* [/] implement a code action for selected code block the way via a unix pipe as faster access in helix
- pipe selected code to external command and replace selection with output
- the external command should open a menu to select an action (e.g. "format", "refactor", "explain", "test", etc.) and then apply it to the selected code
- the external menu can be opened in a separate tmux pane
diff --git a/README.md b/README.md
index 1486f4a..7a4e214 100644
--- a/README.md
+++ b/README.md
@@ -96,3 +96,9 @@ Run:
- `cat input.go | ./hexai-action`
- or `./hexai-action < input.go`
+- or with files: `./hexai-action --infile input.go --outfile output.go`
+
+Flags
+
+- `--infile` Read input from the given file instead of stdin.
+- `--outfile` Write output to the given file instead of stdout (truncates/creates).
diff --git a/cmd/internal/hexai-action/main.go b/cmd/internal/hexai-action/main.go
index 50e6774..8bcc3cd 100644
--- a/cmd/internal/hexai-action/main.go
+++ b/cmd/internal/hexai-action/main.go
@@ -2,15 +2,55 @@ package main
import (
"context"
+ "flag"
"fmt"
+ "io"
"os"
"codeberg.org/snonux/hexai/internal/hexaiaction"
)
func main() {
- if err := hexaiaction.Run(context.Background(), os.Stdin, os.Stdout, os.Stderr); err != nil {
+ infile := flag.String("infile", "", "Read input from this file instead of stdin")
+ outfile := flag.String("outfile", "", "Write output to this file instead of stdout")
+ flag.Parse()
+
+ in, out, closeIn, closeOut, err := openIO(*infile, *outfile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer closeIn()
+ defer closeOut()
+
+ if err := hexaiaction.Run(context.Background(), in, out, os.Stderr); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
+
+// openIO returns readers/writers for infile/outfile flags with deferred closers.
+func openIO(infile, outfile string) (io.Reader, io.Writer, func(), func(), error) {
+ in := io.Reader(os.Stdin)
+ out := io.Writer(os.Stdout)
+ closeIn := func() {}
+ closeOut := func() {}
+
+ if path := infile; path != "" {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, nil, func() {}, func() {}, fmt.Errorf("hexai-action: cannot open infile: %w", err)
+ }
+ in = f
+ closeIn = func() { _ = f.Close() }
+ }
+ if path := outfile; path != "" {
+ f, err := os.Create(path)
+ if err != nil {
+ return nil, nil, func() {}, func() {}, fmt.Errorf("hexai-action: cannot open outfile: %w", err)
+ }
+ out = f
+ closeOut = func() { _ = f.Close() }
+ }
+ return in, out, closeIn, closeOut, nil
+}
diff --git a/cmd/internal/hexai-action/main_test.go b/cmd/internal/hexai-action/main_test.go
new file mode 100644
index 0000000..9603826
--- /dev/null
+++ b/cmd/internal/hexai-action/main_test.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "io"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+// TestOpenIO_InOutFiles verifies that openIO opens the specified files
+// and that writing via the returned writer persists to disk.
+func TestOpenIO_InOutFiles(t *testing.T) {
+ dir := t.TempDir()
+ inPath := filepath.Join(dir, "in.txt")
+ outPath := filepath.Join(dir, "out.txt")
+
+ // Prepare input file
+ want := "hello world"
+ if err := os.WriteFile(inPath, []byte(want), 0o600); err != nil {
+ t.Fatalf("write infile: %v", err)
+ }
+
+ in, out, cin, cout, err := openIO(inPath, outPath)
+ if err != nil {
+ t.Fatalf("openIO: %v", err)
+ }
+ defer cin()
+ defer cout()
+
+ // Copy through to simulate main's behavior
+ if _, err := io.Copy(out.(io.Writer), in); err != nil {
+ t.Fatalf("copy: %v", err)
+ }
+
+ // Verify outfile content
+ got, err := os.ReadFile(outPath)
+ if err != nil {
+ t.Fatalf("read outfile: %v", err)
+ }
+ if string(got) != want {
+ t.Fatalf("mismatch: got %q want %q", string(got), want)
+ }
+}
+
diff --git a/docs/testing.md b/docs/testing.md
index eff6f2e..17dd4b3 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -14,3 +14,17 @@ Suggested additions:
- Expand table‑driven coverage for completion edit computations and label/filter selection.
- Add more negative tests (malformed SSE/JSON payloads) to assert robust error handling.
+## Running Tests
+
+- Full suite with coverage:
+ - `HEXAI_TEST_SKIP_NET=1 go test ./... -cover`
+ - The `HEXAI_TEST_SKIP_NET=1` env var disables any tests that require network access, keeping runs deterministic in CI/sandboxes.
+
+- Package-specific runs:
+ - `HEXAI_TEST_SKIP_NET=1 go test ./cmd/internal/hexai-action -cover`
+ - `HEXAI_TEST_SKIP_NET=1 go test ./internal/hexaiaction -cover`
+
+Notes
+
+- Some environments restrict writes to the Go build cache; if you see cache permission errors, re-run in a less-restricted shell or allow the command to write to the cache.
+- Always format Go code before committing: `gofumpt -w .`
diff --git a/docs/usage.md b/docs/usage.md
index c74ee7d..a5c5dca 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -102,3 +102,25 @@ hexai 'install ripgrep on macOS'
# Verbose explanation
hexai 'install ripgrep on macOS and explain'
```
+
+## Hexai Action (TUI)
+
+`hexai-action` runs code actions over a selection or diagnostics+selection piped from stdin, or read from a file.
+
+- Choose an action with arrow keys, `j/k`, `g/G`, Enter, or hotkeys `[s] [r] [c] [t]`.
+- Output is written to stdout by default, or to a file via `--outfile`.
+
+Input formats are the same as described in the README (inline instruction markers for rewrite; optional `Diagnostics:` header block).
+
+Examples
+
+```sh
+# From stdin
+cat input.go | hexai-action
+
+# From file to file
+hexai-action --infile input.go --outfile output.go
+
+# Using shell redirection
+hexai-action < input.go > output.go
+```