root.go

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