GitHub: clankercode/pi-tasks · npm: @clanker-code/pi-tasks
A pi extension that brings Claude Code-style task tracking and coordination to pi. Track multi-step work with structured tasks, dependency management, and a persistent visual widget.
This is a fork of @tintinweb/pi-tasks. We treat it as a pseudo-fork: upstream changes are reviewed and cherry-picked when possible, otherwise independently reimplemented to fit this fork's direction.
Status: Early release.
pi-tasks2.mp4
- 11 LLM-callable tools —
TaskCreate,TaskList,TaskGet,TaskUpdate,TaskOutput,TaskStop,TaskExecute(matching Claude Code's exact tool specs and descriptions), plus two default-enabled streaming batch toolsTaskCreateBatchandTaskUpdateBatchthat render progress live (each item appears one-by-one) for multi-task create/update/delete. Disable the batch tools via.pi/tasks-config.json: { "batchTools": false }orPI_TASKS_BATCH_TOOLS=0. - T-prefixed task ID parsing — tools accept either bare numeric IDs (
"1") or human-facing references ("T1","T2") intaskId,task_id,task_ids,addBlocks, andaddBlockedBy. IDs are always stored and rendered consistently as bare numbers with aTprefix in UI text. - Live streaming tool rows — when the model emits several task calls in one turn, each call yields a render frame before resolving, so the compact inline block and the task widget grow item-by-item (
⏣ TaskCreate · 1→… · 2→ …) instead of painting all at once. Per-call cadence is tunable viaPI_TASKS_STREAM_YIELD_MS(default 16; set 0 for instant). - Persistent widget — live task list above the editor with
✔/◼/◻status icons, task numbers (T1,T2, …), strikethrough for completed tasks, a dim separator line underneath, and star spinner (✳✽) for active tasks with elapsed time and token counts. The spinner freezes while the owning agent/session is idle and animates only while active. - System-reminder injection — periodic
<system-reminder>nudges injected into the upcoming LLM request (via thecontexthook, transient and never persisted) when task tools haven't been used recently (matches Claude Code's behavior exactly) - Prompt guidelines — workflow contract encoded in tool descriptions, nudging the LLM at the point of tool use
- Dependency management — bidirectional
blocks/blockedByrelationships with warnings for cycles, self-deps, and dangling references - Shared task lists — multiple pi sessions can share a file-backed task list for agent team coordination
- File locking — concurrent access is safe when multiple sessions share a task list
- Background process tracking — track spawned processes with output buffering, blocking wait, and graceful stop
- Subagent integration — tasks with
agentTypecan be executed as subagents viaTaskExecute(requires @tintinweb/pi-subagents). Auto-cascade mode flows through the task DAG automatically when enabled.
This fork intentionally diverges from upstream in the following ways. The README is kept up to date as part of every release.
| Feature | Status | Notes |
|---|---|---|
| Fork package scope | ✅ shipped | Package metadata, install docs, image/video URLs, and support links use @clanker-code/pi-tasks / clankercode/pi-tasks. |
| Compact task-tool TUI renderers | ✅ shipped | Task tool calls render as compact branded rows; same-tool calls in one turn join into a connected block with persisted fallback details. |
| Default-enabled streaming batch tools | ✅ shipped | TaskCreateBatch and TaskUpdateBatch stream item-by-item progress by default; config/env can disable them. |
| Per-call streaming frames | ✅ shipped | Separate task tool calls yield one frame at a time so the TUI visibly grows during same-response batches. |
| Widget ordering and overflow | ✅ shipped | Status-aware ordering/overflow keeps active/open work visible and defaults maxVisible to 8. |
| Live pending task rows | ✅ shipped | Pending task operations can appear before their tool call fully resolves. |
| T-prefixed task ID parsing | ✅ shipped | taskId: "T1" is normalized to "1" everywhere; avoids confusing "TT1 not found" errors and GitHub #123 ambiguity. |
| Persistent widget separator | ✅ shipped | Adds a dim horizontal rule under the visible task widget so it is visually separated from the editor/transcript. |
| Activity-synced task spinner | ✅ shipped | Active task spinners animate only while the owning agent/session is active; they freeze while idle instead of implying work is still happening. |
| Fork maintenance docs | ✅ shipped | AGENTS.md, CLAUDE.md, and RELEASE.md document pseudo-fork ownership, release checks, and upstream sync practice. |
pi install npm:@clanker-code/pi-tasksOr load directly for development:
pi -e ./src/index.tsThe extension renders a persistent widget above the editor:
● 4 tasks (1 in progress, 2 open, 1 done)
✳ T2 Acquiring plutonium… (2m 49s · ↑ 4.1k ↓ 1.2k)
◻ T3 Install flux capacitor in DeLorean › blocked by T1
◻ T4 Test time travel at 88 mph › blocked by T2, T3
✔ T1 Design the flux capacitor
| Icon | Meaning |
|---|---|
✔ |
Completed (strikethrough + dim) |
◼ |
In-progress (not actively executing) |
◻ |
Pending |
✳/✽ |
Animated star spinner — actively executing task (shows activeForm text, elapsed time, token counts) |
How tasks are sorted and how many are shown can be configured via /tasks → Settings (saved to .pi/tasks-config.json).
| Setting | Values | Default | Behaviour |
|---|---|---|---|
sortOrder |
id / status / recent / oldest |
status |
id = creation order; status groups in-progress → pending → completed; recent/oldest = by last-updated time |
maxVisible |
5–100 |
8 |
Caps how many task lines the widget shows (ignored when showAll is on) |
showAll |
true / false |
false |
When true, every task is shown regardless of maxVisible |
hiddenAt |
bottom / top |
bottom |
When the list overflows maxVisible, where the … and N more collapse happens. top pairs well with sortOrder: status to keep active work visible and fold completed tasks away |
Create a structured task. Used proactively for complex multi-step work.
| Parameter | Type | Required | Description |
|---|---|---|---|
subject |
string | yes | Brief imperative title |
description |
string | yes | Detailed context and acceptance criteria |
activeForm |
string | no | Present continuous form for spinner (e.g., "Running tests") |
agentType |
string | no | Agent type for subagent execution (e.g., "general-purpose", "Explore") |
metadata |
object | no | Arbitrary key-value pairs |
→ Task T1 created successfully: Fix authentication bug
List all tasks with status, owner, and blocked-by info.
T1 [pending] Fix authentication bug
T2 [in_progress] Write unit tests (agent-1)
T3 [pending] Update docs [blocked by T1, T2]
Sort order: pending first, then in-progress, then completed (each group by ID).
Get full details for a specific task.
Task T2: Write unit tests
Status: in_progress
Owner: agent-1
Description: Add tests for the auth module
Blocked by: T1
Blocks: T3
Shows owner (if set) and open (non-completed) dependency edges. Non-empty metadata is displayed as JSON.
Update task fields, status, metadata, and dependencies.
| Parameter | Type | Description |
|---|---|---|
taskId |
string | Task ID (required) |
status |
pending / in_progress / completed / deleted |
New status |
subject |
string | New title |
description |
string | New description |
activeForm |
string | Spinner text |
owner |
string | Agent name |
metadata |
object | Shallow merge (null values delete keys) |
addBlocks |
string[] | Task IDs this task blocks |
addBlockedBy |
string[] | Task IDs that block this task |
→ Updated task T1 status
→ Updated task T2 owner, status
→ Updated task T3 blocks
→ Updated task T3 blocks (warning: cycle: T3 and T1 block each other)
→ Updated task T1 deleted
Setting status: "deleted" permanently removes the task.
Dependencies are bidirectional: addBlocks: ["3"] on task 1 also adds blockedBy: ["1"] to task 3.
Retrieve output from a background task process.
| Parameter | Type | Default | Description |
|---|---|---|---|
task_id |
string | — | Task ID or agent ID (required) |
block |
boolean | true |
Wait for completion |
timeout |
number | 30000 |
Max wait time in ms (max 600000) |
Both task IDs and agent IDs (including partial prefixes) are accepted — agent IDs are resolved via the internal agentTaskMap.
Stop a running background task process. Sends SIGTERM, waits 5 seconds, then SIGKILL. For subagent tasks, sends a stop RPC.
| Parameter | Type | Description |
|---|---|---|
task_id |
string | Task ID or agent ID to stop |
Execute one or more tasks as background subagents. Requires @tintinweb/pi-subagents.
| Parameter | Type | Description |
|---|---|---|
task_ids |
string[] | Task IDs to execute (required) |
additional_context |
string | Extra context appended to each agent's prompt |
model |
string | Model override (e.g., "sonnet", "haiku") |
max_turns |
number | Max turns per agent |
Tasks must be pending, have agentType set, and all blockedBy dependencies completed. Each task spawns as an independent background subagent.
With auto-cascade enabled (via /tasks → Settings), completed tasks automatically trigger execution of their unblocked dependents — flowing through the DAG like a build system. Each cascaded agent receives its prerequisites' stored results in the prompt, so it can build directly on what came before without re-fetching.
pending → in_progress → completed
→ deleted (permanently removed)
Tasks are created as pending. Mark in_progress before starting work, completed when done. deleted removes entirely — IDs never reset.
- Bidirectional edges:
addBlocks/addBlockedBymaintain both sides automatically - Dependency warnings: cycles, self-dependencies, and references to non-existent tasks are stored but produce warnings in the tool response
- Display-time filtering:
TaskListonly shows non-completed blockers in[blocked by ...] - Raw data preserved:
TaskGetshows ALL edges, including completed blockers - Cleanup on deletion: removing a task cleans up all edges pointing to it
Task storage is controlled by the taskScope setting (/tasks → Settings → Task storage):
| Mode | File | Behaviour |
|---|---|---|
memory |
(none) | In-memory only — tasks lost when session ends |
session (default) |
<cwd>/.pi/tasks/tasks-<sessionId>.json |
Per-session file — isolated between sessions, survives resume |
project |
<cwd>/.pi/tasks/tasks.json |
Shared across all sessions in the project |
On new session start, if all persisted tasks are completed they are auto-cleared for a clean slate. On session resume, all tasks (including completed) are shown so the user can review progress. Empty session files are automatically deleted when all tasks are cleared.
The autoClearCompleted setting controls automatic cleanup of completed tasks:
| Mode | Behaviour |
|---|---|
never |
Completed tasks stay visible until manually cleared via /tasks → Clear completed |
on_list_complete (default) |
Cleared after all tasks are done and a few idle turns pass |
on_task_complete |
Each completed task cleared individually after a few turns |
Both auto-clear modes use a turn-based delay for non-jarring UX — tasks linger briefly so you see the completion before they disappear.
Settings (taskScope, autoCascade, autoClearCompleted, plus the widget display settings sortOrder / maxVisible / showAll / hiddenAt) are saved to <cwd>/.pi/tasks-config.json.
| Variable | Value | Behaviour |
|---|---|---|
PI_TASKS |
off |
In-memory only (CI/automation) |
PI_TASKS |
sprint-1 |
Named shared list at ~/.pi/tasks/sprint-1.json |
PI_TASKS |
/abs/path/tasks.json |
Explicit absolute file path |
PI_TASKS |
./tasks.json |
Relative path resolved from cwd |
| (unset) | Uses taskScope setting (default: session) |
|
PI_TASKS_DEBUG |
1 |
Trace RPC communication (request/reply/timeout) and spawn errors to stderr |
Named and explicit paths use a file-locked store with stale-lock detection — safe for multiple pi sessions coordinating on the same task list.
CI example (.envrc):
export PI_TASKS=offShared team list (.envrc):
export PI_TASKS=my-projectInteractive menu:
Tasks
├─ View all tasks (4)
├─ Create task
├─ Clear completed (1)
├─ Clear all (4)
└─ Settings
- View all tasks — select a task to see details and take actions (start, complete, delete)
- Create task — input prompts for subject and description
- Clear completed — remove all completed tasks
- Clear all — remove all tasks regardless of status
- Settings — configure task storage, auto-cascade, auto-clear completed tasks, and widget display (sort order, max visible, show all, hidden position) — saved to
tasks-config.json
Cross-extension Communication with @tintinweb/pi-subagents
@clanker-code/pi-tasks communicates with @tintinweb/pi-subagents via pi's eventbus using a scoped request/reply RPC protocol. No shared global state — just events.
Load order doesn't matter. Two handshake paths ensure detection regardless of which extension loads first:
- Ping on init —
@clanker-code/pi-tasksemitssubagents:rpc:pingwith a uniquerequestIdand listens forsubagents:rpc:ping:reply:{requestId}. Ifpi-subagentsis already loaded, it replies immediately. - Ready broadcast —
pi-subagentsemitssubagents:readywhen it initializes. If@clanker-code/pi-tasksloaded first, it picks this up.
┌─────────────┐ ┌──────────────────┐
│ pi-tasks │ │ pi-subagents │
└──────┬──────┘ └────────┬─────────┘
│ │
│──── subagents:rpc:ping ───────────▶│
│◀─── subagents:rpc:ping:reply ──────│
│ │
│◀─── subagents:ready ───────────────│ (broadcast on init)
│ │
When TaskExecute runs, it sends a spawn RPC with a scoped reply channel:
pi-tasks pi-subagents
│ │
│── subagents:rpc:spawn ─────────────────▶│ { requestId, type, prompt, options }
│◀─ subagents:rpc:spawn:reply:{reqId} ───│ { id } (or { error })
│ │
The returned id is stored in an in-memory agentTaskMap (agentId → taskId) for O(1) completion lookup. A 30-second timeout rejects the Promise if no reply arrives.
pi-subagents emits lifecycle events that @clanker-code/pi-tasks listens to:
| Event | Payload | Action |
|---|---|---|
subagents:completed |
{ id, result? } |
Mark task completed, trigger auto-cascade if enabled |
subagents:failed |
{ id, error?, status } |
Revert task to pending, store error in metadata |
If pi-subagents is not installed, everything works except TaskExecute, which returns a friendly error message. All core task tools (create, list, get, update, dependencies, widget, system-reminder injection) function independently.
src/
├── index.ts # Extension entry: 7 tools + /tasks command + widget + subagent integration
├── types.ts # Task, TaskStatus, BackgroundProcess types
├── task-store.ts # File-backed store with CRUD, dependencies, locking
├── auto-clear.ts # Turn-based auto-clearing of completed tasks (AutoClearManager)
├── tasks-config.ts # Config persistence (taskScope, autoCascade, autoClearCompleted) → .pi/tasks-config.json
├── process-tracker.ts # Background process output buffering and stop
└── ui/
├── task-widget.ts # Persistent widget with status icons and spinner
└── settings-menu.ts # /tasks → Settings panel (SettingsList TUI component)
- Background Bash auto-task creation — Claude Code auto-creates tasks when
Bashruns withrun_in_background: true. Pi's bash tool currently lacks arun_in_backgroundparameter (onlycommand+timeout), so there's nothing to hook into. Once pi adds background execution support to its bash tool, we can use thetool_callevent to detect it and auto-create tasks viaTaskStore/ProcessTracker.
npm install
npm run typecheck # TypeScript validation
npm test # Run unit testsMIT — xertrov / clankercode
Forked from tintinweb/pi-tasks.
