1package config
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8 "sync"
9 "sync/atomic"
10
11 "github.com/charmbracelet/crush/internal/logging"
12)
13
14const (
15 InitFlagFilename = "init"
16)
17
18type ProjectInitFlag struct {
19 Initialized bool `json:"initialized"`
20}
21
22// TODO: we need to remove the global config instance keeping it now just until everything is migrated
23var (
24 instance atomic.Pointer[Config]
25 cwd string
26 once sync.Once // Ensures the initialization happens only once
27)
28
29func Init(workingDir string, debug bool) (*Config, error) {
30 var err error
31 once.Do(func() {
32 cwd = workingDir
33 cfg, err := Load(cwd, debug)
34 if err != nil {
35 logging.Error("Failed to load config", "error", err)
36 }
37 instance.Store(cfg)
38 })
39
40 return instance.Load(), err
41}
42
43func Get() *Config {
44 return instance.Load()
45}
46
47func ProjectNeedsInitialization() (bool, error) {
48 cfg := Get()
49 if cfg == nil {
50 return false, fmt.Errorf("config not loaded")
51 }
52
53 flagFilePath := filepath.Join(cfg.Options.DataDirectory, InitFlagFilename)
54
55 _, err := os.Stat(flagFilePath)
56 if err == nil {
57 return false, nil
58 }
59
60 if !os.IsNotExist(err) {
61 return false, fmt.Errorf("failed to check init flag file: %w", err)
62 }
63
64 crushExists, err := crushMdExists(cfg.WorkingDir())
65 if err != nil {
66 return false, fmt.Errorf("failed to check for CRUSH.md files: %w", err)
67 }
68 if crushExists {
69 return false, nil
70 }
71
72 return true, nil
73}
74
75func crushMdExists(dir string) (bool, error) {
76 entries, err := os.ReadDir(dir)
77 if err != nil {
78 return false, err
79 }
80
81 for _, entry := range entries {
82 if entry.IsDir() {
83 continue
84 }
85
86 name := strings.ToLower(entry.Name())
87 if name == "crush.md" {
88 return true, nil
89 }
90 }
91
92 return false, nil
93}
94
95func MarkProjectInitialized() error {
96 cfg := Get()
97 if cfg == nil {
98 return fmt.Errorf("config not loaded")
99 }
100 flagFilePath := filepath.Join(cfg.Options.DataDirectory, InitFlagFilename)
101
102 file, err := os.Create(flagFilePath)
103 if err != nil {
104 return fmt.Errorf("failed to create init flag file: %w", err)
105 }
106 defer file.Close()
107
108 return nil
109}