chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
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 <list|show|status|board>")
|
||||
os.Exit(1)
|
||||
}
|
||||
cmdIssue(append([]string{verb}, rest...), flags)
|
||||
case "flow":
|
||||
if verb == "" {
|
||||
fmt.Fprintln(os.Stderr, "usage: dev_console flow <list|show|status>")
|
||||
os.Exit(1)
|
||||
}
|
||||
cmdFlow(append([]string{verb}, rest...), flags)
|
||||
case "work":
|
||||
if verb == "" {
|
||||
fmt.Fprintln(os.Stderr, "usage: dev_console work <today|dashboard>")
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user