rdev/cmd/sdlc/root.go
jordan 56e3f83955 feat: add auth scopes, OpenAPI docs, SDLC guides, and code quality improvements
- Add auth.RequireScope() to all handler routes for proper authorization
- Add SDLC OpenAPI endpoint documentation (state, features, tasks, branches, merge, archive, orchestrator)
- Add SDLC documentation guides (getting-started, cli-reference, api-reference, command-catalog)
- Add artifact_test.go for SDLC artifact coverage
- Add CLAUDE.md rules: auth scopes requirement, error wrapping with %w
- Fix error wrapping to use %w instead of %v throughout codebase
- Improve CLI merge command with conflict detection and resolution
- Fix handler tests to include auth middleware for RequireScope
- Add cookbook tree runner scripts for automated testing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:55:50 -07:00

84 lines
1.9 KiB
Go

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 panics (for use in RunE where error is returned).
// Note: Panicking here is intentional as these commands run under cobra which
// catches panics and converts them to errors. For production use, prefer resolveRoot().
func mustResolveRoot() string {
root, err := resolveRoot()
if err != nil {
panic(fmt.Sprintf("resolve root: %v", err))
}
return root
}
// printJSON marshals v as indented JSON and prints to stdout.
// Returns an error if marshaling fails instead of exiting.
func printJSON(v any) error {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
return fmt.Errorf("marshal JSON: %w", err)
}
fmt.Println(string(data))
return nil
}