plan

package
v0.3.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var PlanCmd = &cobra.Command{
	Use:   "plan <CBIN-XXX> [tech-stack]",
	Short: "Generate technical implementation plan for a requirement",
	Long: `Generate a technical implementation plan from a requirement specification.

Creates a plan.md file in the spec directory with implementation details,
tech stack decisions, and CANARY token placement instructions.`,
	Args: cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		prompt, _ := cmd.Flags().GetString("prompt")
		if prompt != "" {
			if _, err := utils.LoadPrompt(prompt); err != nil {
				return err
			}
		}
		reqID := args[0]
		techStack := ""
		if len(args) > 1 {
			techStack = strings.Join(args[1:], " ")
		}

		aspect, _ := cmd.Flags().GetString("aspect")

		specsDir := ".canary/specs"
		entries, err := os.ReadDir(specsDir)
		if err != nil {
			return fmt.Errorf("read specs directory: %w", err)
		}

		var specDir string
		for _, entry := range entries {
			if strings.HasPrefix(entry.Name(), reqID) && entry.IsDir() {
				specDir = filepath.Join(specsDir, entry.Name())
				break
			}
		}

		if specDir == "" {
			return fmt.Errorf("specification not found for %s", reqID)
		}

		planFile := filepath.Join(specDir, "plan.md")
		if _, err := os.Stat(planFile); err == nil {
			return fmt.Errorf("plan already exists: %s", planFile)
		}

		templateContent, err := utils.ReadEmbeddedFile("base/templates/plan-template.md")
		if err != nil {
			return fmt.Errorf("read plan template: %w", err)
		}

		specFile := filepath.Join(specDir, "spec.md")
		specContent, err := os.ReadFile(specFile)
		if err != nil {
			return fmt.Errorf("read spec file: %w", err)
		}

		featureName := "Feature"
		specAspect := ""
		for _, line := range strings.Split(string(specContent), "\n") {
			if strings.HasPrefix(line, "# Feature Specification:") {
				featureName = strings.TrimPrefix(line, "# Feature Specification: ")
				featureName = strings.TrimSpace(featureName)
			}
			if strings.HasPrefix(line, "**Aspect:**") {

				parts := strings.SplitN(line, ":", 2)
				if len(parts) == 2 {
					aspectVal := strings.TrimSpace(parts[1])

					aspectVal = strings.TrimPrefix(aspectVal, "[")
					aspectVal = strings.Split(aspectVal, "|")[0]
					aspectVal = strings.TrimSpace(aspectVal)
					if aspectVal != "" {
						specAspect = aspectVal
					}
				}
			}
		}

		if aspect == "" {
			if specAspect != "" {
				aspect = specAspect
			} else {
				aspect = "Engine"
			}
		}

		if err := reqid.ValidateAspect(aspect); err != nil {
			return fmt.Errorf("invalid aspect: %w", err)
		}
		aspect = reqid.NormalizeAspect(aspect)

		content := string(templateContent)
		content = strings.ReplaceAll(content, "REQID-XXX", reqID)
		content = strings.ReplaceAll(content, "[FEATURE NAME]", featureName)
		content = strings.ReplaceAll(content, "YYYY-MM-DD", time.Now().UTC().Format("2006-01-02"))
		content = strings.ReplaceAll(content, "SECURITY_REVIEW", aspect)

		if techStack != "" {
			content = strings.ReplaceAll(content, "[Go/Python/JavaScript/etc.]", techStack)
		}

		dbPath := ".canary/canary.db"
		if _, err := os.Stat(dbPath); err == nil {
			db, err := storage.OpenRO(dbPath)
			if err == nil {
				defer db.Close()
				repo := storage.NewGapRepository(db)
				service := gap.NewService(repo)
				gapContent, err := service.FormatGapsForInjection(reqID)
				if err == nil && gapContent != "" {

					content += "\n" + gapContent
				}
			}
		}

		if err := os.WriteFile(planFile, []byte(content), 0640); err != nil {
			return fmt.Errorf("write plan file: %w", err)
		}

		fmt.Printf("✅ Created implementation plan: %s\n", planFile)
		fmt.Printf("\nRequirement: %s\n", reqID)
		fmt.Println("\nNext steps:")
		fmt.Printf("  1. Edit %s to complete the plan\n", planFile)
		fmt.Println("  2. Implement following TDD (test-first)")
		fmt.Println("  3. Add CANARY tokens to source code")
		fmt.Println("  4. Run: canary scan")

		return nil
	},
}

CANARY: REQ=ENG-4305; FEATURE="PlanCmd"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16 CANARY: REQ=CP-259; FEATURE="PlanTemplatePlaceholderContract"; ASPECT=CLI; STATUS=TESTED; TEST=TestCANARY_CBIN_142_CLI_PlanRendersRealID; UPDATED=2026-08-29

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL