summaryrefslogtreecommitdiff
path: root/docs/plan-ask-uuid-wrapper.md
blob: f7425c67afd369f072cd30002887974cbaba18ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Plan: `ask` as UUID-only Taskwarrior Wrapper

## Goal

Rewrite the `ask` command from a thin pass-through proxy into a **subcommand-based CLI** that wraps Taskwarrior. The wrapper never exposes numeric task IDs to the caller — only UUIDs. Output is minimal and machine-friendly for coding agents.

The existing `project:<repo> +agent` auto-injection is preserved.

## Subcommands

| Subcommand | Example | Taskwarrior equivalent |
|---|---|---|
| `ask add "Implement X"` | create task | `task project:P +agent add "Implement X"` |
| `ask add priority:H +cli "Fix bug"` | create with priority & tag | same + `priority:H +cli` |
| `ask list` | list pending tasks | `task project:P +agent status:pending export` → reformat |
| `ask info <uuid>` | show one task | `task uuid:<uuid> export` → filtered fields |
| `ask annotate <uuid> "note"` | add annotation | `task uuid:<uuid> annotate "note"` |
| `ask start <uuid>` | start work | `task uuid:<uuid> start` |
| `ask stop <uuid>` | stop work | `task uuid:<uuid> stop` |
| `ask done <uuid>` | mark complete | `task uuid:<uuid> done` |
| `ask priority <uuid> H` | set priority | `task uuid:<uuid> modify priority:H` |
| `ask tag <uuid> +foo` | add tag | `task uuid:<uuid> modify +foo` |
| `ask tag <uuid> -foo` | remove tag | `task uuid:<uuid> modify -foo` |
| `ask dep add <uuid> <dep-uuid>` | add dependency | `task uuid:<uuid> modify depends:<dep-uuid>` |
| `ask dep rm <uuid> <dep-uuid>` | remove dependency | `task uuid:<uuid> modify depends:-<dep-uuid>` |
| `ask dep list <uuid>` | show dependencies | `task uuid:<uuid> export` → `depends` field |
| `ask urgency` | list by urgency | `task project:P +agent export` → sort by urgency |
| `ask delete <uuid>` | delete task | `task uuid:<uuid> delete` |
| `ask export` | raw JSON dump | `task project:P +agent export` → pass through |

### List filters, sort, and limit

`ask list` accepts optional filters, sort, and limit arguments:

| Example | Taskwarrior equivalent |
|---|---|
| `ask list` | `task project:P +agent status:pending export` (default sort: priority-, urgency-) |
| `ask list +READY` | `task project:P +agent +READY export` |
| `ask list +BLOCKED` | `task project:P +agent +BLOCKED export` |
| `ask list +frontend` | `task project:P +agent +frontend export` |
| `ask list started` | `task project:P +agent start.any: export` |
| `ask list limit:3` | show only first 3 results |
| `ask list +READY limit:1` | next ready task |

## Data Retrieval: `task export`

All read operations use `task <filter> export` which returns a JSON array. This avoids fragile text parsing. Write operations (`add`, `done`, `start`, `stop`, `annotate`, `modify`) call Taskwarrior directly and capture stdout to extract the created UUID.

### TaskExport struct

```go
type TaskExport struct {
    UUID        string   `json:"uuid"`
    Description string   `json:"description"`
    Status      string   `json:"status"`
    Priority    string   `json:"priority"`
    Tags        []string `json:"tags"`
    Urgency     float64  `json:"urgency"`
    Depends     []string `json:"depends"`
    Annotations []struct {
        Description string `json:"description"`
        Entry       string `json:"entry"`
    } `json:"annotations"`
}
```

## Output Formatting

- **`list` / `urgency`**: Compact table — `UUID | Priority | Status | Tags | Description | Urgency`. No numeric ID column.
- **`info`**: UUID, description, status, priority, tags, annotations, dependencies (as UUIDs), urgency.
- **`add`**: Print only the UUID of the created task (parse from Taskwarrior stdout).
- **All other write commands**: Print success/failure + UUID. Suppress Taskwarrior decorative output.

## ID Rejection

If an argument looks like a bare numeric ID where a UUID is expected, reject with: `"use UUID, not numeric task ID"`.

## Package Layout

```
cmd/ask/main.go             — parse subcommand, dispatch to askcli
internal/askcli/             — NEW package
  ├── dispatch.go            — subcommand router (switch args[0])
  ├── taskexec.go            — wraps Taskwarrior execution (binary lookup, repo detection, run)
  ├── taskexport.go          — TaskExport struct + JSON parse helper
  ├── formatter.go           — shared UUID-only table/info formatting
  ├── command_add.go         — add logic + UUID extraction from stdout
  ├── command_list.go        — list via export + reformat
  ├── command_info.go        — info via export + field filter
  ├── command_annotate.go    — annotate
  ├── command_start.go       — start
  ├── command_stop.go        — stop
  ├── command_done.go        — done
  ├── command_priority.go    — set priority
  ├── command_tag.go         — add/remove tags
  ├── command_dep.go         — dep add/rm/list
  ├── command_urgency.go     — urgency-sorted list
  ├── command_delete.go      — delete task
  └── command_export.go      — raw JSON export
```

Each `command_*.go` file gets a corresponding `command_*_test.go`.

## Changes to Existing Code

- **`cmd/ask/main.go`** — stops calling `taskproxy.Runner.Run` directly; delegates to `askcli.Dispatch()`.
- **`internal/taskproxy/`** — reused by `askcli/taskexec.go` for binary lookup (`findTaskBinary`) and repo root detection (`detectRepoRoot`). The `Runner.Run` pass-through method becomes unused and can be removed.

## Task Breakdown

1. Scaffold `internal/askcli/` — dispatch, taskexec, taskexport, formatter
2. Implement `ask add` (UUID extraction from Taskwarrior stdout)
3. Implement `ask list` (export → UUID-only table)
4. Implement `ask info <uuid>` (export → filtered fields)
5. Implement `ask annotate <uuid> "note"`
6. Implement `ask start <uuid>` / `ask stop <uuid>`
7. Implement `ask done <uuid>`
8. Implement `ask priority <uuid> <P>`
9. Implement `ask tag <uuid> +/-tag`
10. Implement `ask dep add/rm/list`
11. Implement `ask urgency`
12. Implement `ask delete <uuid>`
13. Implement `ask export` (raw JSON)
14. Add filter/sort/limit support to `ask list` (+READY, +BLOCKED, +tag, started, limit:N)
15. Wire `cmd/ask/main.go` to `askcli.Dispatch`, remove old pass-through
16. Update docs and README