package main import ( "fmt" "os" "path/filepath" "strings" ) // Flags holds all parsed CLI flags. type Flags struct { JSON bool Domain string IssueType string // --type Status string Prio string App string Pattern string Risk string } func main() { args := os.Args[1:] if len(args) == 0 { printUsage() os.Exit(0) } // Parse global flags and collect remaining args var noun, verb string var rest []string flags := Flags{} // First arg is noun, second is verb, rest are flags/args pos := 0 for pos < len(args) { arg := args[pos] if strings.HasPrefix(arg, "--") { flag, val, ok := parseFlag(args, pos) pos += flag.consumed if !ok { fmt.Fprintf(os.Stderr, "unknown flag: %s\n", arg) os.Exit(1) } applyFlag(&flags, flag.name, val) } else { // Positional if noun == "" { noun = arg } else if verb == "" { verb = arg } else { rest = append(rest, arg) } pos++ } } // Dispatch switch noun { case "issue": if verb == "" { fmt.Fprintln(os.Stderr, "usage: dev_console issue ") os.Exit(1) } cmdIssue(append([]string{verb}, rest...), flags) case "flow": if verb == "" { fmt.Fprintln(os.Stderr, "usage: dev_console flow ") os.Exit(1) } cmdFlow(append([]string{verb}, rest...), flags) case "work": if verb == "" { fmt.Fprintln(os.Stderr, "usage: dev_console work ") os.Exit(1) } cmdWork(append([]string{verb}, rest...), flags) case "help", "--help", "-h": printUsage() os.Exit(0) default: fmt.Fprintf(os.Stderr, "unknown command: %s\n", noun) printUsage() os.Exit(1) } } type parsedFlag struct { name string consumed int } func parseFlag(args []string, pos int) (parsedFlag, string, bool) { arg := args[pos] // Strip leading -- name := strings.TrimPrefix(arg, "--") // Check for --key=value form if idx := strings.Index(name, "="); idx != -1 { key := name[:idx] val := name[idx+1:] return parsedFlag{name: key, consumed: 1}, val, true } // Boolean flags (no value) switch name { case "json": return parsedFlag{name: name, consumed: 1}, "true", true } // Flags that take a value switch name { case "domain", "type", "status", "prio", "app", "pattern", "risk", "epic", "days": if pos+1 < len(args) && !strings.HasPrefix(args[pos+1], "--") { return parsedFlag{name: name, consumed: 2}, args[pos+1], true } return parsedFlag{name: name, consumed: 1}, "", true } return parsedFlag{consumed: 1}, "", false } func applyFlag(flags *Flags, name, val string) { switch name { case "json": flags.JSON = true case "domain": flags.Domain = val case "type": flags.IssueType = val case "status": flags.Status = val case "prio": flags.Prio = val case "app": flags.App = val case "pattern": flags.Pattern = val case "risk": flags.Risk = val } } func printUsage() { fmt.Print(`dev_console — CLI unificado para issues + flows del registry Usage: dev_console issue list [--domain X] [--type Y] [--status Z] [--prio P] [--json] dev_console issue show NNNN [--json] dev_console issue status NNNN [--json] dev_console issue board [--json] dev_console flow list [--app X] [--pattern P] [--risk R] [--json] dev_console flow show NNNN [--json] dev_console flow status NNNN [--json] dev_console work today [--json] dev_console work dashboard Stubs (v2): issue dep|roadmap|tag|done|stale|create flow create|dod|trace|user-test|run|chain|done work weekly|search Env: FN_REGISTRY_ROOT Root of the fn_registry repo (auto-detected if not set) `) } // mustRegistryRoot returns the registry root or exits. func mustRegistryRoot() string { root, err := findRegistryRoot() if err != nil { fatalf("cannot find registry root: %v\nSet FN_REGISTRY_ROOT env var or run from within fn_registry.", err) } return root } // findRegistryRoot tries FN_REGISTRY_ROOT, then walks up from cwd looking for registry.db. func findRegistryRoot() (string, error) { if r := os.Getenv("FN_REGISTRY_ROOT"); r != "" { return r, nil } cwd, err := os.Getwd() if err != nil { return "", err } dir := cwd for { if _, err := os.Stat(filepath.Join(dir, "registry.db")); err == nil { return dir, nil } parent := filepath.Dir(dir) if parent == dir { break } dir = parent } return "", fmt.Errorf("registry.db not found walking up from %s", cwd) } // fatalf prints an error to stderr and exits with code 1. func fatalf(format string, args ...any) { fmt.Fprintf(os.Stderr, "error: "+format+"\n", args...) os.Exit(1) }