@getpipher/armory-todo 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,22 +41,100 @@ Then restart pi (or `/reload`). Or add to `~/.pi/agent/settings.json`:
41
41
  { "packages": ["npm:@getpipher/armory-todo"] }
42
42
  ```
43
43
 
44
+ ## Lifecycle boxes (v0.2.0)
45
+
46
+ TODOs live in one of three states, only one of which hits the agent context:
47
+
48
+ | Box | Status(es) | Auto-injected? | Recoverable? |
49
+ |---|---|:---:|:---:|
50
+ | **Active** | `open`, `in_progress` | ✅ Yes (capped 15) | n/a |
51
+ | **Parked** | `parked` | ❌ No | ✅ `update --status open` |
52
+ | **Archive** | `done`, `cancelled` | ❌ No | ✅ `restore <id>` |
53
+
54
+ **Pruning:** `prune` (default: done/cancelled older than 7 days) moves finished
55
+ todos from the live file to `todo-archive.json` — nothing is deleted. `prune --all`
56
+ ignores age. `restore <id>` brings an archived todo back as `open`.
57
+
58
+ The only irreversible action is `prune --hard` (hard-prune) — it requires an
59
+ explicit `confirm: true` and is always user-confirmed. See **Self-awareness**
60
+ below.
61
+
62
+ **Storage layout:**
63
+
64
+ ```
65
+ ~/.pi/agent/todo/
66
+ todo.json # active + parked
67
+ todo-archive.json # done + cancelled (sealed history)
68
+ todo.config.json # prune ages + health thresholds
69
+ ```
70
+
71
+ A v1 single-file store at `~/.pi/agent/todo.json` is migrated automatically on
72
+ first load after upgrade.
73
+
74
+ ## Self-awareness: health + hard-prune
75
+
76
+ **`health`** reports bloat across all three boxes — counts, stale items, and
77
+ actionable suggestions (e.g. "archive: 41 items older than 180d → consider
78
+ `prune --hard --box archive --older-than 180 --confirm`"). On `session_start`,
79
+ if any bloat flags are detected, the startup notify appends a `⚠ N bloat
80
+ signals` nudge.
81
+
82
+ **`prune --hard`** is the **only irreversible action** — it permanently deletes
83
+ todos. It's gated three ways:
84
+ 1. **Tool-level:** `confirm: true` is required in the tool call; without it the
85
+ action refuses with a clear message.
86
+ 2. **Prompt-level:** the agent is instructed to always run `health` first,
87
+ surface the report + the exact proposed command, and wait for an explicit
88
+ user "yes" before passing `confirm: true`.
89
+ 3. **Slash-level:** `/todo prune --hard` prompts an interactive `ctx.ui.confirm`
90
+ yes/no dialog before executing.
91
+
92
+ Everything else in armory-todo is reversible. `prune --hard` is the one
93
+ irreversible escape hatch, always user-confirmed.
94
+
95
+ ## Title + notes (v0.3.0)
96
+
97
+ The single `text` field is split into **`title`** (≤120 chars, one-line summary — the only thing injected into the prompt or shown in compact lists) and **`notes`** (any length, the running detail/log — never auto-injected). A hard cap rejects `title` >120 chars at `add`/`update` so the junk-drawer pattern can't re-form. `list` shows titles + a `•` marker when notes exist; `get <id>` reads a todo's full notes before acting on it. v2 `text`-only stores migrate on first load (curated for the 2 known todos + a first-line fallback).
98
+
99
+ ## Interactive panel (SPEC-3)
100
+
101
+ Run `/todo` (no arg) in a TUI session to open the interactive triage panel:
102
+
103
+ - **Box tabs** (Tab / Shift+Tab): Active · Parked · Archive · Config
104
+ - **Filter input**: type to search by text (live filter)
105
+ - **SelectList**: arrow keys navigate, Enter selects
106
+ - **Action submenu** (on Enter): View detail / Complete / Park / Re-activate / Restore / Edit title / Delete
107
+ - **Detail view** (View detail, or Enter on a row): renders the title + full `notes` read-only, with a footer hint on editing notes via the `todo` tool
108
+ - **Archive box**: summary-first (counts by project + month) → Enter on a bucket to drill down
109
+ - **Config box**: SettingsList with prune ages + health thresholds — edit live, persists to `todo.config.json`
110
+ - **Escape**: exit the panel
111
+
112
+ Typed subcommands (`/todo park <id>`, `/todo prune`, etc.) all still work alongside the panel. Non-TUI sessions (`pi -p`, RPC) fall back to the text list.
113
+
44
114
  ## Usage
45
115
 
46
116
  **Say it naturally** — the model calls the `todo` tool:
47
117
 
48
118
  > “put this in our TODO: decouple global rules into AGENTS.md”
49
119
  > “show me the TODO” → “mark td-… done”
120
+ > “park td-… for now” → later: “restore td-…”
121
+ > “prune the done todos”
50
122
 
51
123
  **Slash command** for quick human triage:
52
124
 
53
125
  ```
54
126
  /todo list open + in-progress TODOs
55
- /todo all include done/cancelled
56
- /todo add <text> quick add (priority: med)
127
+ /todo all include parked/done/cancelled
128
+ /todo add <title> quick add (priority: med; notes via the todo tool)
57
129
  /todo done <id> mark done
58
130
  /todo rm <id> cancel (tombstone)
59
- /todo clean clear all done
131
+ /todo park <id> defer (parked — not injected, recoverable)
132
+ /todo restore <id> bring an archived todo back as open
133
+ /todo prune [--all] move done/cancelled to archive (reversible)
134
+ /todo prune --hard permanent deletion (interactive confirm prompt)
135
+ /todo archive [filter] archive summary, or filtered slice (project:X / text:Y)
136
+ /todo health bloat report across all boxes + flags + suggestions
137
+ /todo clean clear all done (deprecated — use prune)
60
138
  /todo path show the store file path
61
139
  ```
62
140
 
@@ -64,31 +142,46 @@ Then restart pi (or `/reload`). Or add to `~/.pi/agent/settings.json`:
64
142
 
65
143
  | action | params | effect |
66
144
  |---|---|---|
67
- | `list` | `statusFilter?`, `projectFilter?`, `tagFilter?` | matching TODOs (default: open + in_progress) |
68
- | `add` | `text`, `project?`, `tags?`, `priority?`, `source?` | create a TODO |
69
- | `update` | `id`, `text?`, `priority?`, `status?`, `project?`, `tags?` | edit a TODO |
145
+ | `list` | `statusFilter?`, `projectFilter?`, `tagFilter?`, `text?`, `since?`, `before?`, `limit?`, `page?`, `archived?` | matching TODOs (default: open + in_progress). `archived:true` queries the archive — bare call returns a summary, filters return paginated slices |
146
+ | `add` | `title`, `notes?`, `project?`, `tags?`, `priority?`, `source?` | create a TODO (`title` ≤120 chars; long detail goes in `notes`) |
147
+ | `get` | `id` | read a TODO's full record incl. `notes` |
148
+ | `update` | `id`, `title?`, `notes?`, `priority?`, `status?`, `project?`, `tags?` | edit a TODO (set `status: parked` to defer; `notes=""` clears) |
70
149
  | `complete` | `id` | mark done |
71
150
  | `delete` | `id` | cancel (tombstone) |
72
- | `clear` | `status?` (default `done`) | bulk-clear a status |
151
+ | `park` | `id` | defer (parked not injected) |
152
+ | `prune` | `ageDays?`, `all?` | move done/cancelled to archive (reversible via restore) |
153
+ | `restore` | `id` | bring an archived TODO back as open |
154
+ | `health` | (none) | bloat report across active/parked/archive + flags + suggestions |
155
+ | `prune` (hard) | `hard:true`, `confirm:true`, `box?`, `olderThan?`, `project?`, `tag?` | PERMANENT deletion — the only irreversible action |
156
+ | `clear` | `status?` (default `done`) | bulk-clear a status (deprecated — use prune) |
73
157
 
74
- Each TODO carries `id, text, project, tags, priority (low|med|high|critical), status (open|in_progress|done|cancelled), source, createdAt, updatedAt, closedAt`.
158
+ Each TODO carries `id, title (≤120 chars), notes (any length), project, tags, priority (low|med|high|critical), status (open|in_progress|parked|done|cancelled), source, createdAt, updatedAt, closedAt`. The auto-injected block + list/panel show `title` only; `notes` is read via `get` and never injected.
75
159
 
76
160
  ## How it works
77
161
 
78
- - **Disk store** — `~/.pi/agent/todo.json`, atomic `0600` writes, corrupt-file auto-recovery, `version: 1` schema. Not pi session entries, so it outlives any conversation.
79
- - **`todo` tool** — model CRUD (above).
162
+ - **Disk store** — `~/.pi/agent/todo/` folder: `todo.json` (live: active + parked), `todo-archive.json` (sealed: done + cancelled), `todo.config.json` (prune ages + health thresholds). Atomic `0600` writes, corrupt-file auto-recovery, `version: 3` schema (`title` ≤120 chars + `notes` any length; v2 `text`-only stores migrate to v3 on first load). Not pi session entries, so it outlives any conversation.
163
+ - **`todo` tool** — model CRUD + lifecycle (above).
80
164
  - **`/todo` command** — human triage (above).
81
- - **Auto-inject** — on every `before_agent_start`, a compact `## Open TODOs (N)` block (titles + ids, capped at 15, sorted by priority) is appended to the system prompt, so the agent starts every turn already aware of pending work. Mutations refresh it on the next turn.
165
+ - **Auto-inject** — on every `before_agent_start`, a compact `## Open TODOs (N)` block (titles + ids, capped at 15, sorted by priority) is appended to the system prompt, so the agent starts every turn already aware of pending work. Only `open` + `in_progress` are injected — `parked` and archived todos are excluded (the lifecycle-box boundary). Mutations refresh it on the next turn.
166
+ - **Archive query** — `list` with `archived:true` is summary-first (counts by project + month) then filtered/paginated on demand, so a large archive never bloats a single query.
82
167
 
83
- Full design + decisions: [`docs/todo-SPEC.md`](docs/todo-SPEC.md).
168
+ Full design + decisions:
169
+ - v0.3.0 (title + notes split): [`docs/superpowers/specs/2026-07-21-title-notes-split-design.md`](docs/superpowers/specs/2026-07-21-title-notes-split-design.md)
170
+ - v0.2.0 (lifecycle boxes + prune + health): [`docs/superpowers/specs/2026-07-20-lifecycle-boxes-prune-design.md`](docs/superpowers/specs/2026-07-20-lifecycle-boxes-prune-design.md)
171
+ - Original v0.1.0 spec: [`docs/todo-SPEC.md`](docs/todo-SPEC.md)
84
172
 
85
173
  ## Configuration
86
174
 
87
175
  | env var | default | purpose |
88
176
  |---|---|---|
89
- | `TODO_STORE_PATH` | `~/.pi/agent/todo.json` | override the store location (tests / multiple profiles) |
177
+ | `TODO_DIR` | `~/.pi/agent/todo/` | override the store folder (tests / multiple profiles) |
178
+
179
+ Run the store tests: `npm test` (220/220 across 8 suites).
180
+
181
+ ## Known issues
90
182
 
91
- Run the store tests: `npm test` (24/24).
183
+ - **No in-panel multi-line `notes` editing.** The panel's inline Edit is single-line (`Input`) for `title` only; `ctx.ui.editor()` from inside `ctx.ui.custom()` triggers a nested-UI bug (`/todo` won't reopen). `notes` is model-managed via the `todo` tool (`action: update, id, notes`). When a safe `ctx.ui.editor()`-from-`custom()` pattern lands in pi-tui, in-panel notes editing is a clean follow-up.
184
+ - **Preventive caps-on-add** (notes length cap + project registry) are deferred to Workstream C (v0.4.0). Until then, `health` reports notes-bytes as a read-only diagnostic.
92
185
 
93
186
  ## Security
94
187