1package cmd
2
3import (
4 "context"
5 "os"
6 "sync"
7
8 tea "github.com/charmbracelet/bubbletea"
9 "github.com/kujtimiihoxha/termai/internal/app"
10 "github.com/kujtimiihoxha/termai/internal/config"
11 "github.com/kujtimiihoxha/termai/internal/db"
12 "github.com/kujtimiihoxha/termai/internal/llm/agent"
13 "github.com/kujtimiihoxha/termai/internal/tui"
14 zone "github.com/lrstanley/bubblezone"
15 "github.com/spf13/cobra"
16)
17
18var rootCmd = &cobra.Command{
19 Use: "termai",
20 Short: "A terminal ai assistant",
21 Long: `A terminal ai assistant`,
22 RunE: func(cmd *cobra.Command, args []string) error {
23 if cmd.Flag("help").Changed {
24 cmd.Help()
25 return nil
26 }
27 debug, _ := cmd.Flags().GetBool("debug")
28 err := config.Load(debug)
29 if err != nil {
30 return err
31 }
32 conn, err := db.Connect()
33 if err != nil {
34 return err
35 }
36 ctx := context.Background()
37
38 app := app.New(ctx, conn)
39 app.Logger.Info("Starting termai...")
40 zone.NewGlobal()
41 tui := tea.NewProgram(
42 tui.New(app),
43 tea.WithAltScreen(),
44 tea.WithMouseCellMotion(),
45 )
46 app.Logger.Info("Setting up subscriptions...")
47 ch, unsub := setupSubscriptions(app)
48 defer unsub()
49
50 go func() {
51 // Set this up once
52 agent.GetMcpTools(ctx)
53 for msg := range ch {
54 tui.Send(msg)
55 }
56 }()
57 if _, err := tui.Run(); err != nil {
58 return err
59 }
60 return nil
61 },
62}
63
64func setupSubscriptions(app *app.App) (chan tea.Msg, func()) {
65 ch := make(chan tea.Msg)
66 wg := sync.WaitGroup{}
67 ctx, cancel := context.WithCancel(app.Context)
68
69 {
70 sub := app.Logger.Subscribe(ctx)
71 wg.Add(1)
72 go func() {
73 for ev := range sub {
74 ch <- ev
75 }
76 wg.Done()
77 }()
78 }
79 {
80 sub := app.Sessions.Subscribe(ctx)
81 wg.Add(1)
82 go func() {
83 for ev := range sub {
84 ch <- ev
85 }
86 wg.Done()
87 }()
88 }
89 {
90 sub := app.Messages.Subscribe(ctx)
91 wg.Add(1)
92 go func() {
93 for ev := range sub {
94 ch <- ev
95 }
96 wg.Done()
97 }()
98 }
99 {
100 sub := app.Permissions.Subscribe(ctx)
101 wg.Add(1)
102 go func() {
103 for ev := range sub {
104 ch <- ev
105 }
106 wg.Done()
107 }()
108 }
109 return ch, func() {
110 cancel()
111 wg.Wait()
112 close(ch)
113 }
114}
115
116func Execute() {
117 err := rootCmd.Execute()
118 if err != nil {
119 os.Exit(1)
120 }
121}
122
123func init() {
124 rootCmd.Flags().BoolP("help", "h", false, "Help")
125 rootCmd.Flags().BoolP("debug", "d", false, "Help")
126}