This guide is for contributors working on Taskplane itself.
- Node.js 22+
- Git
- pi
git clone https://github.com/HenryLach/taskplane.git
cd taskplaneInstall extension test/dev dependencies:
cd extensions
npm install
cd ..pi -e extensions/task-orchestrator.tsCLI entrypoint:
bin/taskplane.mjs
Typical manual checks:
node bin/taskplane.mjs help
node bin/taskplane.mjs version
node bin/taskplane.mjs init --dry-run
node bin/taskplane.mjs doctorDashboard files:
dashboard/server.cjsdashboard/public/index.htmldashboard/public/app.jsdashboard/public/style.css
Launch via CLI:
taskplane dashboardOr direct server invocation:
node dashboard/server.cjs --root . --port 8099 --no-open- Skills:
skills/ - Templates:
templates/
Template changes affect taskplane init output and must be tested by running init in a scratch repo.
Tests use the Node.js native test runner (node:test) exclusively — no vitest or vite dependency.
cd extensions
# Full suite (unit + integration)
npm test
# Fast suite (unit only, skip integration)
npm run test:fast
# Single file
node --experimental-strip-types --experimental-test-module-mocks --no-warnings --import ./tests/loader.mjs --test tests/some-file.test.tsKey flags:
--experimental-strip-types— run TypeScript directly without transpilation--experimental-test-module-mocks— enablemock.module()for ESM module mocking--import ./tests/loader.mjs— register module resolution hooks
The custom loader (tests/loader.mjs) redirects @mariozechner/pi-coding-agent
and @mariozechner/pi-tui to local mock stubs so tests don't need the real packages.
- Use
import { describe, it, mock, beforeEach, afterEach } from "node:test"for test structure - Use
import { expect } from "./expect.ts"for a familiar matcher API (legacy Vitest-style surface backed bynode:assert) - Use
mock.fn()for function mocks,mock.method(obj, key)for spies - Use
mock.module("mod", { namedExports: {...} })for ESM module mocking (must be beforeawait import()of consumer) - Use
mock.timers.enable()/mock.timers.tick(ms)/mock.timers.reset()for fake timers
Three static checks are required CI gates — every PR must pass all three
before it can be merged. Each runs as a separate step in .github/workflows/ci.yml
and a failure blocks the merge:
npm run typecheck # TypeScript type-check (tsc --noEmit against extensions/tsconfig.ci.json)
npm run lint # Biome lint check
npm run format:check # Biome format check (non-zero exit on diff)Run these locally before pushing for PR — they are cheap (each runs in a few
seconds) and catch regressions before CI does. The rationale for the three-gate
baseline is documented in
docs/specifications/taskplane/code-quality-gates.md
(the spec that introduced these gates via TP-191/TP-192/TP-193/TP-194).
To auto-fix the lint/format gates locally:
npm run lint:fix # apply safe Biome lint autofixes
npm run format # rewrite files in-place to match the format checkThe typecheck gate has no auto-fix — type errors must be addressed by hand.
Taskplane uses Biome as both linter and formatter. The
formatter rules are pinned in biome.json and applied uniformly across the
codebase.
Available scripts (run from the repo root):
npm run lint # report lint issues (no fixes)
npm run lint:fix # apply safe lint autofixes
npm run format # format files in-place
npm run format:check # check formatting (CI-style; non-zero exit on diff)The repo ships a .git-blame-ignore-revs file at the root that lists
commits whose changes are purely mechanical — chiefly the one-shot Biome
formatter adoption commit (TP-193). Without this file, git blame would
bottom out on the formatter commit for nearly every line in the codebase
and hide the real authoring history.
Recommended one-time per-developer setup:
git config blame.ignoreRevsFile .git-blame-ignore-revsThis is recommended, not required. Without it, git blame still works
— it just attributes every formatter-touched line to the format-adoption
commit instead of the underlying author. The same .git-blame-ignore-revs
file is also picked up automatically by GitHub's web Blame view (no
client-side config needed there).
When you add a future bulk-mechanical commit (e.g., a one-shot codemod or
another formatter migration), append its full 40-character SHA + a
comment block describing what it did to .git-blame-ignore-revs.
- Edit extension/CLI/template code
- Run tests (
cd extensions && npm test) - Run pi with local extension flags
- Execute manual smoke flows:
/orch-plan all/orch alltaskplane doctor
mkdir ../tp-scratch && cd ../tp-scratch
git init
pi install -l npm:taskplane
npx taskplane init --preset full
piInside pi:
/orch-plan all
/orch all
/orch taskplane-tasks/EXAMPLE-001-hello-world/PROMPT.md
extensions/task-orchestrator.ts— orchestrator facade exportextensions/taskplane/discovery.ts— task discovery + dependency parsingextensions/taskplane/waves.ts— DAG + wave computation + lane assignmentextensions/taskplane/execution.ts— lane spawning/monitoringextensions/taskplane/merge.ts— merge orchestrationextensions/taskplane/persistence.ts— batch state persistenceextensions/taskplane/resume.ts— resume reconciliation and continuationextensions/taskplane/worktree.ts— worktree lifecycle