1package app
2
3import (
4 "context"
5 "database/sql"
6 "maps"
7 "sync"
8 "time"
9
10 "github.com/kujtimiihoxha/termai/internal/config"
11 "github.com/kujtimiihoxha/termai/internal/db"
12 "github.com/kujtimiihoxha/termai/internal/history"
13 "github.com/kujtimiihoxha/termai/internal/llm/agent"
14 "github.com/kujtimiihoxha/termai/internal/logging"
15 "github.com/kujtimiihoxha/termai/internal/lsp"
16 "github.com/kujtimiihoxha/termai/internal/message"
17 "github.com/kujtimiihoxha/termai/internal/permission"
18 "github.com/kujtimiihoxha/termai/internal/session"
19)
20
21type App struct {
22 Sessions session.Service
23 Messages message.Service
24 History history.Service
25 Permissions permission.Service
26
27 CoderAgent agent.Service
28
29 LSPClients map[string]*lsp.Client
30
31 clientsMutex sync.RWMutex
32
33 watcherCancelFuncs []context.CancelFunc
34 cancelFuncsMutex sync.Mutex
35 watcherWG sync.WaitGroup
36}
37
38func New(ctx context.Context, conn *sql.DB) (*App, error) {
39 q := db.New(conn)
40 sessions := session.NewService(q)
41 messages := message.NewService(q)
42 files := history.NewService(q)
43
44 app := &App{
45 Sessions: sessions,
46 Messages: messages,
47 History: files,
48 Permissions: permission.NewPermissionService(),
49 LSPClients: make(map[string]*lsp.Client),
50 }
51
52 app.initLSPClients(ctx)
53
54 var err error
55 app.CoderAgent, err = agent.NewAgent(
56 config.AgentCoder,
57 app.Sessions,
58 app.Messages,
59 agent.CoderAgentTools(
60 app.Permissions,
61 app.Sessions,
62 app.Messages,
63 app.History,
64 app.LSPClients,
65 ),
66 )
67 if err != nil {
68 logging.Error("Failed to create coder agent", err)
69 return nil, err
70 }
71
72 return app, nil
73}
74
75// Shutdown performs a clean shutdown of the application
76func (app *App) Shutdown() {
77 // Cancel all watcher goroutines
78 app.cancelFuncsMutex.Lock()
79 for _, cancel := range app.watcherCancelFuncs {
80 cancel()
81 }
82 app.cancelFuncsMutex.Unlock()
83 app.watcherWG.Wait()
84
85 // Perform additional cleanup for LSP clients
86 app.clientsMutex.RLock()
87 clients := make(map[string]*lsp.Client, len(app.LSPClients))
88 maps.Copy(clients, app.LSPClients)
89 app.clientsMutex.RUnlock()
90
91 for name, client := range clients {
92 shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
93 if err := client.Shutdown(shutdownCtx); err != nil {
94 logging.Error("Failed to shutdown LSP client", "name", name, "error", err)
95 }
96 cancel()
97 }
98}