1/*
2Copyright © 2025 NAME HERE <EMAIL ADDRESS>
3*/
4package cmd
5
6import (
7 "context"
8 "os"
9 "sync"
10
11 tea "github.com/charmbracelet/bubbletea"
12 "github.com/kujtimiihoxha/termai/internal/app"
13 "github.com/kujtimiihoxha/termai/internal/db"
14 "github.com/kujtimiihoxha/termai/internal/tui"
15 "github.com/spf13/cobra"
16 "github.com/spf13/viper"
17)
18
19var rootCmd = &cobra.Command{
20 Use: "termai",
21 Short: "A terminal ai assistant",
22 Long: `A terminal ai assistant`,
23 RunE: func(cmd *cobra.Command, args []string) error {
24 if cmd.Flag("help").Changed {
25 cmd.Help()
26 return nil
27 }
28 debug, _ := cmd.Flags().GetBool("debug")
29 viper.Set("debug", debug)
30 if debug {
31 viper.Set("log.level", "debug")
32 }
33
34 conn, err := db.Connect()
35 if err != nil {
36 return err
37 }
38 ctx := context.Background()
39
40 app := app.New(ctx, conn)
41 app.Logger.Info("Starting termai...")
42 tui := tea.NewProgram(
43 tui.New(app),
44 tea.WithAltScreen(),
45 )
46 app.Logger.Info("Setting up subscriptions...")
47 ch, unsub := setupSubscriptions(app)
48 defer unsub()
49
50 go func() {
51 for msg := range ch {
52 tui.Send(msg)
53 }
54 }()
55 if _, err := tui.Run(); err != nil {
56 return err
57 }
58 return nil
59 },
60}
61
62func setupSubscriptions(app *app.App) (chan tea.Msg, func()) {
63 ch := make(chan tea.Msg)
64 wg := sync.WaitGroup{}
65 ctx, cancel := context.WithCancel(app.Context)
66
67 if viper.GetBool("debug") {
68 sub := app.Logger.Subscribe(ctx)
69 wg.Add(1)
70 go func() {
71 for ev := range sub {
72 ch <- ev
73 }
74 wg.Done()
75 }()
76 }
77 {
78 sub := app.Sessions.Subscribe(ctx)
79 wg.Add(1)
80 go func() {
81 for ev := range sub {
82 ch <- ev
83 }
84 wg.Done()
85 }()
86 }
87 return ch, func() {
88 cancel()
89 wg.Wait()
90 close(ch)
91 }
92}
93
94// Execute adds all child commands to the root command and sets flags appropriately.
95// This is called by main.main(). It only needs to happen once to the rootCmd.
96func Execute() {
97 err := rootCmd.Execute()
98 if err != nil {
99 os.Exit(1)
100 }
101}
102
103func loadConfig() {
104 viper.SetConfigName(".termai")
105 viper.SetConfigType("yaml")
106 viper.AddConfigPath("$HOME")
107 viper.AddConfigPath("$XDG_CONFIG_HOME/termai")
108 viper.AddConfigPath(".")
109 viper.SetEnvPrefix("TERMAI")
110 // SET DEFAULTS
111 viper.SetDefault("log.level", "info")
112 viper.SetDefault("data.dir", ".termai")
113
114 //
115 viper.ReadInConfig()
116}
117
118func init() {
119 loadConfig()
120 // Here you will define your flags and configuration settings.
121 // Cobra supports persistent flags, which, if defined here,
122 // will be global for your application.
123
124 rootCmd.Flags().BoolP("help", "h", false, "Help")
125 rootCmd.Flags().BoolP("debug", "d", false, "Help")
126}