diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-17 18:52:51 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-17 18:52:51 +0300 |
| commit | 454451105ad3522d2ac3d22136eedee4a4d034af (patch) | |
| tree | aa4b5723809c4d45bfc9094a38c01c6415582f9c /cmd | |
| parent | 498923e77c201ca90dc35c7934f4f7f1c9c3ccd2 (diff) | |
cli+lsp: refactor main packages into internal runners; add tests
- Move CLI logic to internal/hexaicli with Run/RunWithClient
- Move LSP logic to internal/hexailsp with Run/RunWithFactory
- Extract helpers; keep behavior identical for both binaries
- Add unit tests for hexaicli (input parsing, messages, streaming) and
hexailsp (factory wiring, client creation, logging settings)
- Add top-of-file summaries and 'Not yet reviewed by a human' comments to all Go files
- Update README with internal package docs
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/hexai-lsp/main.go | 92 | ||||
| -rw-r--r-- | cmd/hexai/main.go | 126 |
2 files changed, 36 insertions, 182 deletions
diff --git a/cmd/hexai-lsp/main.go b/cmd/hexai-lsp/main.go index 065b6e2..a473ad7 100644 --- a/cmd/hexai-lsp/main.go +++ b/cmd/hexai-lsp/main.go @@ -1,82 +1,26 @@ +// Summary: Hexai LSP entrypoint; parses flags and delegates to internal/hexailsp. +// Not yet reviewed by a human package main import ( - "flag" - "log" - "os" - "strings" + "flag" + "log" + "os" - "hexai/internal" - "hexai/internal/appconfig" - "hexai/internal/llm" - "hexai/internal/logging" - "hexai/internal/lsp" + "hexai/internal" + "hexai/internal/hexailsp" ) func main() { - logPath := flag.String("log", "/tmp/hexai-lsp.log", "path to log file (optional)") - showVersion := flag.Bool("version", false, "print version and exit") - flag.Parse() - if *showVersion { - log.Println(internal.Version) - return - } - - // Configure logging (path flag only) - logger := log.New(os.Stderr, "hexai-lsp ", log.LstdFlags|log.Lmsgprefix) - if *logPath != "" { - f, err := os.OpenFile(*logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - if err != nil { - logger.Fatalf("failed to open log file: %v", err) - } - defer f.Close() - logger.SetOutput(f) - } - logging.Bind(logger) - - // Load config file - cfg := appconfig.Load(logger) - - // Normalize and apply logging config - cfg.ContextMode = strings.ToLower(strings.TrimSpace(cfg.ContextMode)) - if cfg.LogPreviewLimit >= 0 { - logging.SetLogPreviewLimit(cfg.LogPreviewLimit) - } - - // Build LLM client from config - var client llm.Client - { - llmCfg := llm.Config{ - Provider: cfg.Provider, - OpenAIBaseURL: cfg.OpenAIBaseURL, - OpenAIModel: cfg.OpenAIModel, - OllamaBaseURL: cfg.OllamaBaseURL, - OllamaModel: cfg.OllamaModel, - CopilotBaseURL: cfg.CopilotBaseURL, - CopilotModel: cfg.CopilotModel, - } - oaKey := os.Getenv("OPENAI_API_KEY") - cpKey := os.Getenv("COPILOT_API_KEY") - if c, err := llm.NewFromConfig(llmCfg, oaKey, cpKey); err != nil { - logging.Logf("lsp ", "llm disabled: %v", err) - } else { - client = c - logging.Logf("lsp ", "llm enabled provider=%s model=%s", c.Name(), c.DefaultModel()) - } - } - - server := lsp.NewServer(os.Stdin, os.Stdout, logger, lsp.ServerOptions{ - LogContext: *logPath != "", - MaxTokens: cfg.MaxTokens, - ContextMode: cfg.ContextMode, - WindowLines: cfg.ContextWindowLines, - MaxContextTokens: cfg.MaxContextTokens, - NoDiskIO: cfg.NoDiskIO, - Client: client, - TriggerCharacters: cfg.TriggerCharacters, - }) - if err := server.Run(); err != nil { - logger.Fatalf("server error: %v", err) - } + logPath := flag.String("log", "/tmp/hexai-lsp.log", "path to log file (optional)") + showVersion := flag.Bool("version", false, "print version and exit") + flag.Parse() + if *showVersion { + log.Println(internal.Version) + return + } + + if err := hexailsp.Run(*logPath, os.Stdin, os.Stdout, os.Stderr); err != nil { + log.Fatalf("server error: %v", err) + } } - diff --git a/cmd/hexai/main.go b/cmd/hexai/main.go index 6cbd288..2a0e81b 100644 --- a/cmd/hexai/main.go +++ b/cmd/hexai/main.go @@ -1,116 +1,26 @@ +// Summary: Hexai CLI entrypoint; parses flags and delegates to internal/hexaicli. +// Not yet reviewed by a human package main import ( - "bufio" - "context" - "flag" - "fmt" - "io" - "os" - "strings" - "time" + "context" + "flag" + "fmt" + "os" - "hexai/internal" - "hexai/internal/appconfig" - "hexai/internal/llm" - "hexai/internal/logging" + "hexai/internal" + "hexai/internal/hexaicli" ) func main() { - showVersion := flag.Bool("version", false, "print version and exit") - flag.Parse() - if *showVersion { - fmt.Fprintln(os.Stdout, internal.Version) - return - } - - // Read stdin if present - var stdinData string - if fi, err := os.Stdin.Stat(); err == nil && (fi.Mode()&os.ModeCharDevice) == 0 { - b, _ := io.ReadAll(bufio.NewReader(os.Stdin)) - stdinData = string(b) - } - - // Read argument input (join all remaining args with space) - argData := strings.TrimSpace(strings.Join(flag.Args(), " ")) - - // Combine inputs - var input string - switch { - case stdinData != "" && argData != "": - input = strings.TrimSpace(stdinData) + "\n\n" + argData - case stdinData != "": - input = strings.TrimSpace(stdinData) - case argData != "": - input = argData - default: - fmt.Fprintln(os.Stderr, logging.AnsiBase+"hexai: no input provided; pass text as an argument or via stdin"+logging.AnsiReset) - os.Exit(2) - } - - // Load config (no external logging for CLI) - cfg := appconfig.Load(nil) - - // Build LLM client - llmCfg := llm.Config{ - Provider: cfg.Provider, - OpenAIBaseURL: cfg.OpenAIBaseURL, - OpenAIModel: cfg.OpenAIModel, - OllamaBaseURL: cfg.OllamaBaseURL, - OllamaModel: cfg.OllamaModel, - CopilotBaseURL: cfg.CopilotBaseURL, - CopilotModel: cfg.CopilotModel, - } - oaKey := os.Getenv("OPENAI_API_KEY") - cpKey := os.Getenv("COPILOT_API_KEY") - client, err := llm.NewFromConfig(llmCfg, oaKey, cpKey) - if err != nil { - fmt.Fprintf(os.Stderr, logging.AnsiBase+"hexai: LLM disabled: %v"+logging.AnsiReset+"\n", err) - os.Exit(1) - } - - // Print provider/model immediately to stderr - fmt.Fprintf(os.Stderr, logging.AnsiBase+"provider=%s model=%s"+logging.AnsiReset+"\n", client.Name(), client.DefaultModel()) - - // Prepare and send request - start := time.Now() - lower := strings.ToLower(input) - system := "You are Hexai CLI. Default to very short, concise answers. If the user asks for commands, output only the commands (one per line) with no commentary or explanation. Only when the word 'explain' appears in the prompt, produce a verbose explanation." - if strings.Contains(lower, "explain") { - system = "You are Hexai CLI. The user requested an explanation. Provide a clear, verbose explanation with reasoning and details. If commands are needed, include them with brief context." - } - msgs := []llm.Message{ - {Role: "system", Content: system}, - {Role: "user", Content: input}, - } - var out string - if s, ok := client.(llm.Streamer); ok { - var b strings.Builder - err := s.ChatStream(context.Background(), msgs, func(chunk string) { - b.WriteString(chunk) - fmt.Fprint(os.Stdout, chunk) - }) - dur := time.Since(start) - if err != nil { - fmt.Fprintf(os.Stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err) - os.Exit(1) - } - out = b.String() - // Summary - inSize := len(input) - outSize := len(out) - fmt.Fprintf(os.Stderr, "\n"+logging.AnsiBase+"done provider=%s model=%s time=%s in_bytes=%d out_bytes=%d"+logging.AnsiReset+"\n", client.Name(), client.DefaultModel(), dur.Round(time.Millisecond), inSize, outSize) - } else { - outText, err := client.Chat(context.Background(), msgs) - dur := time.Since(start) - if err != nil { - fmt.Fprintf(os.Stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err) - os.Exit(1) - } - out = outText - fmt.Fprint(os.Stdout, out) - inSize := len(input) - outSize := len(out) - fmt.Fprintf(os.Stderr, "\n"+logging.AnsiBase+"done provider=%s model=%s time=%s in_bytes=%d out_bytes=%d"+logging.AnsiReset+"\n", client.Name(), client.DefaultModel(), dur.Round(time.Millisecond), inSize, outSize) - } + showVersion := flag.Bool("version", false, "print version and exit") + flag.Parse() + if *showVersion { + fmt.Fprintln(os.Stdout, internal.Version) + return + } + + if err := hexaicli.Run(context.Background(), flag.Args(), os.Stdin, os.Stdout, os.Stderr); err != nil { + os.Exit(1) + } } |
