Skip to content

Commit 578b05f

Browse files
committed
fixing setup
1 parent dc7424d commit 578b05f

13 files changed

Lines changed: 460 additions & 112 deletions

File tree

internal/config/config.go

Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ const (
1111
File = Dir + "/config.json"
1212
)
1313

14+
const (
15+
stateDirName = ".git-genius"
16+
stateFileName = "state.json"
17+
)
18+
19+
type appState struct {
20+
ActiveWorkDir string `json:"active_work_dir"`
21+
RecentWorkDirs []string `json:"recent_work_dirs"`
22+
}
23+
1424
// Config holds Git Genius configuration
1525
type Config struct {
1626
/* ---------------- Git basics ---------------- */
@@ -40,18 +50,27 @@ type Config struct {
4050

4151
// Load reads config from disk and applies safe defaults
4252
func Load() Config {
43-
data, err := os.ReadFile(File)
53+
dir := preferredWorkDir()
54+
data, err := os.ReadFile(configPath(dir))
4455
if err != nil {
45-
return defaultConfig()
56+
c := defaultConfig()
57+
c.WorkDir = dir
58+
return c
4659
}
4760

4861
var c Config
4962
if err := json.Unmarshal(data, &c); err != nil {
50-
return defaultConfig()
63+
c = defaultConfig()
64+
c.WorkDir = dir
65+
return c
5166
}
5267

5368
applyDefaults(&c)
5469
normalizePaths(&c)
70+
if c.WorkDir == "" {
71+
c.WorkDir = dir
72+
}
73+
updateState(c.WorkDir)
5574

5675
return c
5776
}
@@ -61,14 +80,20 @@ func Save(c Config) {
6180
applyDefaults(&c)
6281
normalizePaths(&c)
6382

64-
_ = os.MkdirAll(Dir, 0700)
83+
if c.WorkDir == "" {
84+
c.WorkDir = preferredWorkDir()
85+
}
86+
87+
p := configPath(c.WorkDir)
88+
_ = os.MkdirAll(filepath.Dir(p), 0700)
6589

6690
data, err := json.MarshalIndent(c, "", " ")
6791
if err != nil {
6892
return
6993
}
7094

71-
_ = os.WriteFile(File, data, 0600)
95+
_ = os.WriteFile(p, data, 0600)
96+
updateState(c.WorkDir)
7297
}
7398

7499
/* ============================================================
@@ -121,6 +146,107 @@ func (c Config) GetWorkDir() string {
121146
if c.WorkDir != "" {
122147
return c.WorkDir
123148
}
149+
if d := preferredWorkDir(); d != "" {
150+
return d
151+
}
152+
wd, _ := os.Getwd()
153+
return wd
154+
}
155+
156+
func RecentWorkDirs() []string {
157+
s := loadState()
158+
out := make([]string, 0, len(s.RecentWorkDirs))
159+
for _, d := range s.RecentWorkDirs {
160+
if d != "" {
161+
if info, err := os.Stat(d); err != nil || !info.IsDir() {
162+
continue
163+
}
164+
out = append(out, d)
165+
}
166+
}
167+
return out
168+
}
169+
170+
func preferredWorkDir() string {
171+
s := loadState()
172+
if s.ActiveWorkDir != "" {
173+
if info, err := os.Stat(s.ActiveWorkDir); err == nil && info.IsDir() {
174+
return s.ActiveWorkDir
175+
}
176+
}
124177
wd, _ := os.Getwd()
125178
return wd
126179
}
180+
181+
func configPath(workDir string) string {
182+
if workDir == "" {
183+
return File
184+
}
185+
return filepath.Join(workDir, ".git", ".genius", "config.json")
186+
}
187+
188+
func statePath() string {
189+
home, err := os.UserHomeDir()
190+
if err != nil || home == "" {
191+
return ""
192+
}
193+
return filepath.Join(home, stateDirName, stateFileName)
194+
}
195+
196+
func loadState() appState {
197+
p := statePath()
198+
if p == "" {
199+
return appState{}
200+
}
201+
202+
data, err := os.ReadFile(p)
203+
if err != nil {
204+
return appState{}
205+
}
206+
207+
var s appState
208+
if err := json.Unmarshal(data, &s); err != nil {
209+
return appState{}
210+
}
211+
return s
212+
}
213+
214+
func saveState(s appState) {
215+
p := statePath()
216+
if p == "" {
217+
return
218+
}
219+
220+
_ = os.MkdirAll(filepath.Dir(p), 0700)
221+
b, err := json.MarshalIndent(s, "", " ")
222+
if err != nil {
223+
return
224+
}
225+
_ = os.WriteFile(p, b, 0600)
226+
}
227+
228+
func updateState(workDir string) {
229+
if workDir == "" {
230+
return
231+
}
232+
233+
abs, err := filepath.Abs(workDir)
234+
if err != nil {
235+
return
236+
}
237+
238+
s := loadState()
239+
s.ActiveWorkDir = abs
240+
241+
next := []string{abs}
242+
for _, d := range s.RecentWorkDirs {
243+
if d != abs && d != "" {
244+
next = append(next, d)
245+
}
246+
if len(next) >= 10 {
247+
break
248+
}
249+
}
250+
s.RecentWorkDirs = next
251+
saveState(s)
252+
}

internal/config/history.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package config
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"time"
10+
)
11+
12+
type HistoryEntry struct {
13+
Timestamp int64 `json:"timestamp"`
14+
WorkDir string `json:"work_dir"`
15+
Section string `json:"section"`
16+
Action string `json:"action"`
17+
Success bool `json:"success"`
18+
Note string `json:"note,omitempty"`
19+
}
20+
21+
func RecordHistory(workDir, section, action string, success bool, note string) {
22+
p := historyPath()
23+
if p == "" {
24+
return
25+
}
26+
_ = os.MkdirAll(filepath.Dir(p), 0700)
27+
28+
e := HistoryEntry{
29+
Timestamp: time.Now().UnixMilli(),
30+
WorkDir: workDir,
31+
Section: section,
32+
Action: action,
33+
Success: success,
34+
Note: strings.TrimSpace(note),
35+
}
36+
37+
b, err := json.Marshal(e)
38+
if err != nil {
39+
return
40+
}
41+
42+
f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
43+
if err != nil {
44+
return
45+
}
46+
defer f.Close()
47+
_, _ = f.Write(append(b, '\n'))
48+
}
49+
50+
func RecentHistory(limit int) []HistoryEntry {
51+
if limit <= 0 {
52+
limit = 20
53+
}
54+
p := historyPath()
55+
if p == "" {
56+
return nil
57+
}
58+
f, err := os.Open(p)
59+
if err != nil {
60+
return nil
61+
}
62+
defer f.Close()
63+
64+
var all []HistoryEntry
65+
s := bufio.NewScanner(f)
66+
for s.Scan() {
67+
line := strings.TrimSpace(s.Text())
68+
if line == "" {
69+
continue
70+
}
71+
var e HistoryEntry
72+
if json.Unmarshal([]byte(line), &e) == nil {
73+
all = append(all, e)
74+
}
75+
}
76+
if len(all) <= limit {
77+
return all
78+
}
79+
return all[len(all)-limit:]
80+
}
81+
82+
func HistorySuggestions(workDir string) []string {
83+
recent := RecentHistory(60)
84+
if len(recent) == 0 {
85+
return []string{"Run Tools -> Setup / Reconfigure to initialize this project."}
86+
}
87+
88+
var inDir []HistoryEntry
89+
for _, e := range recent {
90+
if e.WorkDir == workDir {
91+
inDir = append(inDir, e)
92+
}
93+
}
94+
if len(inDir) == 0 {
95+
return []string{"No history for this repo yet. Start with Setup / Reconfigure."}
96+
}
97+
98+
failures := 0
99+
pushCount := 0
100+
pullCount := 0
101+
changeDirCount := 0
102+
for _, e := range inDir {
103+
if !e.Success {
104+
failures++
105+
}
106+
if e.Action == "push" {
107+
pushCount++
108+
}
109+
if e.Action == "pull" || e.Action == "smart_pull" {
110+
pullCount++
111+
}
112+
if e.Action == "change_project_dir" {
113+
changeDirCount++
114+
}
115+
}
116+
117+
out := make([]string, 0, 3)
118+
if failures > 0 {
119+
out = append(out, "Recent failures detected. Run Tools -> Doctor first.")
120+
}
121+
if pushCount >= 3 && pullCount == 0 {
122+
out = append(out, "You push often without pulling. Use Smart Pull before push to avoid conflicts.")
123+
}
124+
if changeDirCount >= 2 {
125+
out = append(out, "You switch repos often. Use Setup recent-directory shortcuts (1/2/3).")
126+
}
127+
if len(out) == 0 {
128+
out = append(out, "Workflow looks healthy. Continue with Daily Git Operations.")
129+
}
130+
return out
131+
}
132+
133+
func historyPath() string {
134+
home, err := os.UserHomeDir()
135+
if err != nil || home == "" {
136+
return ""
137+
}
138+
return filepath.Join(home, ".git-genius", "history.jsonl")
139+
}
140+

internal/doctor/doctor.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package doctor
22

33
import (
44
"os"
5-
"path/filepath"
65

76
"git-genius/internal/config"
87
"git-genius/internal/github"
@@ -29,6 +28,7 @@ func Run() {
2928
checkRemote()
3029
checkGitHubToken()
3130
checkGitHubRepo()
31+
checkGitCredentialHelper()
3232
checkErrorLog()
3333

3434
ui.Success("Doctor check completed")
@@ -174,14 +174,7 @@ func checkGitHubRepo() {
174174
}
175175

176176
func checkErrorLog() {
177-
cfg := config.Load()
178-
179-
logPath := filepath.Join(
180-
cfg.GetWorkDir(),
181-
".git",
182-
".genius",
183-
"error.log",
184-
)
177+
logPath := system.ErrorLogPath()
185178

186179
if _, err := os.Stat(logPath); err == nil {
187180
ui.Warn("Error log exists")
@@ -190,3 +183,15 @@ func checkErrorLog() {
190183
ui.Success("No error log found")
191184
}
192185
}
186+
187+
func checkGitCredentialHelper() {
188+
out, err := system.GitOutput("config", "--global", "--get", "credential.helper")
189+
if err != nil || out == "" {
190+
return
191+
}
192+
193+
if out == "!/usr/bin/gh auth git-credential" && !system.CommandExists("gh") {
194+
ui.Warn("Git credential helper uses gh, but gh is not installed")
195+
ui.Info("Push may still work, but auth helper warnings will appear")
196+
}
197+
}

0 commit comments

Comments
 (0)