Files
ai-code-assistant/cmd/main.go

126 lines
2.9 KiB
Go

package main
import (
"ai-code-assistant/cmd/autopatch"
"ai-code-assistant/cmd/chunks"
"ai-code-assistant/cmd/indexer"
"ai-code-assistant/pkg/config"
"ai-code-assistant/pkg/database"
"ai-code-assistant/pkg/llm"
"context"
"fmt"
_ "github.com/lib/pq"
"github.com/urfave/cli/v3"
"gopkg.in/yaml.v3"
"log/slog"
"os"
)
// main is the entry point to the application. It's mostly responsible for bootstrapping the database, configuration,
// logging, and llms. It passes everything to subcommands by injecting state into the context to avoid coupling during
// initialization.
func main() {
app := &cli.Command{
Name: "ai-coding-assistant",
Usage: "an AI-powered autonomous code assistant that can help software engineers by performing tasks on their behalf",
Commands: []*cli.Command{
indexer.Command(),
chunks.Command(),
autopatch.Command(),
},
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
initPhases := []cli.BeforeFunc{
readConfig,
initLogging,
initDatabase,
initLLM,
}
for _, phase := range initPhases {
var err error
ctx, err = phase(ctx, cmd)
if err != nil {
return nil, err
}
}
return ctx, nil
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Usage: "path to config file",
Value: "config.yaml",
},
},
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := app.Run(ctx, os.Args); err != nil {
slog.Error("problem running command", "error", err)
}
}
// readConfig attempts to read the configuration file.
func readConfig(ctx context.Context, cmd *cli.Command) (context.Context, error) {
cfgFile := cmd.String("config")
cfgHandle, err := os.Open(cfgFile)
if err != nil {
return nil, fmt.Errorf("problem opening config: %s: %w", cfgFile, err)
}
cfg := &config.Configuration{}
if err := yaml.NewDecoder(cfgHandle).Decode(cfg); err != nil {
return nil, fmt.Errorf("problem parsing config: %w", err)
}
if cfg.IndexChunkSize == 0 {
cfg.IndexChunkSize = 512 * 4
}
if cfg.RelevantDocs == 0 {
cfg.RelevantDocs = 5
}
return config.WrapContext(ctx, cfg), nil
}
func initLogging(ctx context.Context, _ *cli.Command) (context.Context, error) {
cfg := config.FromContext(ctx)
var lvl slog.Level
if err := lvl.UnmarshalText([]byte(cfg.Logging.Level)); err != nil {
return nil, err
}
handler := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: lvl,
}))
slog.SetDefault(handler)
return ctx, nil
}
func initDatabase(ctx context.Context, _ *cli.Command) (context.Context, error) {
cfg := config.FromContext(ctx)
db, err := database.FromConfig(ctx, cfg)
if err != nil {
return nil, err
}
return database.WrapContext(ctx, db), nil
}
func initLLM(ctx context.Context, _ *cli.Command) (context.Context, error) {
cfg := config.FromContext(ctx)
llmRef, err := llm.FromConfig(cfg)
if err != nil {
return nil, err
}
return llm.WrapContext(ctx, llmRef), nil
}