Files
gems/opal-task/cmd/done.go
T
joakim b02c40f716 feat: improve CLI output with relative dates, rich feedback, and recurring task info
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>
2026-02-19 13:44:56 +01:00

98 lines
2.2 KiB
Go

package cmd
import (
"fmt"
"os"
"git.jnss.me/joakim/opal/internal/engine"
"github.com/spf13/cobra"
)
var doneCmd = &cobra.Command{
Use: "done [filter...]",
Short: "Mark tasks as completed",
Long: `Mark one or more tasks as completed.
Examples:
opal done 1 # Complete task with display ID 1
opal 1 done # Flexible syntax (same as above)
opal done +urgent # Complete all urgent tasks
opal +urgent done # Flexible syntax (same as above)
opal done project:backend`,
Run: func(cmd *cobra.Command, args []string) {
parsed := getParsedArgs(cmd)
if err := completeTasks(parsed.Filters); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
},
}
func completeTasks(args []string) error {
// Parse filter
filter, err := engine.ParseFilter(args)
if err != nil {
return fmt.Errorf("failed to parse filter: %w", err)
}
// Load working set to resolve IDs
ws, err := engine.LoadWorkingSet()
if err != nil {
return fmt.Errorf("failed to load working set: %w", err)
}
// Get matching 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
matched, err := engine.GetTasks(filter)
if err != nil {
return fmt.Errorf("failed to get tasks: %w", err)
}
tasks = matched
}
if len(tasks) == 0 {
return fmt.Errorf("no tasks matched filter")
}
if len(tasks) > 1 {
fmt.Print(engine.FormatTaskConfirmList("complete", tasks, ws))
fmt.Printf("Proceed? (y/N): ")
var confirm string
fmt.Scanln(&confirm)
if confirm != "y" && confirm != "Y" {
fmt.Println("Cancelled.")
return nil
}
}
// Complete tasks
completed := 0
for _, task := range tasks {
if _, err := task.Complete(); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to complete task %s: %v\n", task.UUID, err)
} else {
completed++
}
}
if len(tasks) == 1 {
fmt.Printf("Completed task %s\n", engine.FormatTaskSummary(tasks[0], ws))
} else {
fmt.Printf("Completed %d task(s).\n", completed)
}
return nil
}