@getpipher/armory-todo 0.3.1 → 0.5.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 +33 -8
- package/assets/hero.svg +4 -4
- package/docs/superpowers/plans/2026-07-21-caps-release.md +1113 -0
- package/docs/superpowers/plans/2026-07-21-project-scope-management.md +1537 -0
- package/docs/superpowers/specs/2026-07-21-auto-prune-done-view-design.md +1 -1
- package/docs/superpowers/specs/2026-07-21-caps-release-design.md +222 -0
- package/docs/superpowers/specs/2026-07-21-project-scope-management-design.md +319 -0
- package/extensions/todo.ts +60 -3
- package/package.json +2 -2
- package/src/caps.ts +71 -0
- package/src/config.ts +10 -1
- package/src/health.ts +81 -7
- package/src/levenshtein.ts +22 -0
- package/src/panel-data.ts +32 -0
- package/src/panel.ts +100 -6
- package/src/paths.ts +5 -0
- package/src/projects.ts +91 -0
- package/src/registry.ts +175 -0
- package/src/todo-store.ts +73 -12
package/README.md
CHANGED
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
<img src="https://img.shields.io/badge/dependencies-0-9aa7a1" alt="no dependencies">
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
+
<p align="center">
|
|
19
|
+
<strong>lifecycle boxes</strong> · <strong>title + notes</strong> · <strong>auto-prune</strong> · <strong>interactive panel</strong> · <strong>health diagnostics</strong> · <strong>hard-prune gate</strong>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
18
22
|
---
|
|
19
23
|
|
|
20
24
|
## The problem
|
|
@@ -99,21 +103,40 @@ todos. It's gated three ways:
|
|
|
99
103
|
Everything else in armory-todo is reversible. `prune --hard` is the one
|
|
100
104
|
irreversible escape hatch, always user-confirmed.
|
|
101
105
|
|
|
102
|
-
##
|
|
106
|
+
## Project-scope management (v0.4.0)
|
|
107
|
+
|
|
108
|
+
A project registry (`~/.pi/agent/todo/projects.json`, lazy-synced on read) tracks known projects + an advisory per-project **`maxOpen`** cap slot. The `todo` tool gains two actions: **`projects`** (per-project scope overview — open/in_progress/parked/done counts + `maxOpen` + `OVER`/`?typo` markers + last-updated) and **`project_rename`** (rename or merge a project; rewrites live + archive + registry — the typo-cleanup path, e.g. `getpither` → `getpipher`).
|
|
109
|
+
|
|
110
|
+
`health` gains four per-project flags: **`PROJECT_OVER`** (open > a project's `maxOpen` slot), **`PROJECT_LARGE`** (open > `health.perProjectDefaultMax`, default 8 — fires out-of-the-box, no per-project config needed), **`PROJECT_STALE`** (project untouched > `activeStaleDays`), **`PROJECT_TYPO`** (1-todo project with a near-named sibling, Levenshtein ≤ 2). The `/todo health` report + the `todo` `health` action both show a `projects:` section.
|
|
111
|
+
|
|
112
|
+
The interactive `/todo` panel gains a 6th tab — **Projects** — listing the overview rows + a `(no project)` summary, with an action submenu per project: **Rename / merge** (inline input), **Set maxOpen** (number or `clear`), **Filter active to project** (jump to the Active tab scoped). A thin `/todo projects` slash mirrors the overview as text.
|
|
113
|
+
|
|
114
|
+
**Advisory in v0.4.0 → enforced in v0.5.0** — `maxOpen` now blocks `add` (and project-move) when a project is at its cap. See the [Caps enforcement (v0.5.0)](#caps-enforcement-v050) section below.
|
|
115
|
+
|
|
116
|
+
## Caps enforcement (v0.5.0)
|
|
117
|
+
|
|
118
|
+
Three caps keep the store (and its auto-injected prompt block) from bloating silently — the forcing-function half of [issue #1](https://github.com/getpither/armory-todo/issues/1):
|
|
119
|
+
|
|
120
|
+
1. **Count cap (per-project `maxOpen`, enforced).** A project's `maxOpen` slot (set via the Projects tab → Set maxOpen, or `setProjectMaxOpen`) **blocks `add`** when the project is at its cap, and **blocks a project-move** of an `open`/`in_progress` todo into a capped project. The cap is on the `open` count (matches the `PROJECT_OVER` health flag); `in_progress` doesn't count. Un-park (`parked→open`) is intentionally **not** blocked — reactivating deferred work isn't adding new work. The block message tells you how to raise/clear the cap. `maxOpen: null` (default) = uncapped.
|
|
121
|
+
|
|
122
|
+
2. **Notes cap (`health.maxNotesBytes`, default 8192 bytes, enforced).** Oversize notes are rejected at `add`/`update` (only when `notes` is being written — a title edit on a grandfathered oversize note isn't trapped). Byte-length, not char-length (notes can hold Unicode). Existing oversize notes are grandfathered; `health` surfaces the worst offender via the `NOTES_OVER` flag + an actionable `todo update <id> notes:…` suggestion.
|
|
123
|
+
|
|
124
|
+
3. **Over-cap injection truncation.** When actionable > `health.activeMaxOpen` (default 15), the auto-injected `## Open TODOs (N)` block collapses to a ~4-line summary (total + project span + over-budget projects + a `todo list` pointer) instead of the row list. Under the cap → the familiar row list. `activeMaxOpen` itself stays **advisory** (it drives the `ACTIVE_LARGE` flag and the truncation trigger; it is not a hard global block).
|
|
103
125
|
|
|
104
|
-
|
|
126
|
+
**Backwards-compat:** zero migration (store v3, config v1, registry v1 unchanged in shape). Oversize notes grandfathered. The `maxOpen` advisory→enforced graduation is a documented behavior change for any v0.4.0 user who set a slot (the block message tells them how to raise/clear).
|
|
105
127
|
|
|
106
128
|
## Interactive panel (SPEC-3)
|
|
107
129
|
|
|
108
130
|
Run `/todo` (no arg) in a TUI session to open the interactive triage panel:
|
|
109
131
|
|
|
110
|
-
- **Box tabs** (Tab / Shift+Tab): Active · Parked · **Done** · Archive · Config
|
|
132
|
+
- **Box tabs** (Tab / Shift+Tab): Active · Parked · **Done** · Archive · **Projects** · Config
|
|
111
133
|
- **Filter input**: type to search by text (live filter)
|
|
112
134
|
- **SelectList**: arrow keys navigate, Enter selects
|
|
113
135
|
- **Action submenu** (on Enter): View detail / Complete / Park / Re-activate / Restore / Edit title / Delete
|
|
114
136
|
- **Done tab** (v0.3.1): all finished work (`status: done`) unified across live + archive, location-tagged (`[live Nd]` / `[archived YYYY-MM-DD]`), filterable; Enter → View detail, or Restore-from-archive. Excludes `cancelled` (that's in the Archive tab).
|
|
115
137
|
- **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
|
|
116
138
|
- **Archive box**: summary-first (counts by project + month) → Enter on a bucket to drill down
|
|
139
|
+
- **Projects tab** (v0.4.0): per-project scope overview (open/in_progress/parked/done counts + `maxOpen` + `OVER`/`?typo` markers) + a `(no project)` summary row; Enter on a project → action submenu: Rename / merge · Set maxOpen · Filter active to project.
|
|
117
140
|
- **Config box**: SettingsList with prune ages + health thresholds — edit live, persists to `todo.config.json`
|
|
118
141
|
- **Escape**: exit the panel
|
|
119
142
|
|
|
@@ -134,7 +157,7 @@ Typed subcommands (`/todo park <id>`, `/todo prune`, etc.) all still work alongs
|
|
|
134
157
|
/todo list open + in-progress TODOs
|
|
135
158
|
/todo all include parked/done/cancelled
|
|
136
159
|
/todo add <title> quick add (priority: med; notes via the todo tool)
|
|
137
|
-
/todo finished
|
|
160
|
+
/todo finished list all done todos (live + archived, recent first)
|
|
138
161
|
/todo done <id> mark done
|
|
139
162
|
/todo rm <id> cancel (tombstone)
|
|
140
163
|
/todo park <id> defer (parked — not injected, recoverable)
|
|
@@ -168,13 +191,15 @@ Each TODO carries `id, title (≤120 chars), notes (any length), project, tags,
|
|
|
168
191
|
|
|
169
192
|
## How it works
|
|
170
193
|
|
|
171
|
-
- **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.
|
|
194
|
+
- **Disk store** — `~/.pi/agent/todo/` folder: `todo.json` (live: active + parked), `todo-archive.json` (sealed: done + cancelled), `todo.config.json` (prune ages + health thresholds), `projects.json` (project registry: canonical names + advisory `maxOpen` slots, v0.4.0). Atomic `0600` writes, corrupt-file auto-recovery, `version: 3` store 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.
|
|
172
195
|
- **`todo` tool** — model CRUD + lifecycle (above).
|
|
173
196
|
- **`/todo` command** — human triage (above).
|
|
174
|
-
- **Auto-inject** — on every `before_agent_start`, a compact `## Open TODOs (N)` block (titles + ids,
|
|
197
|
+
- **Auto-inject** — on every `before_agent_start`, a compact `## Open TODOs (N)` block (titles + ids, sorted by priority) is appended to the system prompt, so the agent starts every turn already aware of pending work. The block is **cap-aware** (v0.5.0): under `health.activeMaxOpen` (default 15) it lists the rows; **over** the cap it collapses to a lean summary (counts + over-budget projects + a `todo list` pointer) so the prompt stays bounded when the store bloats. Only `open` + `in_progress` are injected — `parked` and archived todos are excluded (the lifecycle-box boundary). Mutations refresh it on the next turn.
|
|
175
198
|
- **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.
|
|
176
199
|
|
|
177
200
|
Full design + decisions:
|
|
201
|
+
- v0.4.0 (project-scope management): [`docs/superpowers/specs/2026-07-21-project-scope-management-design.md`](docs/superpowers/specs/2026-07-21-project-scope-management-design.md)
|
|
202
|
+
- v0.3.1 (auto-prune + unified Done view): [`docs/superpowers/specs/2026-07-21-auto-prune-done-view-design.md`](docs/superpowers/specs/2026-07-21-auto-prune-done-view-design.md)
|
|
178
203
|
- 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)
|
|
179
204
|
- 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)
|
|
180
205
|
- Original v0.1.0 spec: [`docs/todo-SPEC.md`](docs/todo-SPEC.md)
|
|
@@ -185,12 +210,12 @@ Full design + decisions:
|
|
|
185
210
|
|---|---|---|
|
|
186
211
|
| `TODO_DIR` | `~/.pi/agent/todo/` | override the store folder (tests / multiple profiles) |
|
|
187
212
|
|
|
188
|
-
Run the store tests: `npm test` (
|
|
213
|
+
Run the store tests: `npm test` (315/315 across 11 suites).
|
|
189
214
|
|
|
190
215
|
## Known issues
|
|
191
216
|
|
|
192
217
|
- **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.
|
|
193
|
-
- **
|
|
218
|
+
- **Caps enforcement shipped in v0.5.0.** Per-project `maxOpen` blocks `add`/move; `health.maxNotesBytes` (default 8KB) rejects oversize notes at write; the auto-injected block collapses to a lean summary over `activeMaxOpen`. See [Caps enforcement (v0.5.0)](#caps-enforcement-v050) above.
|
|
194
219
|
|
|
195
220
|
## Security
|
|
196
221
|
|
package/assets/hero.svg
CHANGED
|
@@ -103,11 +103,11 @@
|
|
|
103
103
|
<path d="M14,8 L14,18 M11,8 L17,8 M11,18 L17,18" fill="none" stroke="#8a9791" stroke-width="1.4" stroke-linecap="round"/>
|
|
104
104
|
<text x="27" y="17.5" fill="#c2cfc9" letter-spacing="0.4">MIT</text>
|
|
105
105
|
</g>
|
|
106
|
-
<!-- v0.1
|
|
106
|
+
<!-- v0.3.1 -->
|
|
107
107
|
<g transform="translate(234, 0)">
|
|
108
108
|
<rect x="0" y="0" rx="13" ry="13" width="98" height="26" fill="#10171a" stroke="#3a4a44" stroke-width="1"/>
|
|
109
109
|
<path d="M14,9 L14,17 M14,9 L11,12 M14,9 L17,12" fill="none" stroke="#8a9791" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
110
|
-
<text x="27" y="17.5" fill="#c2cfc9" letter-spacing="0.4">v0.1
|
|
110
|
+
<text x="27" y="17.5" fill="#c2cfc9" letter-spacing="0.4">v0.3.1</text>
|
|
111
111
|
</g>
|
|
112
112
|
<!-- no dependencies -->
|
|
113
113
|
<g transform="translate(342, 0)">
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
<rect x="0" y="0" rx="6" ry="6" width="40" height="20" fill="#3a2a0e" stroke="#fbbf24" stroke-width="1"/>
|
|
153
153
|
<text x="20" y="14.5" font-size="11" font-weight="700" fill="#fcd34d" text-anchor="middle" letter-spacing="0.4">high</text>
|
|
154
154
|
<text x="52" y="14.5" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="12" fill="#b8c4be">[td-…m6pjxhj]</text>
|
|
155
|
-
<text x="184" y="14.5" font-size="13" fill="#f4f7f4">⏵ Decouple global rules → AGENTS.md
|
|
155
|
+
<text x="184" y="14.5" font-size="13" fill="#f4f7f4">⏵ Decouple global rules → AGENTS.md •</text>
|
|
156
156
|
<rect x="408" y="2" rx="8" ry="8" width="44" height="16" fill="#13202b" stroke="#2b4250"/>
|
|
157
157
|
<text x="430" y="13.5" font-size="10.5" fill="#a8b5af" text-anchor="middle">(pi)</text>
|
|
158
158
|
</g>
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
<rect x="0" y="0" rx="6" ry="6" width="34" height="20" fill="#0e1f33" stroke="#60a5fa" stroke-width="1"/>
|
|
163
163
|
<text x="17" y="14.5" font-size="11" font-weight="700" fill="#93c5fd" text-anchor="middle" letter-spacing="0.4">med</text>
|
|
164
164
|
<text x="46" y="14.5" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="12" fill="#b8c4be">[td-…mzr0qs9]</text>
|
|
165
|
-
<text x="178" y="14.5" font-size="13" fill="#f4f7f4">Research browser-use vs Chrome MCP
|
|
165
|
+
<text x="178" y="14.5" font-size="13" fill="#f4f7f4">Research browser-use vs Chrome MCP •</text>
|
|
166
166
|
</g>
|
|
167
167
|
|
|
168
168
|
<!-- row 3: low -->
|