taskplane 0.2.9 → 0.3.1
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
CHANGED
|
@@ -19,7 +19,24 @@ Taskplane turns your coding project into an AI-managed task board. You define ta
|
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
|
-
Taskplane is a [pi package](https://github.com/badlogic/pi-mono). You need [Node.js](https://nodejs.org/) ≥
|
|
22
|
+
Taskplane is a [pi package](https://github.com/badlogic/pi-mono). You need [Node.js](https://nodejs.org/) ≥ 22 and [pi](https://github.com/badlogic/pi-mono) installed first.
|
|
23
|
+
|
|
24
|
+
### Prerequisites
|
|
25
|
+
|
|
26
|
+
| Dependency | Required | Notes |
|
|
27
|
+
|-----------|----------|-------|
|
|
28
|
+
| [Node.js](https://nodejs.org/) ≥ 22 | Yes | Runtime |
|
|
29
|
+
| [pi](https://github.com/badlogic/pi-mono) | Yes | Agent framework |
|
|
30
|
+
| [Git](https://git-scm.com/) | Yes | Version control, worktrees |
|
|
31
|
+
| **tmux** | **Strongly recommended** | Required for `/orch` parallel execution |
|
|
32
|
+
|
|
33
|
+
**tmux** is needed for the orchestrator to spawn parallel worker sessions. Without it, `/orch` will not work. On Windows, Taskplane can install it for you:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
taskplane install-tmux
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On macOS: `brew install tmux` · On Linux: `sudo apt install tmux` (or your distro's package manager)
|
|
23
40
|
|
|
24
41
|
### Option A: Global Install (all projects)
|
|
25
42
|
|
|
@@ -138,6 +155,7 @@ Orchestrator lanes execute tasks through task-runner under the hood, so `/task`
|
|
|
138
155
|
|---------|-------------|
|
|
139
156
|
| `taskplane init` | Scaffold project config (interactive or `--preset`) |
|
|
140
157
|
| `taskplane doctor` | Validate installation and config |
|
|
158
|
+
| `taskplane install-tmux` | Install or upgrade tmux for Git Bash (Windows) |
|
|
141
159
|
| `taskplane version` | Show version info |
|
|
142
160
|
| `taskplane dashboard` | Launch the web dashboard |
|
|
143
161
|
| `taskplane uninstall` | Remove Taskplane project files and optionally uninstall package (`--package`) |
|
package/bin/taskplane.mjs
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
* and auto-discovered by pi. This CLI is for everything else.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
// ─── Node.js version gate (fail fast) ───────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const MIN_NODE_MAJOR = 22;
|
|
16
|
+
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
|
17
|
+
if (nodeMajor < MIN_NODE_MAJOR) {
|
|
18
|
+
console.error(
|
|
19
|
+
`\x1b[31m❌ Taskplane requires Node.js >= ${MIN_NODE_MAJOR}.0.0 (found ${process.versions.node}).\x1b[0m\n` +
|
|
20
|
+
` Upgrade: https://nodejs.org/\n`
|
|
21
|
+
);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
import fs from "node:fs";
|
|
14
26
|
import path from "node:path";
|
|
15
27
|
import readline from "node:readline";
|
|
@@ -633,10 +645,11 @@ async function cmdInit(args) {
|
|
|
633
645
|
console.log(`\n${c.bold}Creating files...${c.reset}\n`);
|
|
634
646
|
const skipIfExists = !force;
|
|
635
647
|
|
|
636
|
-
// Agent prompts
|
|
648
|
+
// Agent prompts — copy thin local files (base prompts ship in the package
|
|
649
|
+
// and are composed automatically by the task-runner at runtime)
|
|
637
650
|
for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md"]) {
|
|
638
651
|
copyTemplate(
|
|
639
|
-
path.join(TEMPLATES_DIR, "agents", agent),
|
|
652
|
+
path.join(TEMPLATES_DIR, "agents", "local", agent),
|
|
640
653
|
path.join(projectRoot, ".pi", "agents", agent),
|
|
641
654
|
{ skipIfExists, label: `.pi/agents/${agent}` }
|
|
642
655
|
);
|
|
@@ -1249,10 +1262,10 @@ function cmdDoctor() {
|
|
|
1249
1262
|
const checks = [
|
|
1250
1263
|
{ label: "pi installed", check: () => commandExists("pi"), detail: () => getVersion("pi") },
|
|
1251
1264
|
{
|
|
1252
|
-
label: "Node.js >=
|
|
1265
|
+
label: "Node.js >= 22.0.0",
|
|
1253
1266
|
check: () => {
|
|
1254
1267
|
const v = process.versions.node;
|
|
1255
|
-
return parseInt(v.split(".")[0]) >=
|
|
1268
|
+
return parseInt(v.split(".")[0]) >= 22;
|
|
1256
1269
|
},
|
|
1257
1270
|
detail: () => `v${process.versions.node}`,
|
|
1258
1271
|
},
|
|
@@ -339,21 +339,141 @@ function clearConversationLog(prefix: string): void {
|
|
|
339
339
|
|
|
340
340
|
// ── Agent Loader ─────────────────────────────────────────────────────
|
|
341
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Parse a markdown agent file into frontmatter key-value pairs and body content.
|
|
344
|
+
* Returns null if the file doesn't exist or has no frontmatter block.
|
|
345
|
+
*/
|
|
346
|
+
function parseAgentFile(filePath: string): { fm: Record<string, string>; body: string } | null {
|
|
347
|
+
if (!existsSync(filePath)) return null;
|
|
348
|
+
const raw = readFileSync(filePath, "utf-8").replace(/\r\n/g, "\n");
|
|
349
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
350
|
+
if (!match) return null;
|
|
351
|
+
const fm: Record<string, string> = {};
|
|
352
|
+
for (const line of match[1].split("\n")) {
|
|
353
|
+
const idx = line.indexOf(":");
|
|
354
|
+
if (idx > 0) fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
355
|
+
}
|
|
356
|
+
return { fm, body: match[2].trim() };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** Cached package root — resolved once, reused for all agent file lookups. */
|
|
360
|
+
let _packageRoot: string | null = null;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Find the taskplane package root directory.
|
|
364
|
+
*
|
|
365
|
+
* Strategy: this file lives at <package-root>/extensions/task-runner.ts.
|
|
366
|
+
* When pi loads it via `-e`, it resolves the full path. We can find the
|
|
367
|
+
* package root by searching for package.json with name "taskplane"
|
|
368
|
+
* starting from known candidate locations.
|
|
369
|
+
*/
|
|
370
|
+
function findPackageRoot(): string {
|
|
371
|
+
if (_packageRoot !== null) return _packageRoot;
|
|
372
|
+
|
|
373
|
+
// Strategy 1: Walk up from this file's location via require.resolve or npm paths
|
|
374
|
+
const candidates: string[] = [];
|
|
375
|
+
|
|
376
|
+
// The extension is loaded by pi from the installed package location.
|
|
377
|
+
// Check well-known npm global paths.
|
|
378
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
379
|
+
if (home) {
|
|
380
|
+
candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", "taskplane"));
|
|
381
|
+
candidates.push(join(home, ".npm-global", "lib", "node_modules", "taskplane"));
|
|
382
|
+
}
|
|
383
|
+
candidates.push(join("/usr", "local", "lib", "node_modules", "taskplane"));
|
|
384
|
+
|
|
385
|
+
// Strategy 2: resolve from pi's node_modules peer
|
|
386
|
+
try {
|
|
387
|
+
const piPath = process.argv[1] || "";
|
|
388
|
+
const piPkgDir = resolve(piPath, "..", "..");
|
|
389
|
+
candidates.push(join(piPkgDir, "..", "taskplane"));
|
|
390
|
+
} catch { /* ignore */ }
|
|
391
|
+
|
|
392
|
+
// Strategy 3: Check TASKPLANE_WORKSPACE_ROOT project-local install
|
|
393
|
+
const wsRoot = process.env.TASKPLANE_WORKSPACE_ROOT;
|
|
394
|
+
if (wsRoot) {
|
|
395
|
+
candidates.push(join(wsRoot, ".pi", "npm", "node_modules", "taskplane"));
|
|
396
|
+
candidates.push(join(wsRoot, "node_modules", "taskplane"));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
for (const dir of candidates) {
|
|
400
|
+
try {
|
|
401
|
+
const pkgPath = join(dir, "package.json");
|
|
402
|
+
if (existsSync(pkgPath)) {
|
|
403
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
404
|
+
if (pkg.name === "taskplane") {
|
|
405
|
+
_packageRoot = dir;
|
|
406
|
+
return dir;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
} catch { /* ignore */ }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
_packageRoot = "";
|
|
413
|
+
return "";
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Resolve the package-shipped base agent file path.
|
|
418
|
+
* Base files live in the package's templates/agents/ directory.
|
|
419
|
+
*/
|
|
420
|
+
function resolveBaseAgentPath(name: string): string {
|
|
421
|
+
const root = findPackageRoot();
|
|
422
|
+
if (!root) return "";
|
|
423
|
+
return join(root, "templates", "agents", `${name}.md`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Load an agent definition with prompt inheritance.
|
|
428
|
+
*
|
|
429
|
+
* Inheritance model (default: compose base + local):
|
|
430
|
+
* 1. Load base agent from the shipped package (templates/agents/{name}.md)
|
|
431
|
+
* 2. Load local agent from .pi/agents/{name}.md (if it exists)
|
|
432
|
+
* 3. If local file has `standalone: true` in frontmatter, use it as-is (no base)
|
|
433
|
+
* 4. Otherwise, compose: base prompt + separator + local content
|
|
434
|
+
* 5. Local frontmatter values (tools, model) override base values
|
|
435
|
+
*
|
|
436
|
+
* If no local file exists, the base file is used directly.
|
|
437
|
+
* If no base file exists (e.g., custom agent), local file is used as-is.
|
|
438
|
+
*/
|
|
342
439
|
function loadAgentDef(cwd: string, name: string): { systemPrompt: string; tools: string; model: string } | null {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
440
|
+
const basePath = resolveBaseAgentPath(name);
|
|
441
|
+
const localPaths = [join(cwd, ".pi", "agents", `${name}.md`), join(cwd, "agents", `${name}.md`)];
|
|
442
|
+
|
|
443
|
+
// Load base from package
|
|
444
|
+
const baseDef = parseAgentFile(basePath);
|
|
445
|
+
|
|
446
|
+
// Load local override (first found wins)
|
|
447
|
+
let localDef: { fm: Record<string, string>; body: string } | null = null;
|
|
448
|
+
for (const p of localPaths) {
|
|
449
|
+
localDef = parseAgentFile(p);
|
|
450
|
+
if (localDef) break;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// No base and no local → null
|
|
454
|
+
if (!baseDef && !localDef) return null;
|
|
455
|
+
|
|
456
|
+
// Local with standalone: true → use local as-is, ignore base
|
|
457
|
+
if (localDef?.fm.standalone === "true") {
|
|
458
|
+
return {
|
|
459
|
+
systemPrompt: localDef.body,
|
|
460
|
+
tools: localDef.fm.tools || "read,grep,find,ls",
|
|
461
|
+
model: localDef.fm.model || "",
|
|
462
|
+
};
|
|
355
463
|
}
|
|
356
|
-
|
|
464
|
+
|
|
465
|
+
// Compose base + local
|
|
466
|
+
const basePrompt = baseDef?.body || "";
|
|
467
|
+
const localPrompt = localDef?.body || "";
|
|
468
|
+
const composedPrompt = localPrompt
|
|
469
|
+
? basePrompt + "\n\n---\n\n## Project-Specific Guidance\n\n" + localPrompt
|
|
470
|
+
: basePrompt;
|
|
471
|
+
|
|
472
|
+
// Local frontmatter overrides base (tools, model)
|
|
473
|
+
const tools = localDef?.fm.tools || baseDef?.fm.tools || "read,grep,find,ls";
|
|
474
|
+
const model = localDef?.fm.model || baseDef?.fm.model || "";
|
|
475
|
+
|
|
476
|
+
return { systemPrompt: composedPrompt.trim(), tools, model };
|
|
357
477
|
}
|
|
358
478
|
|
|
359
479
|
// ── PROMPT.md Parser ─────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskplane",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
26
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
27
|
+
"node": ">=22.0.0"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"bin/",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-merger
|
|
3
|
+
# tools: read,write,edit,bash,grep,find,ls
|
|
4
|
+
# model:
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Merger Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-merger prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- Branch merge workflow (fast-forward, 3-way, conflict resolution)
|
|
16
|
+
- Post-merge verification command execution
|
|
17
|
+
- Result file JSON format and writing conventions
|
|
18
|
+
|
|
19
|
+
Add project-specific merge rules below. Common examples:
|
|
20
|
+
- Post-merge verification commands (build, lint, test)
|
|
21
|
+
- Conflict resolution preferences
|
|
22
|
+
- Protected files that should never be auto-merged
|
|
23
|
+
|
|
24
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
25
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
26
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
27
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-reviewer
|
|
3
|
+
# tools: read,write,bash,grep,find,ls
|
|
4
|
+
# model: openai/gpt-5.3-codex
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Reviewer Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-reviewer prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- Plan review and code review workflows
|
|
16
|
+
- Verdict format (APPROVE / REVISE)
|
|
17
|
+
- Review file output conventions
|
|
18
|
+
- Plan granularity guidance
|
|
19
|
+
|
|
20
|
+
Add project-specific review criteria below. Common examples:
|
|
21
|
+
- Required test coverage thresholds
|
|
22
|
+
- Security review checklist items
|
|
23
|
+
- Architecture constraints to enforce
|
|
24
|
+
- Performance requirements
|
|
25
|
+
|
|
26
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
27
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
28
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
29
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-worker
|
|
3
|
+
# tools: read,write,edit,bash,grep,find,ls
|
|
4
|
+
# model: anthropic/claude-sonnet-4-20250514
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Worker Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-worker prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- STATUS.md-first workflow and checkpoint discipline
|
|
16
|
+
- Fresh-context loop behavior and iteration rules
|
|
17
|
+
- Git commit conventions and .DONE file creation
|
|
18
|
+
- Review response handling
|
|
19
|
+
|
|
20
|
+
Add project-specific rules below. Common examples:
|
|
21
|
+
- Preferred package manager (pnpm, yarn, bun)
|
|
22
|
+
- Test commands (make test, npm run test:unit)
|
|
23
|
+
- Coding standards (linting, formatting)
|
|
24
|
+
- Framework-specific patterns
|
|
25
|
+
- Environment or deployment constraints
|
|
26
|
+
|
|
27
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
28
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
29
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
30
|
+
═══════════════════════════════════════════════════════════════════ -->
|