clone

a lazygit-style terminal viewer for openspec repos: watch changes, tasks and spec deltas land as agents work

main
lazyopenspec / main.go
main.go 2.4K
1// Command lazyopenspec is a live terminal viewer for OpenSpec specs and2// changes. Leave it open next to your agents and watch the specs move.3package main45import (6 "flag"7 "fmt"8 "os"9 "runtime/debug"10 "time"1112 tea "charm.land/bubbletea/v2"1314 "src.rest/cat/lazyopenspec/internal/spec"15 "src.rest/cat/lazyopenspec/internal/ui"16)1718// version is set by release builds (-ldflags "-X main.version=v1.2.3").19var version = ""2021// versionString reports the release version, or the module version that22// `go install ...@vX` records, or "dev".23func versionString() string {24 if version != "" {25 return version26 }27 if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "" && info.Main.Version != "(devel)" {28 return info.Main.Version29 }30 return "dev"31}3233func main() {34 interval := flag.Duration("interval", 400*time.Millisecond, "how often to poll openspec/ for edits")35 follow := flag.Bool("follow", false, "start in follow mode (jump to whatever changes)")36 portrait := flag.String("portrait", "auto", "stack panels above the main pane like lazygit's portraitMode: auto (when <=84 cols and >=46 rows), always, never")37 showVersion := flag.Bool("version", false, "print version and exit")38 flag.Usage = func() {39 fmt.Fprintf(os.Stderr, "usage: lazyopenspec [flags] [path]\n\n")40 fmt.Fprintf(os.Stderr, "Watches the openspec/ directory of the repo containing path (default: .).\n\n")41 flag.PrintDefaults()42 }43 // Accept flags before or after the path: `lazyopenspec ~/repo --follow`.44 var positional []string45 for args := os.Args[1:]; ; {46 _ = flag.CommandLine.Parse(args) // ExitOnError handles failures47 args = flag.Args()48 if len(args) == 0 {49 break50 }51 positional, args = append(positional, args[0]), args[1:]52 }53 if *showVersion {54 fmt.Println("lazyopenspec", versionString())55 return56 }5758 portraitMode, ok := ui.ParsePortraitMode(*portrait)59 if !ok {60 fmt.Fprintf(os.Stderr, "lazyopenspec: --portrait must be auto, always, or never (got %q)\n", *portrait)61 os.Exit(2)62 }6364 start := "."65 if len(positional) > 0 {66 start = positional[0]67 }68 root, err := spec.FindRoot(start)69 if err != nil {70 fmt.Fprintln(os.Stderr, "lazyopenspec:", err)71 os.Exit(1)72 }73 display := spec.DisplayRoot(start, root)7475 p := tea.NewProgram(ui.New(ui.Options{Root: root, Display: display, Interval: *interval, Follow: *follow, Portrait: portraitMode}))76 if _, err := p.Run(); err != nil {77 fmt.Fprintln(os.Stderr, "lazyopenspec:", err)78 os.Exit(1)79 }80}