Documentation
¶
Overview ¶
CANARY: REQ=CBIN-134; FEATURE="SpecModification"; ASPECT=CLI; STATUS=IMPL; DOC=user:docs/user/spec-modification-guide.md; DOC_HASH=676eb2a18c9d002a; UPDATED=2025-10-17
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var SpecifyCmd = &cobra.Command{ Use: "specify <feature-description>", Short: "Create a new requirement specification", Long: `Create a new CANARY requirement specification from a feature description. Generates a new requirement ID with aspect-based format (CBIN-SECURITY_REVIEW-XXX), creates a spec directory, and populates it with a specification template.`, 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 } } featureDesc := strings.Join(args, " ") aspect, _ := cmd.Flags().GetString("aspect") if err := reqid.ValidateAspect(aspect); err != nil { return fmt.Errorf("invalid aspect: %w", err) } aspect = reqid.NormalizeAspect(aspect) generatedID, err := reqid.GenerateNextID("CBIN", aspect) if err != nil { return fmt.Errorf("generate requirement ID: %w", err) } featureName := strings.Map(func(r rune) rune { if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') { return r } return '-' }, featureDesc) if len(featureName) > 50 { featureName = featureName[:50] } featureName = strings.Trim(featureName, "-") specsDir := ".canary/specs" specDir := filepath.Join(specsDir, fmt.Sprintf("%s-%s", generatedID, featureName)) specFile := filepath.Join(specDir, "spec.md") if err := os.MkdirAll(specDir, 0750); err != nil { return fmt.Errorf("create spec directory: %w", err) } templateContent, err := utils.ReadEmbeddedFile("base/templates/spec-template.md") if err != nil { return fmt.Errorf("read spec template: %w", err) } content := string(templateContent) content = strings.ReplaceAll(content, "CBIN-XXX", generatedID) content = strings.ReplaceAll(content, "[FEATURE NAME]", featureDesc) content = strings.ReplaceAll(content, "YYYY-MM-DD", time.Now().UTC().Format("2006-01-02")) content = strings.ReplaceAll(content, "SECURITY_REVIEW", aspect) if err := os.WriteFile(specFile, []byte(content), 0640); err != nil { return fmt.Errorf("write spec file: %w", err) } fmt.Printf("✅ Created specification: %s\n", specFile) fmt.Printf("\nRequirement ID: %s\n", generatedID) fmt.Printf("Aspect: %s\n", aspect) fmt.Printf("Feature: %s\n", featureDesc) fmt.Println("\nNext steps:") fmt.Printf(" 1. Edit %s to complete the specification\n", specFile) fmt.Printf(" 2. Run: canary plan %s\n", generatedID) return nil }, }
CANARY: REQ=CBIN-120; FEATURE="SpecifyCmd"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.