rdev/cmd/sdlc/cmd_query.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

200 lines
3.8 KiB
Go

package main
import (
"fmt"
"github.com/orchard9/rdev/internal/sdlc"
"github.com/spf13/cobra"
)
var queryCmd = &cobra.Command{
Use: "query",
Short: "Query SDLC state",
}
var queryBlockedCmd = &cobra.Command{
Use: "blocked",
Short: "List all blocked items",
RunE: func(_ *cobra.Command, _ []string) error {
root := mustResolveRoot()
features, err := sdlc.ListFeatures(root)
if err != nil {
return err
}
type blockedInfo struct {
Slug string `json:"slug"`
Phase string `json:"phase"`
Blockers []string `json:"blockers"`
}
var blocked []blockedInfo
for _, f := range features {
if f.IsBlocked() {
blocked = append(blocked, blockedInfo{
Slug: f.Slug,
Phase: string(f.Phase),
Blockers: f.Blockers,
})
}
}
if jsonOutput {
return printJSON(blocked)
}
if len(blocked) == 0 {
fmt.Println("No blocked items.")
return nil
}
fmt.Println("Blocked Items:")
for _, b := range blocked {
fmt.Printf(" %s [%s]:\n", b.Slug, b.Phase)
for _, reason := range b.Blockers {
fmt.Printf(" - %s\n", reason)
}
}
return nil
},
}
var queryReadyCmd = &cobra.Command{
Use: "ready",
Short: "List items ready for work",
RunE: func(_ *cobra.Command, _ []string) error {
root := mustResolveRoot()
state, err := sdlc.LoadState(root)
if err != nil {
return err
}
cfg, err := sdlc.LoadConfig(root)
if err != nil {
return err
}
features, err := sdlc.ListFeatures(root)
if err != nil {
return err
}
classifier := sdlc.NewClassifier()
type readyInfo struct {
Slug string `json:"slug"`
Phase string `json:"phase"`
Action string `json:"action"`
}
var ready []readyInfo
for _, f := range features {
if f.IsBlocked() {
continue
}
cl := classifier.Classify(&sdlc.EvalContext{
State: state,
Feature: f,
Config: cfg,
Root: root,
})
if cl.Action != sdlc.ActionIdle && cl.Action != sdlc.ActionBlocked && cl.Action != sdlc.ActionAwaitApproval {
ready = append(ready, readyInfo{
Slug: f.Slug,
Phase: string(f.Phase),
Action: string(cl.Action),
})
}
}
if jsonOutput {
return printJSON(ready)
}
if len(ready) == 0 {
fmt.Println("No items ready for work.")
return nil
}
fmt.Println("Ready for Work:")
for _, r := range ready {
fmt.Printf(" %-20s [%-15s] -> %s\n", r.Slug, r.Phase, r.Action)
}
return nil
},
}
var queryNeedsApprovalCmd = &cobra.Command{
Use: "needs-approval",
Short: "List items awaiting approval",
RunE: func(_ *cobra.Command, _ []string) error {
root := mustResolveRoot()
state, err := sdlc.LoadState(root)
if err != nil {
return err
}
cfg, err := sdlc.LoadConfig(root)
if err != nil {
return err
}
features, err := sdlc.ListFeatures(root)
if err != nil {
return err
}
classifier := sdlc.NewClassifier()
type approvalInfo struct {
Slug string `json:"slug"`
Phase string `json:"phase"`
Message string `json:"message"`
}
var pending []approvalInfo
for _, f := range features {
cl := classifier.Classify(&sdlc.EvalContext{
State: state,
Feature: f,
Config: cfg,
Root: root,
})
if cl.Action == sdlc.ActionAwaitApproval {
pending = append(pending, approvalInfo{
Slug: f.Slug,
Phase: string(f.Phase),
Message: cl.Message,
})
}
}
if jsonOutput {
return printJSON(pending)
}
if len(pending) == 0 {
fmt.Println("No items awaiting approval.")
return nil
}
fmt.Println("Awaiting Approval:")
for _, p := range pending {
fmt.Printf(" %-20s [%-15s] %s\n", p.Slug, p.Phase, p.Message)
}
return nil
},
}
func init() {
queryCmd.AddCommand(
queryBlockedCmd,
queryReadyCmd,
queryNeedsApprovalCmd,
)
rootCmd.AddCommand(queryCmd)
}