taskplane 0.3.0 → 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/bin/taskplane.mjs
CHANGED
|
@@ -645,10 +645,11 @@ async function cmdInit(args) {
|
|
|
645
645
|
console.log(`\n${c.bold}Creating files...${c.reset}\n`);
|
|
646
646
|
const skipIfExists = !force;
|
|
647
647
|
|
|
648
|
-
// 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)
|
|
649
650
|
for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md"]) {
|
|
650
651
|
copyTemplate(
|
|
651
|
-
path.join(TEMPLATES_DIR, "agents", agent),
|
|
652
|
+
path.join(TEMPLATES_DIR, "agents", "local", agent),
|
|
652
653
|
path.join(projectRoot, ".pi", "agents", agent),
|
|
653
654
|
{ skipIfExists, label: `.pi/agents/${agent}` }
|
|
654
655
|
);
|
|
@@ -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
|
@@ -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
|
+
═══════════════════════════════════════════════════════════════════ -->
|