11package main
22
33import (
4+ "bufio"
45 "flag"
56 "fmt"
67 "log"
78 "os"
9+ "path/filepath"
10+ "strings"
811
912 "github.com/Bharath-code/git-scope/internal/config"
1013 "github.com/Bharath-code/git-scope/internal/scan"
1114 "github.com/Bharath-code/git-scope/internal/tui"
1215)
1316
14- const version = "0.1.0 "
17+ const version = "0.1.1 "
1518
1619func usage () {
1720 fmt .Fprintf (os .Stderr , `git-scope v%s — A fast TUI to see the status of all git repositories
1821
1922Usage:
20- git-scope [command]
23+ git-scope [command] [directories...]
2124
2225Commands:
2326 (default) Launch TUI dashboard
24- scan Scan configured roots and print repos (JSON)
25- tui Launch TUI dashboard (same as default)
27+ scan Scan and print repos (JSON)
28+ init Create config file interactively
2629 help Show this help
2730
31+ Examples:
32+ git-scope # Scan configured dirs or current dir
33+ git-scope ~/code ~/work # Scan specific directories
34+ git-scope scan . # Scan current directory (JSON)
35+ git-scope init # Setup config interactively
36+
2837Flags:
2938` , version )
3039 flag .PrintDefaults ()
@@ -35,10 +44,21 @@ func main() {
3544 configPath := flag .String ("config" , config .DefaultConfigPath (), "Path to config file" )
3645 flag .Parse ()
3746
38- // Default command is "tui" if no args provided
39- cmd := "tui"
40- if flag .NArg () >= 1 {
41- cmd = flag .Arg (0 )
47+ args := flag .Args ()
48+ cmd := ""
49+ dirs := []string {}
50+
51+ // Parse command and directories
52+ if len (args ) >= 1 {
53+ switch args [0 ] {
54+ case "scan" , "tui" , "help" , "init" , "-h" , "--help" :
55+ cmd = args [0 ]
56+ dirs = args [1 :]
57+ default :
58+ // Assume it's a directory
59+ cmd = "tui"
60+ dirs = args
61+ }
4262 }
4363
4464 // Handle help early
@@ -47,12 +67,26 @@ func main() {
4767 return
4868 }
4969
70+ // Handle init command
71+ if cmd == "init" {
72+ runInit ()
73+ return
74+ }
75+
5076 // Load configuration
5177 cfg , err := config .Load (* configPath )
5278 if err != nil {
5379 log .Fatalf ("failed to load config: %v" , err )
5480 }
5581
82+ // Override roots if directories provided via CLI
83+ if len (dirs ) > 0 {
84+ cfg .Roots = expandDirs (dirs )
85+ } else if ! config .ConfigExists (* configPath ) {
86+ // No config file and no CLI dirs - use smart defaults
87+ cfg .Roots = getSmartDefaults ()
88+ }
89+
5690 switch cmd {
5791 case "scan" :
5892 repos , err := scan .ScanRoots (cfg .Roots , cfg .Ignore )
@@ -74,3 +108,143 @@ func main() {
74108 os .Exit (1 )
75109 }
76110}
111+
112+ // expandDirs converts relative paths and ~ to absolute paths
113+ func expandDirs (dirs []string ) []string {
114+ result := make ([]string , 0 , len (dirs ))
115+ for _ , d := range dirs {
116+ if d == "." {
117+ if cwd , err := os .Getwd (); err == nil {
118+ result = append (result , cwd )
119+ }
120+ } else if strings .HasPrefix (d , "~/" ) {
121+ if home , err := os .UserHomeDir (); err == nil {
122+ result = append (result , filepath .Join (home , d [2 :]))
123+ }
124+ } else if filepath .IsAbs (d ) {
125+ result = append (result , d )
126+ } else {
127+ if abs , err := filepath .Abs (d ); err == nil {
128+ result = append (result , abs )
129+ }
130+ }
131+ }
132+ return result
133+ }
134+
135+ // getSmartDefaults returns directories that likely contain git repos
136+ func getSmartDefaults () []string {
137+ home , err := os .UserHomeDir ()
138+ if err != nil {
139+ cwd , _ := os .Getwd ()
140+ return []string {cwd }
141+ }
142+
143+ // Common developer directories to check
144+ candidates := []string {
145+ filepath .Join (home , "code" ),
146+ filepath .Join (home , "Code" ),
147+ filepath .Join (home , "projects" ),
148+ filepath .Join (home , "Projects" ),
149+ filepath .Join (home , "dev" ),
150+ filepath .Join (home , "Dev" ),
151+ filepath .Join (home , "work" ),
152+ filepath .Join (home , "Work" ),
153+ filepath .Join (home , "repos" ),
154+ filepath .Join (home , "Repos" ),
155+ filepath .Join (home , "src" ),
156+ filepath .Join (home , "Developer" ),
157+ filepath .Join (home , "Documents" , "GitHub" ),
158+ filepath .Join (home , "Desktop" , "projects" ),
159+ }
160+
161+ found := []string {}
162+ for _ , dir := range candidates {
163+ if info , err := os .Stat (dir ); err == nil && info .IsDir () {
164+ found = append (found , dir )
165+ }
166+ }
167+
168+ // If no common dirs found, use current directory
169+ if len (found ) == 0 {
170+ cwd , _ := os .Getwd ()
171+ return []string {cwd }
172+ }
173+
174+ return found
175+ }
176+
177+ // runInit creates a config file interactively
178+ func runInit () {
179+ configPath := config .DefaultConfigPath ()
180+
181+ fmt .Println ("git-scope init — Setup your configuration" )
182+ fmt .Println ()
183+
184+ // Check if config already exists
185+ if config .ConfigExists (configPath ) {
186+ fmt .Printf ("Config file already exists at: %s\n " , configPath )
187+ fmt .Print ("Overwrite? [y/N]: " )
188+ reader := bufio .NewReader (os .Stdin )
189+ answer , _ := reader .ReadString ('\n' )
190+ answer = strings .TrimSpace (strings .ToLower (answer ))
191+ if answer != "y" && answer != "yes" {
192+ fmt .Println ("Aborted." )
193+ return
194+ }
195+ }
196+
197+ reader := bufio .NewReader (os .Stdin )
198+
199+ // Get directories
200+ fmt .Println ("Enter directories to scan for git repos (one per line, empty line to finish):" )
201+ fmt .Println ("Examples: ~/code, ~/projects, ~/work" )
202+ fmt .Println ()
203+
204+ dirs := []string {}
205+ for {
206+ fmt .Print ("> " )
207+ line , _ := reader .ReadString ('\n' )
208+ line = strings .TrimSpace (line )
209+ if line == "" {
210+ break
211+ }
212+ dirs = append (dirs , line )
213+ }
214+
215+ if len (dirs ) == 0 {
216+ // Suggest detected directories
217+ detected := getSmartDefaults ()
218+ if len (detected ) > 0 {
219+ fmt .Println ("\n No directories entered. Detected these on your system:" )
220+ for _ , d := range detected {
221+ fmt .Printf (" - %s\n " , d )
222+ }
223+ fmt .Print ("\n Use these? [Y/n]: " )
224+ answer , _ := reader .ReadString ('\n' )
225+ answer = strings .TrimSpace (strings .ToLower (answer ))
226+ if answer == "" || answer == "y" || answer == "yes" {
227+ dirs = detected
228+ } else {
229+ fmt .Println ("No directories configured. Run 'git-scope init' again to set up." )
230+ return
231+ }
232+ }
233+ }
234+
235+ // Get editor
236+ fmt .Print ("\n Editor command (default: code): " )
237+ editor , _ := reader .ReadString ('\n' )
238+ editor = strings .TrimSpace (editor )
239+ if editor == "" {
240+ editor = "code"
241+ }
242+
243+ // Create config
244+ if err := config .CreateConfig (configPath , dirs , editor ); err != nil {
245+ log .Fatalf ("Failed to create config: %v" , err )
246+ }
247+
248+ fmt .Printf ("\n ✅ Config created at: %s\n " , configPath )
249+ fmt .Println ("\n Run 'git-scope' to launch the dashboard!" )
250+ }
0 commit comments