Skip to content
 
 

Repository files navigation

@clanker-code/pi-tasks

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-tasks screenshot

pi-tasks2.mp4

Features

  • 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 tools TaskCreateBatch and TaskUpdateBatch that 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 } or PI_TASKS_BATCH_TOOLS=0.
  • T-prefixed task ID parsing — tools accept either bare numeric IDs ("1") or human-facing references ("T1", "T2") in taskId, task_id, task_ids, addBlocks, and addBlockedBy. IDs are always stored and rendered consistently as bare numbers with a T prefix 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 via PI_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 the context hook, 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/blockedBy relationships 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 agentType can be executed as subagents via TaskExecute (requires @tintinweb/pi-subagents). Auto-cascade mode flows through the task DAG automatically when enabled.

Fork divergences

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.

Install

pi install npm:@clanker-code/pi-tasks

Or load directly for development:

pi -e ./src/index.ts

Widget

The 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)

Widget display settings

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

Tools

TaskCreate

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

TaskList

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).

TaskGet

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.

TaskUpdate

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.

TaskOutput

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.

TaskStop

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

TaskExecute

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.

Task Lifecycle

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.

Dependency Management

  • Bidirectional edges: addBlocks/addBlockedBy maintain 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: TaskList only shows non-completed blockers in [blocked by ...]
  • Raw data preserved: TaskGet shows ALL edges, including completed blockers
  • Cleanup on deletion: removing a task cleans up all edges pointing to it

Task Storage

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.

Auto-clear completed tasks

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.

Override via environment variables

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=off

Shared team list (.envrc):

export PI_TASKS=my-project

/tasks Command

Interactive 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.

Presence Detection

Load order doesn't matter. Two handshake paths ensure detection regardless of which extension loads first:

  1. Ping on init — @clanker-code/pi-tasks emits subagents:rpc:ping with a unique requestId and listens for subagents:rpc:ping:reply:{requestId}. If pi-subagents is already loaded, it replies immediately.
  2. Ready broadcast — pi-subagents emits subagents:ready when it initializes. If @clanker-code/pi-tasks loaded first, it picks this up.
┌─────────────┐                    ┌──────────────────┐
│  pi-tasks   │                    │  pi-subagents    │
└──────┬──────┘                    └────────┬─────────┘
       │                                    │
       │──── subagents:rpc:ping ───────────▶│
       │◀─── subagents:rpc:ping:reply ──────│
       │                                    │
       │◀─── subagents:ready ───────────────│  (broadcast on init)
       │                                    │

Spawning Subagents

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.

Lifecycle Events

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

Standalone Mode

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.

Architecture

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)

Future Work

  • Background Bash auto-task creation — Claude Code auto-creates tasks when Bash runs with run_in_background: true. Pi's bash tool currently lacks a run_in_background parameter (only command + timeout), so there's nothing to hook into. Once pi adds background execution support to its bash tool, we can use the tool_call event to detect it and auto-create tasks via TaskStore/ProcessTracker.

Development

npm install
npm run typecheck   # TypeScript validation
npm test            # Run unit tests

License

MIT — xertrov / clankercode

Forked from tintinweb/pi-tasks.

About

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.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages