package main import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/orchard9/rdev/internal/sdlc" "github.com/spf13/cobra" ) var ( rootDir string jsonOutput bool ) var rootCmd = &cobra.Command{ Use: "sdlc", Short: "Deterministic SDLC orchestration tool", Long: "Manage the software development lifecycle with deterministic state, artifacts, and classification.", } func init() { rootCmd.PersistentFlags().StringVar(&rootDir, "root", "", "project root (default: auto-detect)") rootCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "output as JSON") } // resolveRoot finds the project root by looking for .sdlc/ or .git/ walking up. func resolveRoot() (string, error) { if rootDir != "" { abs, err := filepath.Abs(rootDir) if err != nil { return "", fmt.Errorf("resolve root: %w", err) } return abs, nil } dir, err := os.Getwd() if err != nil { return "", fmt.Errorf("get working directory: %w", err) } for { if sdlc.IsInitialized(dir) { return dir, nil } if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil { return dir, nil } parent := filepath.Dir(dir) if parent == dir { break } dir = parent } // Fall back to cwd cwd, _ := os.Getwd() return cwd, nil } // mustResolveRoot resolves root or exits. func mustResolveRoot() string { root, err := resolveRoot() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } return root } // printJSON marshals v as indented JSON and prints to stdout. func printJSON(v any) { data, err := json.MarshalIndent(v, "", " ") if err != nil { fmt.Fprintf(os.Stderr, "Error marshaling JSON: %v\n", err) os.Exit(1) } fmt.Println(string(data)) }