RunNew creates a new PRD by launching an interactive agent session.
(opts NewOptions)
| 23 | |
| 24 | // RunNew creates a new PRD by launching an interactive agent session. |
| 25 | func RunNew(opts NewOptions) error { |
| 26 | // Set defaults |
| 27 | if opts.Name == "" { |
| 28 | opts.Name = "main" |
| 29 | } |
| 30 | if opts.BaseDir == "" { |
| 31 | cwd, err := os.Getwd() |
| 32 | if err != nil { |
| 33 | return fmt.Errorf("failed to get current directory: %w", err) |
| 34 | } |
| 35 | opts.BaseDir = cwd |
| 36 | } |
| 37 | |
| 38 | // Validate name (alphanumeric, -, _) |
| 39 | if !isValidPRDName(opts.Name) { |
| 40 | return fmt.Errorf("invalid PRD name %q: must contain only letters, numbers, hyphens, and underscores", opts.Name) |
| 41 | } |
| 42 | |
| 43 | // Create directory structure: .chief/prds/<name>/ |
| 44 | prdDir := filepath.Join(opts.BaseDir, ".chief", "prds", opts.Name) |
| 45 | if err := os.MkdirAll(prdDir, 0755); err != nil { |
| 46 | return fmt.Errorf("failed to create PRD directory: %w", err) |
| 47 | } |
| 48 | |
| 49 | // Check if prd.md already exists |
| 50 | prdMdPath := filepath.Join(prdDir, "prd.md") |
| 51 | if _, err := os.Stat(prdMdPath); err == nil { |
| 52 | return fmt.Errorf("PRD already exists at %s. Use 'chief edit %s' to modify it", prdMdPath, opts.Name) |
| 53 | } |
| 54 | |
| 55 | // Get the init prompt with the PRD directory path |
| 56 | prompt := embed.GetInitPrompt(prdDir, opts.Context) |
| 57 | if opts.Provider == nil { |
| 58 | return fmt.Errorf("new command requires Provider to be set") |
| 59 | } |
| 60 | |
| 61 | // Launch interactive agent session |
| 62 | fmt.Printf("Creating PRD in %s...\n", prdDir) |
| 63 | fmt.Printf("Launching %s to help you create your PRD...\n", opts.Provider.Name()) |
| 64 | fmt.Println() |
| 65 | |
| 66 | if err := runInteractiveAgent(opts.Provider, opts.BaseDir, prompt); err != nil { |
| 67 | return fmt.Errorf("%s session failed: %w", opts.Provider.Name(), err) |
| 68 | } |
| 69 | |
| 70 | // Check if prd.md was created |
| 71 | if _, err := os.Stat(prdMdPath); os.IsNotExist(err) { |
| 72 | // Clean up empty directory to prevent broken picker entries |
| 73 | os.Remove(prdDir) |
| 74 | fmt.Println("\nNo prd.md was created. Run 'chief new' again to try again.") |
| 75 | return nil |
| 76 | } |
| 77 | |
| 78 | // Validate the created prd.md can be parsed |
| 79 | if _, err := prd.ParseMarkdownPRD(prdMdPath); err != nil { |
| 80 | fmt.Printf("\nWarning: prd.md was created but could not be parsed: %v\n", err) |
| 81 | fmt.Println("You may need to edit it to match the expected format.") |
| 82 | } else { |