@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 +107 -14
- package/docs/superpowers/plans/2026-07-20-spec-1-store-layer.md +1691 -0
- package/docs/superpowers/plans/2026-07-20-spec-2-health-hard-prune.md +762 -0
- package/docs/superpowers/plans/2026-07-20-spec-3-interactive-panel.md +651 -0
- package/docs/superpowers/plans/2026-07-21-title-notes-split.md +1586 -0
- package/docs/superpowers/specs/2026-07-20-lifecycle-boxes-prune-design.md +323 -0
- package/docs/superpowers/specs/2026-07-21-title-notes-split-design.md +411 -0
- package/docs/todo-SPEC.md +5 -0
- package/extensions/todo.ts +273 -36
- package/package.json +2 -2
- package/src/archive.ts +214 -0
- package/src/config.ts +101 -0
- package/src/hard-prune.ts +89 -0
- package/src/health.ts +97 -0
- package/src/migrate.ts +154 -0
- package/src/panel-data.ts +63 -0
- package/src/panel.ts +382 -0
- package/src/paths.ts +36 -0
- package/src/todo-store.ts +86 -37
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 <
|
|
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
|
|
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` | `
|
|
69
|
-
| `
|
|
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
|
-
| `
|
|
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,
|
|
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
|
|
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:
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
|