rdev/cmd/sdlc/cmd_state.go
jordan 425ef0f806 feat: add SDLC orchestration - library, CLI, and API integration
Implements deterministic feature lifecycle management for agent-driven
development. Agents use the CLI in pods; operators control via REST API.

Library (internal/sdlc/):
- Feature lifecycle with 10 phases (draft → released)
- Classifier engine with priority-ordered rules
- Artifact tracking with approval workflow
- Task management within features
- YAML-based state persistence

CLI (cmd/sdlc/):
- init, state, next, feature, artifact, task, query commands
- --json flag for machine-readable output
- Runs inside project pods

API (21 endpoints under /projects/{id}/sdlc/):
- State: GET /state, GET /next
- Features: CRUD + transition/block/unblock
- Artifacts: approve/reject per type
- Tasks: add/start/complete/block
- Queries: blocked/ready/needs-approval

Architecture:
- Port: SDLCExecutor interface (internal/port/)
- Adapter: kubectl exec into pods (internal/adapter/kubernetes/)
- Service: pod resolution + logging (internal/service/)
- Handlers: 5 files under 500-line limit (internal/handlers/)

Also includes template upgrades (chassis framework, UI components,
OpenAPI helpers, backend/frontend guides) and component improvements.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 09:57:05 -07:00

67 lines
1.4 KiB
Go

package main
import (
"fmt"
"github.com/orchard9/rdev/internal/sdlc"
"github.com/spf13/cobra"
)
var stateCmd = &cobra.Command{
Use: "state",
Short: "Show current SDLC state",
RunE: func(_ *cobra.Command, _ []string) error {
root := mustResolveRoot()
state, err := sdlc.LoadState(root)
if err != nil {
return err
}
if jsonOutput {
printJSON(state)
return nil
}
fmt.Printf("SDLC State (v%d)\n", state.Version)
fmt.Printf(" Project: %s\n", state.Project.Name)
if state.Project.CurrentRoadmap != "" {
fmt.Printf(" Roadmap: %s\n", state.Project.CurrentRoadmap)
}
fmt.Println()
if len(state.ActiveWork.Features) > 0 {
fmt.Println("Active Features:")
for _, f := range state.ActiveWork.Features {
branch := ""
if f.Branch != "" {
branch = fmt.Sprintf(" (%s)", f.Branch)
}
fmt.Printf(" - %s [%s]%s\n", f.Slug, f.Phase, branch)
}
fmt.Println()
}
if len(state.Blocked) > 0 {
fmt.Println("Blocked Items:")
for _, b := range state.Blocked {
fmt.Printf(" - %s/%s: %s\n", b.Type, b.Slug, b.Reason)
}
fmt.Println()
}
if state.LastAction != "" {
fmt.Printf("Last Action: %s by %s\n", state.LastAction, state.LastActor)
}
if state.LastUpdated != nil {
fmt.Printf("Last Updated: %s\n", state.LastUpdated.Format("2006-01-02 15:04:05"))
}
return nil
},
}
func init() {
rootCmd.AddCommand(stateCmd)
}