package main import ( "fmt" "os" "github.com/orchard9/rdev/internal/sdlc" "github.com/spf13/cobra" ) var artifactCmd = &cobra.Command{ Use: "artifact", Short: "Manage feature artifacts", } var artifactCreateCmd = &cobra.Command{ Use: "create ", Short: "Create an artifact file (sets status to draft)", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) if !sdlc.IsValidArtifactType(artType) { return fmt.Errorf("invalid artifact type: %s (valid: spec, design, tasks, qa_plan, review, audit, qa_results)", artTypeStr) } f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } // Create the artifact file if it doesn't exist path := sdlc.ArtifactPath(root, slug, artType) if _, err := os.Stat(path); os.IsNotExist(err) { if err := os.WriteFile(path, fmt.Appendf(nil, "# %s: %s\n\n", artType, f.Title), 0o644); err != nil { return fmt.Errorf("create artifact file: %w", err) } } // Update manifest art := f.GetArtifact(artType) if art == nil { art = sdlc.NewArtifact(artType) f.SetArtifact(artType, art) } art.MarkDraft() if err := f.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": string(art.Status), "path": path, }) } fmt.Printf("Created artifact: %s/%s\n", slug, artType) fmt.Printf(" Status: %s\n", art.Status) fmt.Printf(" Path: %s\n", path) return nil }, } var artifactApproveCmd = &cobra.Command{ Use: "approve ", Short: "Approve an artifact", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } art := f.GetArtifact(artType) if art == nil { return sdlc.ErrArtifactNotFound } art.Approve("user") if err := f.Save(root); err != nil { return err } // Record in state state, err := sdlc.LoadState(root) if err != nil { return err } state.RecordAction("APPROVE_ARTIFACT", slug, "user", "success") if err := state.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": "approved", }) } fmt.Printf("Approved: %s/%s\n", slug, artType) return nil }, } var artifactRejectCmd = &cobra.Command{ Use: "reject ", Short: "Reject an artifact", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } art := f.GetArtifact(artType) if art == nil { return sdlc.ErrArtifactNotFound } art.Reject("user") if err := f.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": "rejected", }) } fmt.Printf("Rejected: %s/%s\n", slug, artType) return nil }, } var artifactPassCmd = &cobra.Command{ Use: "pass ", Short: "Mark an artifact as passed (for execution artifacts: review, audit, qa_results)", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } art := f.GetArtifact(artType) if art == nil { return sdlc.ErrArtifactNotFound } art.MarkPassed() if err := f.Save(root); err != nil { return err } // Record in state state, err := sdlc.LoadState(root) if err != nil { return err } state.RecordAction("PASS_ARTIFACT", slug, "user", "success") if err := state.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": "passed", }) } fmt.Printf("Passed: %s/%s\n", slug, artType) return nil }, } var artifactFailCmd = &cobra.Command{ Use: "fail ", Short: "Mark an artifact as failed (for execution artifacts: review, audit, qa_results)", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } art := f.GetArtifact(artType) if art == nil { return sdlc.ErrArtifactNotFound } art.MarkFailed() if err := f.Save(root); err != nil { return err } // Record in state state, err := sdlc.LoadState(root) if err != nil { return err } state.RecordAction("FAIL_ARTIFACT", slug, "user", "success") if err := state.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": "failed", }) } fmt.Printf("Failed: %s/%s\n", slug, artType) return nil }, } var artifactNeedsFixCmd = &cobra.Command{ Use: "needs-fix ", Short: "Mark an artifact as needing fixes (for execution artifacts: review, audit)", Args: cobra.ExactArgs(2), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() slug, artTypeStr := args[0], args[1] artType := sdlc.ArtifactType(artTypeStr) f, err := sdlc.LoadFeature(root, slug) if err != nil { return err } art := f.GetArtifact(artType) if art == nil { return sdlc.ErrArtifactNotFound } art.MarkNeedsFix() if err := f.Save(root); err != nil { return err } // Record in state state, err := sdlc.LoadState(root) if err != nil { return err } state.RecordAction("NEEDS_FIX_ARTIFACT", slug, "user", "success") if err := state.Save(root); err != nil { return err } if jsonOutput { return printJSON(map[string]string{ "feature": slug, "artifact": string(artType), "status": "needs_fix", }) } fmt.Printf("Needs fix: %s/%s\n", slug, artType) return nil }, } var artifactStatusCmd = &cobra.Command{ Use: "status ", Short: "Show all artifact statuses for a feature", Args: cobra.ExactArgs(1), RunE: func(_ *cobra.Command, args []string) error { root := mustResolveRoot() f, err := sdlc.LoadFeature(root, args[0]) if err != nil { return err } if jsonOutput { return printJSON(f.Artifacts) } fmt.Printf("Artifacts for %s:\n", f.Slug) for _, at := range sdlc.ValidArtifactTypes { art := f.GetArtifact(at) if art == nil { fmt.Printf(" %-12s -\n", at) continue } fmt.Printf(" %-12s %s\n", at, art.Status) } return nil }, } func init() { artifactCmd.AddCommand( artifactCreateCmd, artifactApproveCmd, artifactRejectCmd, artifactPassCmd, artifactFailCmd, artifactNeedsFixCmd, artifactStatusCmd, ) rootCmd.AddCommand(artifactCmd) }