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 }