Implement opal-task Phases 6-8: Complete CLI Implementation

Phase 6: Display and Basic Commands
- Add display.go with colored formatting for tasks, projects, tags
- Implement cmd/root.go with Cobra command structure
- Implement cmd/list.go for listing and filtering tasks
- Implement cmd/add.go with support for regular and recurring tasks
- Implement cmd/done.go with bulk completion and confirmation

Phase 7: Advanced Commands
- Implement cmd/modify.go for updating task attributes
- Implement cmd/delete.go with soft delete confirmation
- Implement cmd/start.go and cmd/stop.go for task timing
- Implement cmd/count.go for counting filtered tasks
- Implement cmd/projects.go and cmd/tags.go for aggregation

Phase 8: Integration and Polish
- Update main.go to use CLI commands
- Add colored output with fatih/color
- Format task lists with proper alignment
- Highlight overdue tasks in red, upcoming in yellow
- Test end-to-end workflow: add, list, done, recurring tasks
- Verify recurrence spawning works correctly

All CLI commands functional and tested!
This commit is contained in:
2026-01-04 18:17:04 +01:00
parent cb4b7ac14b
commit 6e6c3dbea4
13 changed files with 919 additions and 15 deletions
+132 -1
View File
@@ -1,3 +1,134 @@
package cmd
// TODO: Implement add command
import (
"fmt"
"os"
"strings"
"git.jnss.me/joakim/opal/internal/engine"
"github.com/spf13/cobra"
)
var addCmd = &cobra.Command{
Use: "add <description> [modifiers...]",
Short: "Add a new task",
Long: `Add a new task with optional modifiers.
Examples:
opal add "Buy groceries"
opal add "Review PR" priority:H project:backend
opal add "Team meeting" due:mon recur:1w +meetings`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if err := addTask(args); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
},
}
func addTask(args []string) error {
// First arg is description, rest are modifiers
description := args[0]
modifierArgs := args[1:]
// Parse modifiers
var mod *engine.Modifier
var err error
if len(modifierArgs) > 0 {
mod, err = engine.ParseModifier(modifierArgs)
if err != nil {
return fmt.Errorf("failed to parse modifiers: %w", err)
}
}
// Check if this is a recurring task
isRecurring := mod != nil && mod.SetAttributes["recur"] != nil
if isRecurring {
// Create recurring task (template + first instance)
return addRecurringTask(description, mod)
}
// Create regular task
task, err := engine.CreateTaskWithModifier(description, mod)
if err != nil {
return fmt.Errorf("failed to create task: %w", err)
}
fmt.Printf("Created task %s\n", task.UUID)
if len(task.Tags) > 0 {
fmt.Printf("Tags: %s\n", strings.Join(task.Tags, ", "))
}
return nil
}
func addRecurringTask(description string, mod *engine.Modifier) error {
// Extract recurrence pattern
recurPattern := mod.SetAttributes["recur"]
if recurPattern == nil {
return fmt.Errorf("no recurrence pattern specified")
}
duration, err := engine.ParseRecurrencePattern(*recurPattern)
if err != nil {
return fmt.Errorf("invalid recurrence pattern: %w", err)
}
// Create template task
template, err := engine.CreateTask(description)
if err != nil {
return fmt.Errorf("failed to create template: %w", err)
}
template.Status = engine.StatusRecurring
template.RecurrenceDuration = &duration
// Apply other modifiers to template (except recur)
if mod != nil {
tempMod := &engine.Modifier{
SetAttributes: make(map[string]*string),
AddTags: mod.AddTags,
RemoveTags: mod.RemoveTags,
}
// Copy all attributes except recur
for key, val := range mod.SetAttributes {
if key != "recur" {
tempMod.SetAttributes[key] = val
}
}
if err := tempMod.Apply(template); err != nil {
return fmt.Errorf("failed to apply modifiers to template: %w", err)
}
}
// Create first instance
instance, err := engine.CreateTask(description)
if err != nil {
return fmt.Errorf("failed to create first instance: %w", err)
}
instance.ParentUUID = &template.UUID
instance.Due = template.Due
instance.Project = template.Project
instance.Priority = template.Priority
if err := instance.Save(); err != nil {
return fmt.Errorf("failed to save first instance: %w", err)
}
// Copy tags to instance
for _, tag := range template.Tags {
instance.AddTag(tag)
}
fmt.Printf("Created recurring task %s\n", template.UUID)
fmt.Printf("First instance: %s\n", instance.UUID)
fmt.Printf("Recurrence: %s\n", engine.FormatRecurrenceDuration(duration))
return nil
}