b02c40f716
Add relative date formatting (today, tomorrow, in 3d, etc.) for list and detail views. Add structured feedback helpers for add/complete/delete operations showing display IDs and parsed modifiers. Change Complete() to return spawned recurring instance so callers can display recurrence info. Add AppendTask to working set for immediate display ID assignment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"git.jnss.me/joakim/opal/internal/engine"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var modifyCmd = &cobra.Command{
|
|
Use: "modify [modifier...]",
|
|
Short: "Modify task attributes",
|
|
Long: `Modify task attributes. Filters must come before 'modify', modifiers after.
|
|
|
|
Examples:
|
|
opal 2 modify priority:H
|
|
opal +urgent modify due:tomorrow
|
|
opal project:backend modify priority:L
|
|
opal modify priority:H # Modify ALL pending tasks (with confirmation)`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
parsed := getParsedArgs(cmd)
|
|
if err := modifyTasks(parsed.Filters, parsed.Modifiers); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
},
|
|
}
|
|
|
|
func modifyTasks(filterArgs, modifierArgs []string) error {
|
|
// Validate we have modifiers
|
|
if len(modifierArgs) == 0 {
|
|
return fmt.Errorf("no modifiers specified (modifiers must come after 'modify')")
|
|
}
|
|
|
|
// Parse filter (or use default if no filters specified)
|
|
var filter *engine.Filter
|
|
var err error
|
|
|
|
if len(filterArgs) == 0 {
|
|
// No filters = modify all pending tasks (with confirmation)
|
|
filter = engine.DefaultFilter()
|
|
} else {
|
|
filter, err = engine.ParseFilter(filterArgs)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse filter: %w", err)
|
|
}
|
|
}
|
|
|
|
// Load working set for ID resolution
|
|
ws, err := engine.LoadWorkingSet()
|
|
if err != nil {
|
|
// If no working set exists yet, build one from filtered tasks
|
|
tasks, err := engine.GetTasks(filter)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get tasks: %w", err)
|
|
}
|
|
ws, err = engine.BuildWorkingSet(tasks)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to build working set: %w", err)
|
|
}
|
|
}
|
|
|
|
// Resolve tasks
|
|
var tasks []*engine.Task
|
|
|
|
if len(filter.IDs) > 0 {
|
|
// Resolve display IDs
|
|
for _, id := range filter.IDs {
|
|
task, err := ws.GetTaskByDisplayID(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tasks = append(tasks, task)
|
|
}
|
|
} else {
|
|
// Use filter to get tasks
|
|
tasks, err = engine.GetTasks(filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(tasks) == 0 {
|
|
return fmt.Errorf("no tasks matched filter")
|
|
}
|
|
|
|
// Confirm if multiple tasks or no filters specified
|
|
if len(tasks) > 1 || len(filterArgs) == 0 {
|
|
fmt.Print(engine.FormatTaskConfirmList("modify", tasks, ws))
|
|
fmt.Printf("Proceed? (y/N): ")
|
|
var confirm string
|
|
fmt.Scanln(&confirm)
|
|
if confirm != "y" && confirm != "Y" {
|
|
fmt.Println("Cancelled.")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Parse and apply modifiers
|
|
mod, err := engine.ParseModifier(modifierArgs)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse modifiers: %w", err)
|
|
}
|
|
|
|
modified := 0
|
|
for _, task := range tasks {
|
|
if err := mod.Apply(task); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Warning: failed to modify task %s: %v\n", task.UUID, err)
|
|
} else {
|
|
modified++
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Modified %d task(s).\n", modified)
|
|
return nil
|
|
}
|