// Package tui is eeco's control center: a hybrid command box with a // live status digest, rendered inline in the terminal scrollback (no // alt-screen takeover). It accepts slash commands (with history and Tab // completion) and free-text requests; free text is routed through the // shared, gated AI provider so it is consented, budget-capped, and // parked-and-queued rather than ever a silent spend or a hard failure. // // The control center only orchestrates engine operations that already // exist (config, memory, garbage collection, queue, workflow run and // scaffold). It introduces no new write path, obeys write-scope, and // honours the same exit-code contract as every other entry point. // // When stdout or stdin is not a terminal (piped or CI) there is no // interactive loop: Run prints the one-screen status digest and exits 0. package tui import ( "fmt" "io" "os" "github.com/ajhahnde/eeco/internal/config" tea "github.com/charmbracelet/bubbletea" ) // interactive reports whether an interactive control center should be // started. Both stdin and stdout must be character devices; a pipe, a // file, or a CI sink is not, so tests and pipelines deterministically // take the digest path. The check is stdlib-only (no extra dependency). func interactive() bool { return isCharDevice(os.Stdin) && isCharDevice(os.Stdout) } func isCharDevice(f *os.File) bool { if f == nil { return false } info, err := f.Stat() if err != nil { return false } return info.Mode()&os.ModeCharDevice != 0 } // Run is the control-center entry point invoked by `eeco` with no // arguments. Non-interactive: write the one-screen digest to stdout and // return 0. Interactive: run the inline control center on the real // terminal; return 0 on a clean quit, 1 on a terminal I/O failure. func Run(cfg *config.Config, version string, stdout, stderr io.Writer) int { if !interactive() { fmt.Fprint(stdout, OneScreen(cfg, version)) return 0 } p := tea.NewProgram(newModel(cfg, version)) if _, err := p.Run(); err != nil { fmt.Fprintln(stderr, "eeco:", err) return 1 } return 0 }