package cmd import ( "fmt" "os" "strings" "git.jnss.me/joakim/opal/internal/engine" "github.com/spf13/cobra" ) var annotateCmd = &cobra.Command{ Use: "annotate [filter...] [text]", Short: "Add an annotation to a task", Long: `Add a timestamped annotation to a task. Examples: opal 2 annotate Traced to token expiry in middleware opal annotate +bug Found root cause in auth handler`, Run: func(cmd *cobra.Command, args []string) { parsed := getParsedArgs(cmd) if err := annotateTask(parsed.Filters, parsed.Modifiers); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } }, } func annotateTask(filterArgs, textArgs []string) error { if len(filterArgs) == 0 { return fmt.Errorf("no task specified") } if len(textArgs) == 0 { return fmt.Errorf("annotation text is required") } text := strings.Join(textArgs, " ") filter, err := engine.ParseFilter(filterArgs) if err != nil { return fmt.Errorf("failed to parse filter: %w", err) } ws, err := engine.LoadWorkingSet() if err != nil { return fmt.Errorf("failed to load working set: %w", err) } var task *engine.Task if len(filter.IDs) > 0 { if len(filter.IDs) != 1 { return fmt.Errorf("annotate requires exactly one task") } task, err = ws.GetTaskByDisplayID(filter.IDs[0]) if err != nil { return err } } else { tasks, err := engine.GetTasks(filter) if err != nil { return fmt.Errorf("failed to get tasks: %w", err) } if len(tasks) == 0 { return fmt.Errorf("no tasks matched filter") } if len(tasks) > 1 { return fmt.Errorf("annotate requires exactly one task (filter matched %d)", len(tasks)) } task = tasks[0] } if err := task.Annotate(text); err != nil { return fmt.Errorf("failed to annotate task: %w", err) } fmt.Printf("Annotated task %s\n", engine.FormatTaskSummary(task, ws)) return nil }