taskplane 0.13.0 → 0.14.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
@@ -507,6 +507,7 @@ async function cmdUninstall(args) {
507
507
  ".pi/agents/task-worker.md",
508
508
  ".pi/agents/task-reviewer.md",
509
509
  ".pi/agents/task-merger.md",
510
+ ".pi/agents/supervisor.md",
510
511
  ".pi/batch-state.json",
511
512
  ".pi/batch-history.json",
512
513
  ".pi/orch-abort-signal",
@@ -1301,7 +1302,7 @@ async function cmdInit(args) {
1301
1302
  const skipIfExists = !force && !userConfirmedOverwrite;
1302
1303
 
1303
1304
  // Agent prompts
1304
- for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md"]) {
1305
+ for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md", "supervisor.md"]) {
1305
1306
  copyTemplate(
1306
1307
  path.join(TEMPLATES_DIR, "agents", "local", agent),
1307
1308
  path.join(taskplaneDir, "agents", agent),
@@ -1524,7 +1525,7 @@ async function cmdInit(args) {
1524
1525
 
1525
1526
  // Agent prompts — copy thin local files (base prompts ship in the package
1526
1527
  // and are composed automatically by the task-runner at runtime)
1527
- for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md"]) {
1528
+ for (const agent of ["task-worker.md", "task-reviewer.md", "task-merger.md", "supervisor.md"]) {
1528
1529
  copyTemplate(
1529
1530
  path.join(TEMPLATES_DIR, "agents", "local", agent),
1530
1531
  path.join(projectRoot, ".pi", "agents", agent),
@@ -1685,6 +1686,7 @@ function printFileList(vars, noExamples, preset, exampleTemplateDirs = [], proje
1685
1686
  ".pi/agents/task-worker.md",
1686
1687
  ".pi/agents/task-reviewer.md",
1687
1688
  ".pi/agents/task-merger.md",
1689
+ ".pi/agents/supervisor.md",
1688
1690
  ".pi/task-runner.yaml",
1689
1691
  ];
1690
1692
  if (preset !== "runner-only") files.push(".pi/task-orchestrator.yaml");
@@ -1723,6 +1725,7 @@ function printWorkspaceFileList(vars, noExamples, preset, exampleTemplateDirs, c
1723
1725
  `${prefix}/agents/task-worker.md`,
1724
1726
  `${prefix}/agents/task-reviewer.md`,
1725
1727
  `${prefix}/agents/task-merger.md`,
1728
+ `${prefix}/agents/supervisor.md`,
1726
1729
  `${prefix}/task-runner.yaml`,
1727
1730
  ];
1728
1731
  if (preset !== "runner-only") files.push(`${prefix}/task-orchestrator.yaml`);
@@ -2518,6 +2521,7 @@ function cmdDoctor() {
2518
2521
  { path: "agents/task-worker.md", required: true },
2519
2522
  { path: "agents/task-reviewer.md", required: true },
2520
2523
  { path: "agents/task-merger.md", required: true },
2524
+ { path: "agents/supervisor.md", required: false },
2521
2525
  { path: "taskplane.json", required: false },
2522
2526
  ];
2523
2527
 
@@ -627,8 +627,23 @@ function renderMergeAgents(batch, tmuxSessions) {
627
627
  const showRepos = knownRepos.length >= 2;
628
628
  const telemetry = currentData?.telemetry || {};
629
629
 
630
- // Check for active merge sessions (convention: orch-merge-*)
631
- const mergeSessions = (tmuxSessions || []).filter(s => s.startsWith("orch-merge"));
630
+ // Check for active merge sessions (convention: {prefix}-{opId}-merge-{N})
631
+ const mergeSessions = (tmuxSessions || []).filter(s => s.includes("-merge-"));
632
+
633
+ // Derive merge session name from lane session naming pattern.
634
+ // Lane sessions: "{prefix}-{opId}-lane-{N}", merge sessions: "{prefix}-{opId}-merge-{N}".
635
+ // Extract the prefix-opId part from the first lane and use it to construct merge names.
636
+ const lanes = batch?.lanes || [];
637
+ let mergePrefix = "orch-merge"; // fallback for legacy/unknown patterns
638
+ if (lanes.length > 0 && lanes[0].tmuxSessionName) {
639
+ const laneName = lanes[0].tmuxSessionName;
640
+ const laneMatch = laneName.match(/^(.+)-lane-\d+$/);
641
+ if (laneMatch) {
642
+ mergePrefix = laneMatch[1] + "-merge";
643
+ }
644
+ }
645
+ // Helper: get merge session name for a lane number
646
+ const getMergeSessionName = (laneNum) => `${mergePrefix}-${laneNum}`;
632
647
 
633
648
  if (mergeResults.length === 0 && mergeSessions.length === 0) {
634
649
  $mergeBody.innerHTML = '<div class="empty-state">No merge agents active</div>';
@@ -639,6 +654,9 @@ function renderMergeAgents(batch, tmuxSessions) {
639
654
  html += '<th>Wave</th><th>Status</th><th>Session</th><th>Telemetry</th><th>Attach</th><th>Details</th>';
640
655
  html += '</tr></thead><tbody>';
641
656
 
657
+ // Track sessions shown in wave result rows so we don't duplicate them below
658
+ const shownSessions = new Set();
659
+
642
660
  // Show merge results
643
661
  for (const mr of mergeResults) {
644
662
  // Repo filtering: if a repo is selected and this merge has repoResults,
@@ -653,12 +671,19 @@ function renderMergeAgents(batch, tmuxSessions) {
653
671
  : mr.status === "partial" ? "status-stalled"
654
672
  : "status-failed";
655
673
 
656
- // Look for matching tmux session
657
- const sessionName = `orch-merge-w${mr.waveIndex + 1}`;
658
- const alive = tmuxSet.has(sessionName);
659
-
660
- // Look for merge telemetry data
661
- const mergeTel = telemetry[sessionName] || telemetry[`orch-merge-${mr.waveIndex + 1}`] || null;
674
+ // Look for matching tmux merge sessions for this wave result.
675
+ // Merge sessions follow the naming pattern: {prefix}-{opId}-merge-{laneNumber}
676
+ // (e.g., "orch-henrylach-merge-1"). Find any alive merge sessions.
677
+ const waveMergeSessions = mergeSessions.filter(s => tmuxSet.has(s));
678
+ const sessionName = waveMergeSessions.length > 0 ? waveMergeSessions[0] : null;
679
+ const alive = sessionName !== null;
680
+ if (alive) shownSessions.add(sessionName);
681
+
682
+ // Look for merge telemetry data — check all merge sessions
683
+ let mergeTel = null;
684
+ for (const ms of mergeSessions) {
685
+ if (telemetry[ms]) { mergeTel = telemetry[ms]; break; }
686
+ }
662
687
 
663
688
  html += `<tr>`;
664
689
  html += `<td style="font-family:var(--font-mono);">Wave ${mr.waveIndex + 1}</td>`;
@@ -718,8 +743,7 @@ function renderMergeAgents(batch, tmuxSessions) {
718
743
 
719
744
  // Show active merge sessions not yet in results
720
745
  for (const sess of mergeSessions) {
721
- const alreadyShown = mergeResults.some((mr) => `orch-merge-w${mr.waveIndex + 1}` === sess);
722
- if (alreadyShown) continue;
746
+ if (shownSessions.has(sess)) continue;
723
747
 
724
748
  const sessTel = telemetry[sess] || null;
725
749
  const cmd = `tmux attach -t ${sess}`;
@@ -436,6 +436,7 @@ export function buildDashboardViewModel(
436
436
  return {
437
437
  phase: batchState.phase,
438
438
  batchId: batchState.batchId,
439
+ orchBranch: batchState.orchBranch || batchState.baseBranch || "",
439
440
  waveProgress,
440
441
  elapsed,
441
442
  summary,
@@ -684,7 +685,7 @@ export function createOrchWidget(
684
685
  } else if (vm.phase === "merging") {
685
686
  lines.push("");
686
687
  lines.push(truncateToWidth(
687
- theme.fg("accent", " 🔀 Merging lane branches into develop..."),
688
+ theme.fg("accent", ` 🔀 Merging lane branches into ${vm.orchBranch || "orch branch"}...`),
688
689
  width,
689
690
  ));
690
691
  } else if (vm.phase === "paused") {
@@ -25,6 +25,12 @@ reviewers), or merge branches (that's merge agents). You supervise all of them.
25
25
  full filesystem and command-line access. Use it to read state files, run git
26
26
  commands, edit batch state, manage tmux sessions, and run verification.
27
27
 
28
+ **Your system prompt** is built from `templates/agents/supervisor.md` (base
29
+ template, ships with the package) composed with `.pi/agents/supervisor.md`
30
+ (project-specific overrides). The operator can customize your behavior by
31
+ editing the local override file. Dynamic batch context (IDs, paths, counts,
32
+ guardrails) is injected at runtime.
33
+
28
34
  ---
29
35
 
30
36
  ## 2. Architecture in 60 Seconds
@@ -1070,6 +1076,7 @@ Create the directory and add thin override files:
1070
1076
  - `.pi/agents/task-worker.md` — worker prompt overrides (can be empty initially)
1071
1077
  - `.pi/agents/task-reviewer.md` — reviewer prompt overrides (can be empty initially)
1072
1078
  - `.pi/agents/task-merger.md` — merger prompt overrides (can be empty initially)
1079
+ - `.pi/agents/supervisor.md` — supervisor prompt overrides (can be empty initially)
1073
1080
 
1074
1081
  Each file can start with a brief comment explaining its purpose:
1075
1082
  ```markdown
@@ -1748,6 +1748,171 @@ function resolvePrimerPath(): string {
1748
1748
  }
1749
1749
  }
1750
1750
 
1751
+
1752
+ // ── Template Loading (TP-058) ────────────────────────────────────────
1753
+
1754
+ /**
1755
+ * Resolve the path to a base supervisor template shipped with the package.
1756
+ *
1757
+ * Templates live in `<package-root>/templates/agents/`. This function derives
1758
+ * the package root from the extension file's location
1759
+ * (`<package-root>/extensions/taskplane/supervisor.ts`).
1760
+ *
1761
+ * @param name - Template filename without extension (e.g. "supervisor", "supervisor-routing")
1762
+ * @returns Absolute path to the template file
1763
+ *
1764
+ * @since TP-058
1765
+ */
1766
+ function resolveBaseTemplatePath(name: string): string {
1767
+ try {
1768
+ const thisDir = dirname(fileURLToPath(import.meta.url));
1769
+ // thisDir = <package-root>/extensions/taskplane/
1770
+ return join(thisDir, "..", "..", "templates", "agents", `${name}.md`);
1771
+ } catch {
1772
+ return join(__dirname, "..", "..", "templates", "agents", `${name}.md`);
1773
+ }
1774
+ }
1775
+
1776
+ /**
1777
+ * Parse a simple frontmatter+body markdown file.
1778
+ * Returns null if the file doesn't exist or has no frontmatter.
1779
+ *
1780
+ * @since TP-058
1781
+ */
1782
+ function parseSupervisorTemplate(filePath: string): { fm: Record<string, string>; body: string } | null {
1783
+ if (!existsSync(filePath)) return null;
1784
+ const raw = readFileSync(filePath, "utf-8").replace(/\r\n/g, "\n");
1785
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
1786
+ if (!match) return null;
1787
+ const fm: Record<string, string> = {};
1788
+ for (const line of match[1].split("\n")) {
1789
+ const idx = line.indexOf(":");
1790
+ if (idx > 0) {
1791
+ const key = line.slice(0, idx).trim();
1792
+ if (!key.startsWith("#")) { // Skip commented-out frontmatter
1793
+ fm[key] = line.slice(idx + 1).trim();
1794
+ }
1795
+ }
1796
+ }
1797
+ return { fm, body: match[2].trim() };
1798
+ }
1799
+
1800
+ /**
1801
+ * Load a supervisor template: base (from package) + local override (from project).
1802
+ *
1803
+ * Follows the same composition pattern as `loadAgentDef()` in task-runner.ts:
1804
+ * - Base template: shipped in `templates/agents/{name}.md`
1805
+ * - Local override: `.pi/agents/{name}.md` in the project
1806
+ * - If local has `standalone: true`, use it exclusively
1807
+ * - Otherwise, compose base + local with a separator
1808
+ *
1809
+ * @param name - Template name (e.g. "supervisor", "supervisor-routing")
1810
+ * @param stateRoot - Root path for .pi/ state directory
1811
+ * @returns The composed template body, or null if no template found
1812
+ *
1813
+ * @since TP-058
1814
+ */
1815
+ export function loadSupervisorTemplate(name: string, stateRoot: string, localName?: string): string | null {
1816
+ const basePath = resolveBaseTemplatePath(name);
1817
+ const baseDef = parseSupervisorTemplate(basePath);
1818
+
1819
+ // Load local override from .pi/agents/{localName}.md (defaults to base name)
1820
+ // This allows routing template (base: "supervisor-routing") to share the
1821
+ // same local override as the main supervisor (local: "supervisor").
1822
+ const effectiveLocalName = localName || name;
1823
+ const localPath = stateRoot ? join(stateRoot, ".pi", "agents", `${effectiveLocalName}.md`) : "";
1824
+ const localDef = localPath ? parseSupervisorTemplate(localPath) : null;
1825
+
1826
+ // No base and no local → null (triggers fallback to inline prompt)
1827
+ if (!baseDef && !localDef) return null;
1828
+
1829
+ // Local with standalone: true → use local as-is, ignore base
1830
+ if (localDef?.fm.standalone === "true") {
1831
+ return localDef.body;
1832
+ }
1833
+
1834
+ // Compose base + local
1835
+ const baseBody = baseDef?.body || "";
1836
+ const localBody = localDef?.body || "";
1837
+ if (localBody) {
1838
+ return baseBody + "\n\n---\n\n## Project-Specific Guidance\n\n" + localBody;
1839
+ }
1840
+ return baseBody;
1841
+ }
1842
+
1843
+ /**
1844
+ * Replace `{{variable}}` placeholders in a template string.
1845
+ *
1846
+ * @param template - Template string with `{{key}}` placeholders
1847
+ * @param vars - Key-value map of variable replacements
1848
+ * @returns Template with all known placeholders replaced
1849
+ *
1850
+ * @since TP-058
1851
+ */
1852
+ function replaceTemplateVars(template: string, vars: Record<string, string>): string {
1853
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1854
+ return key in vars ? vars[key] : match;
1855
+ });
1856
+ }
1857
+
1858
+
1859
+ /**
1860
+ * Build the guardrails section dynamically based on integration mode (TP-043).
1861
+ * Extracted as a helper so both the template path and inline fallback can reuse it.
1862
+ * @since TP-058
1863
+ */
1864
+ function buildGuardrailsSection(integrationMode: string): string {
1865
+ if (integrationMode === "supervised" || integrationMode === "auto") {
1866
+ const modeNote = integrationMode === "supervised"
1867
+ ? `**Supervised mode:** Before executing integration, describe your plan and ask the operator for confirmation.`
1868
+ : `**Auto mode:** Execute integration directly. Report the outcome to the operator. Pause only on errors or conflicts.`;
1869
+ return `## What You Must NEVER Do
1870
+
1871
+ 1. Never delete \`.pi/batch-state.json\` without operator approval
1872
+ 2. Never modify task code (files that workers wrote)
1873
+ 3. Never modify PROMPT.md files
1874
+ 4. Never \`git reset --hard\` with uncommitted changes
1875
+ 5. Never skip tasks/waves without telling the operator
1876
+ 6. Never create GitHub releases
1877
+
1878
+ ## Integration Permissions (mode: ${integrationMode})
1879
+
1880
+ You are authorized to perform integration operations after batch completion:
1881
+ - \`git push origin <orch-branch>\` — push the orch branch for PR creation
1882
+ - \`gh pr create\` — create pull requests for integration
1883
+ - \`git merge --ff-only\` or \`git merge --no-edit\` — local branch integration
1884
+ - \`git branch -D <orch-branch>\` — cleanup after successful integration
1885
+
1886
+ ${modeNote}`;
1887
+ }
1888
+ return `## What You Must NEVER Do
1889
+
1890
+ 1. Never \`git push\` to any remote
1891
+ 2. Never delete \`.pi/batch-state.json\` without operator approval
1892
+ 3. Never modify task code (files that workers wrote)
1893
+ 4. Never modify PROMPT.md files
1894
+ 5. Never \`git reset --hard\` with uncommitted changes
1895
+ 6. Never skip tasks/waves without telling the operator
1896
+ 7. Never create PRs or GitHub releases`;
1897
+ }
1898
+
1899
+ /**
1900
+ * Build the autonomy level description for the current autonomy setting.
1901
+ * @since TP-058
1902
+ */
1903
+ function buildAutonomyDescription(autonomyLabel: string): string {
1904
+ switch (autonomyLabel) {
1905
+ case "interactive":
1906
+ return `**Your current level is INTERACTIVE.** ASK the operator before any Tier 0 Known or Destructive action. Explain what you want to do, why, and what the alternatives are. Let the operator decide.`;
1907
+ case "supervised":
1908
+ return `**Your current level is SUPERVISED.** Execute Tier 0 Known patterns automatically (retries, cleanup, session restarts). ASK before Destructive actions (manual merges, state editing, skipping tasks, killing sessions). Always explain what you did and why.`;
1909
+ case "autonomous":
1910
+ return `**Your current level is AUTONOMOUS.** Execute all recovery actions automatically. Pause and summarize only when you're genuinely stuck and cannot resolve the issue. The operator trusts you to make reasonable decisions.`;
1911
+ default:
1912
+ return "";
1913
+ }
1914
+ }
1915
+
1751
1916
  /**
1752
1917
  * Build the supervisor system prompt.
1753
1918
  *
@@ -1789,38 +1954,37 @@ export function buildSupervisorSystemPrompt(
1789
1954
  const actionsPath = auditTrailPath(stateRoot);
1790
1955
  const integrationMode = config.orchestrator.integration;
1791
1956
 
1792
- // TP-043: Build guardrails section dynamically based on integration mode.
1793
- // When integration is "supervised" or "auto", the supervisor is allowed to
1794
- // push branches and create PRs as part of post-batch integration.
1795
- const guardrailsSection = integrationMode === "supervised" || integrationMode === "auto"
1796
- ? `## What You Must NEVER Do
1797
-
1798
- 1. Never delete \`.pi/batch-state.json\` without operator approval
1799
- 2. Never modify task code (files that workers wrote)
1800
- 3. Never modify PROMPT.md files
1801
- 4. Never \`git reset --hard\` with uncommitted changes
1802
- 5. Never skip tasks/waves without telling the operator
1803
- 6. Never create GitHub releases
1804
-
1805
- ## Integration Permissions (mode: ${integrationMode})
1806
-
1807
- You are authorized to perform integration operations after batch completion:
1808
- - \`git push origin <orch-branch>\` — push the orch branch for PR creation
1809
- - \`gh pr create\` — create pull requests for integration
1810
- - \`git merge --ff-only\` or \`git merge --no-edit\` — local branch integration
1811
- - \`git branch -D <orch-branch>\` — cleanup after successful integration
1812
-
1813
- ${integrationMode === "supervised" ? `**Supervised mode:** Before executing integration, describe your plan and ask the operator for confirmation.` : `**Auto mode:** Execute integration directly. Report the outcome to the operator. Pause only on errors or conflicts.`}`
1814
- : `## What You Must NEVER Do
1815
-
1816
- 1. Never \`git push\` to any remote
1817
- 2. Never delete \`.pi/batch-state.json\` without operator approval
1818
- 3. Never modify task code (files that workers wrote)
1819
- 4. Never modify PROMPT.md files
1820
- 5. Never \`git reset --hard\` with uncommitted changes
1821
- 6. Never skip tasks/waves without telling the operator
1822
- 7. Never create PRs or GitHub releases`;
1957
+ // Build dynamic sections
1958
+ const guardrailsSection = buildGuardrailsSection(integrationMode);
1959
+ const autonomyGuidance = buildAutonomyDescription(autonomyLabel);
1960
+
1961
+ // TP-058: Try template-based prompt first, fall back to inline prompt.
1962
+ const template = loadSupervisorTemplate("supervisor", stateRoot);
1963
+ if (template) {
1964
+ const vars: Record<string, string> = {
1965
+ batchId: batchState.batchId || "(initializing read batch state file)",
1966
+ phase: batchState.phase,
1967
+ baseBranch: batchState.baseBranch,
1968
+ orchBranch: batchState.orchBranch || "(legacy mode)",
1969
+ waveSummary,
1970
+ totalTasks: String(batchState.totalTasks),
1971
+ succeededTasks: String(batchState.succeededTasks),
1972
+ failedTasks: String(batchState.failedTasks),
1973
+ skippedTasks: String(batchState.skippedTasks),
1974
+ blockedTasks: String(batchState.blockedTasks),
1975
+ autonomy: autonomyLabel,
1976
+ batchStatePath,
1977
+ eventsPath,
1978
+ actionsPath,
1979
+ stateRoot,
1980
+ primerPath,
1981
+ guardrailsSection,
1982
+ autonomyGuidance,
1983
+ };
1984
+ return replaceTemplateVars(template, vars);
1985
+ }
1823
1986
 
1987
+ // ── Fallback: inline prompt (backward compatibility when template missing) ──
1824
1988
  const prompt = `# Supervisor Agent
1825
1989
 
1826
1990
  You are the **batch supervisor** — a persistent agent that monitors a Taskplane
@@ -1915,7 +2079,7 @@ Every action you take falls into one of three categories:
1915
2079
  | Tier 0 Known | ❓ ASK | ✅ auto | ✅ auto |
1916
2080
  | Destructive | ❓ ASK | ❓ ASK | ✅ auto |
1917
2081
 
1918
- ${autonomyLabel === "interactive" ? `**Your current level is INTERACTIVE.** ASK the operator before any Tier 0 Known or Destructive action. Explain what you want to do, why, and what the alternatives are. Let the operator decide.` : ""}${autonomyLabel === "supervised" ? `**Your current level is SUPERVISED.** Execute Tier 0 Known patterns automatically (retries, cleanup, session restarts). ASK before Destructive actions (manual merges, state editing, skipping tasks, killing sessions). Always explain what you did and why.` : ""}${autonomyLabel === "autonomous" ? `**Your current level is AUTONOMOUS.** Execute all recovery actions automatically. Pause and summarize only when you're genuinely stuck and cannot resolve the issue. The operator trusts you to make reasonable decisions.` : ""}
2082
+ ${autonomyGuidance}
1919
2083
 
1920
2084
  ## Audit Trail
1921
2085
 
@@ -2020,8 +2184,33 @@ export function buildRoutingSystemPrompt(
2020
2184
  const primerPath = resolvePrimerPath();
2021
2185
 
2022
2186
  // Map routing state to the appropriate script section in the primer
2187
+ const scriptGuidance = buildRoutingScriptGuidance(routingContext.routingState, primerPath);
2188
+
2189
+ // TP-058: Try template-based prompt first, fall back to inline prompt.
2190
+ const template = loadSupervisorTemplate("supervisor-routing", stateRoot, "supervisor");
2191
+ if (template) {
2192
+ const vars: Record<string, string> = {
2193
+ routingState: routingContext.routingState,
2194
+ contextMessage: routingContext.contextMessage,
2195
+ scriptGuidance,
2196
+ primerPath,
2197
+ };
2198
+ return replaceTemplateVars(template, vars);
2199
+ }
2200
+
2201
+ // ── Fallback: inline prompt (backward compatibility when template missing) ──
2202
+ return buildRoutingInlinePrompt(routingContext, primerPath, scriptGuidance);
2203
+ }
2204
+
2205
+ /**
2206
+ * Build the script guidance section for routing prompts.
2207
+ * Contains the per-state instructions that guide the supervisor's behavior.
2208
+ *
2209
+ * @since TP-058
2210
+ */
2211
+ function buildRoutingScriptGuidance(routingState: string, primerPath: string): string {
2023
2212
  let scriptGuidance: string;
2024
- switch (routingContext.routingState) {
2213
+ switch (routingState) {
2025
2214
  case "no-config":
2026
2215
  scriptGuidance = `## Your Mission: Onboarding
2027
2216
 
@@ -2048,6 +2237,7 @@ When the conversation reaches the config generation phase, create ALL of these
2048
2237
  - \`.pi/agents/task-worker.md\` — worker prompt overrides (can start empty with a brief comment)
2049
2238
  - \`.pi/agents/task-reviewer.md\` — reviewer prompt overrides (can start empty with a brief comment)
2050
2239
  - \`.pi/agents/task-merger.md\` — merger prompt overrides (can start empty with a brief comment)
2240
+ - \`.pi/agents/supervisor.md\` — supervisor prompt overrides (can start empty with a brief comment)
2051
2241
  - \`.gitignore\` entries — add Taskplane working file patterns if not already present
2052
2242
 
2053
2243
  Use conservative creation: check if each file exists before writing. If files
@@ -2152,7 +2342,7 @@ A completed batch exists that hasn't been integrated yet.
2152
2342
  default:
2153
2343
  scriptGuidance = `## Your Mission: Project Assistance
2154
2344
 
2155
- Detected state: ${routingContext.routingState}
2345
+ Detected state: ${routingState}
2156
2346
 
2157
2347
  1. **Read the primer** at \`${primerPath}\`
2158
2348
  2. **Assess the situation** and help the operator with their next step
@@ -2160,6 +2350,20 @@ Detected state: ${routingContext.routingState}
2160
2350
  break;
2161
2351
  }
2162
2352
 
2353
+ return scriptGuidance;
2354
+ }
2355
+
2356
+ /**
2357
+ * Inline fallback for the routing system prompt.
2358
+ * Used when the base template file cannot be found.
2359
+ *
2360
+ * @since TP-058
2361
+ */
2362
+ function buildRoutingInlinePrompt(
2363
+ routingContext: SupervisorRoutingContext,
2364
+ primerPath: string,
2365
+ scriptGuidance: string,
2366
+ ): string {
2163
2367
  const prompt = `# Project Supervisor
2164
2368
 
2165
2369
  You are the **project supervisor** — a conversational agent that helps operators
@@ -1957,6 +1957,7 @@ export interface OrchLaneCardData {
1957
1957
  export interface OrchDashboardViewModel {
1958
1958
  phase: OrchBatchPhase;
1959
1959
  batchId: string;
1960
+ orchBranch: string; // e.g., "orch/henry-20260318T140000" — merge target branch
1960
1961
  waveProgress: string; // e.g., "2/3"
1961
1962
  elapsed: string; // e.g., "2m 14s"
1962
1963
  summary: OrchSummaryCounts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: supervisor
3
+ # tools: read,write,edit,bash,grep,find,ls
4
+ # model:
5
+ # standalone: true
6
+ ---
7
+
8
+ <!-- ═══════════════════════════════════════════════════════════════════
9
+ Project-Specific Supervisor Guidance
10
+
11
+ This file is COMPOSED with the base supervisor 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
+ - Supervisor identity and standing orders
16
+ - Recovery action classification and autonomy levels
17
+ - Audit trail format and rules
18
+ - Batch monitoring, failure handling, operator communication
19
+ - Orchestrator tool reference (orch_status, orch_pause, etc.)
20
+ - Startup checklist and operational knowledge
21
+
22
+ Add project-specific supervisor rules below. Common examples:
23
+ - Run linter before integration ("always run `npm run lint` after merge")
24
+ - CI dashboard URL for failure triage
25
+ - PR template or label conventions
26
+ - Project-specific recovery procedures
27
+ - Team notification preferences (Slack, etc.)
28
+ - Custom health check commands
29
+
30
+ To override frontmatter values (tools, model), uncomment and edit above.
31
+ To use this file as a FULLY STANDALONE prompt (ignoring the base),
32
+ uncomment `standalone: true` above and write the complete prompt below.
33
+ ═══════════════════════════════════════════════════════════════════ -->
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: supervisor-routing
3
+ description: Project supervisor routing agent — onboarding, batch planning, and conversational flows
4
+ tools: read,write,edit,bash,grep,find,ls
5
+ # model:
6
+ ---
7
+ # Project Supervisor
8
+
9
+ You are the **project supervisor** — a conversational agent that helps operators
10
+ set up, plan, and manage their Taskplane project. You were activated because the
11
+ operator typed `/orch` without arguments, and I detected the project state.
12
+
13
+ ## Identity
14
+
15
+ You share this terminal session with the human operator. You are a senior
16
+ engineer helping them get the most out of Taskplane. Be conversational, helpful,
17
+ and adaptive — follow the scripts as guides, not rigid templates. If the
18
+ operator wants to skip ahead or go minimal, respect that.
19
+
20
+ ## Detected State
21
+
22
+ **Routing state:** {{routingState}}
23
+ **Context:** {{contextMessage}}
24
+
25
+ {{scriptGuidance}}
26
+
27
+ ## Capabilities
28
+
29
+ You have full tool access: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`.
30
+ Use these to:
31
+ - Analyze project structure (read files, list directories, grep for patterns)
32
+ - Read existing configuration and docs
33
+ - Generate configuration files and CONTEXT.md documents
34
+ - Run git commands for branch analysis
35
+ - Run `gh` CLI commands for GitHub integration (issues, branch protection)
36
+ - Create task folders and PROMPT.md files
37
+
38
+ ### Orchestrator Tools
39
+
40
+ You also have orchestrator tools available for batch management:
41
+ - **orch_status()** — Check batch status
42
+ - **orch_resume(force?)** — Resume a paused batch
43
+ - **orch_integrate(mode?, force?, branch?)** — Integrate completed batch (modes: "fast-forward", "merge", "pr")
44
+ - **orch_pause()** — Pause running batch
45
+ - **orch_abort(hard?)** — Abort running batch
46
+
47
+ Use these when the conversation leads to batch operations (e.g., integrating a completed batch).
48
+
49
+ ## Operational Knowledge
50
+
51
+ **IMPORTANT:** Read `{{primerPath}}` for your complete operational runbook.
52
+ It contains:
53
+ - Onboarding scripts (Scripts 1-5) with detailed conversation guides
54
+ - Returning user scripts (Scripts 6-8) for batch planning, health checks, and retrospectives
55
+ - Project detection heuristics and exploration checklists
56
+ - Config generation templates and conventions
57
+
58
+ Read the relevant script section now before starting the conversation.
59
+
60
+ ## Communication Style
61
+
62
+ - Be conversational, not robotic — you're having a dialog, not running a wizard
63
+ - Show what you discover as you explore ("I can see you have a TypeScript project with...")
64
+ - Ask questions when choices matter, propose defaults when they don't
65
+ - Summarize what you'll create before writing files — let the operator confirm
66
+ - If the operator says "just give me defaults", do it and move on
67
+
68
+ ## Starting a Batch
69
+
70
+ When the operator wants to run pending tasks, use the `/orch all` command.
71
+ You can invoke it directly — it will seamlessly transition you from conversational
72
+ mode to batch monitoring mode. Examples of operator intent:
73
+
74
+ - "run the open tasks" → respond with a brief confirmation, then invoke `/orch all`
75
+ - "start the batch" → invoke `/orch all`
76
+ - "run just the platform tasks" → invoke `/orch platform` (with the area name)
77
+
78
+ Before starting, you may optionally:
79
+ - Show a quick summary of pending tasks and wave plan (`/orch-plan all`)
80
+ - Ask for confirmation if the operator's intent was ambiguous
81
+
82
+ After `/orch all` starts, your system prompt will automatically switch to
83
+ batch monitoring mode. You'll have full visibility into wave progress, task
84
+ outcomes, and can handle failures.
85
+
86
+ ## What You Must NEVER Do
87
+
88
+ 1. Never modify existing code files (only create config/scaffolding)
89
+ 2. Never `git push` to any remote
90
+ 3. Never overwrite existing config files without asking
91
+ 4. Never make assumptions about project conventions — detect them
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: supervisor
3
+ description: Batch supervisor — monitors orchestration, handles failures, keeps operator informed
4
+ tools: read,write,edit,bash,grep,find,ls
5
+ ---
6
+ # Supervisor Agent
7
+
8
+ You are the **batch supervisor** — a persistent agent that monitors a Taskplane
9
+ orchestration batch, handles failures, and keeps the operator informed.
10
+
11
+ ## Identity
12
+
13
+ You share this terminal session with the human operator. After `/orch` started
14
+ a batch, you activated to supervise it. The operator can talk to you naturally
15
+ at any time. You are a senior engineer on call for this batch.
16
+
17
+ ## Current Batch Context
18
+
19
+ - **Batch ID:** {{batchId}}
20
+ - **Phase:** {{phase}}
21
+ - **Base branch:** {{baseBranch}}
22
+ - **Orch branch:** {{orchBranch}}
23
+ - **Progress:** {{waveSummary}}, {{totalTasks}} total tasks
24
+ - **Succeeded:** {{succeededTasks}} | **Failed:** {{failedTasks}} | **Skipped:** {{skippedTasks}} | **Blocked:** {{blockedTasks}}
25
+ - **Autonomy:** {{autonomy}}
26
+
27
+ ## Key File Paths
28
+
29
+ - **Batch state:** `{{batchStatePath}}`
30
+ - **Engine events:** `{{eventsPath}}`
31
+ - **Audit trail:** `{{actionsPath}}`
32
+ - **State root:** `{{stateRoot}}`
33
+
34
+ ## Capabilities
35
+
36
+ You have full tool access: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`.
37
+ Use these to:
38
+ - Read batch state, STATUS.md files, merge results, event logs
39
+ - Run git commands for diagnostics and manual merge recovery
40
+ - Edit batch-state.json for state repairs (when needed)
41
+ - Manage tmux sessions (list, kill, attach)
42
+ - Run verification commands (tests)
43
+
44
+ ## Standing Orders
45
+
46
+ 1. **Monitor engine events.** Periodically read `{{eventsPath}}` to track
47
+ batch progress. Report significant events to the operator proactively:
48
+ - Wave starts/completions
49
+ - Task failures requiring attention
50
+ - Merge successes/failures
51
+ - Batch completion
52
+
53
+ 2. **Handle failures.** When tasks fail or merges time out, diagnose the
54
+ issue using the patterns in supervisor-primer.md and take appropriate
55
+ recovery action based on your autonomy level ({{autonomy}}).
56
+
57
+ 3. **Keep the operator informed.** Provide clear, natural status updates.
58
+ When the operator asks "how's it going?" — read batch state and summarize.
59
+
60
+ 4. **Log all recovery actions** to the audit trail (see Audit Trail section below).
61
+
62
+ 5. **Respect your autonomy level** (see Recovery Action Classification below).
63
+
64
+ ## Recovery Action Classification
65
+
66
+ Every action you take falls into one of three categories:
67
+
68
+ ### Diagnostic (always allowed — no confirmation needed)
69
+ - Reading batch-state.json, STATUS.md, events.jsonl, merge results
70
+ - Running `git status`, `git log`, `git diff`
71
+ - Running test suites (`npx vitest run`, etc.)
72
+ - Listing tmux sessions (`tmux list-sessions`)
73
+ - Checking worktree health (`git worktree list`)
74
+ - Reading any file for diagnostics
75
+
76
+ ### Tier 0 Known (known recovery patterns)
77
+ - Restarting a crashed tmux worker session
78
+ - Cleaning up stale worktrees for retry
79
+ - Retrying a timed-out merge
80
+ - Resetting a session name collision
81
+ - Clearing a git lock file (`.git/index.lock`)
82
+
83
+ ### Destructive (state mutations, irreversible operations)
84
+ - Killing a tmux session (`tmux kill-session`)
85
+ - Editing batch-state.json fields
86
+ - Running `git reset`, `git merge`, `git checkout -B`
87
+ - Removing worktrees (`git worktree remove`)
88
+ - Modifying STATUS.md or .DONE files
89
+ - Deleting git branches (`git branch -D`)
90
+ - Skipping tasks or waves
91
+
92
+ ### Autonomy Decision Table (current level: {{autonomy}})
93
+
94
+ | Classification | Interactive | Supervised | Autonomous |
95
+ |----------------|-------------|------------|------------|
96
+ | Diagnostic | ✅ auto | ✅ auto | ✅ auto |
97
+ | Tier 0 Known | ❓ ASK | ✅ auto | ✅ auto |
98
+ | Destructive | ❓ ASK | ❓ ASK | ✅ auto |
99
+
100
+ {{autonomyGuidance}}
101
+
102
+ ## Audit Trail
103
+
104
+ Log every recovery action to `{{actionsPath}}` as a single-line JSON entry.
105
+
106
+ **Format** (one JSON object per line):
107
+ ```json
108
+ {"ts":"<ISO 8601>","action":"<action_name>","classification":"<diagnostic|tier0_known|destructive>","context":"<why>","command":"<what>","result":"<pending|success|failure|skipped>","detail":"<outcome>","batchId":"{{batchId}}"}
109
+ ```
110
+
111
+ **Rules:**
112
+ 1. For **destructive** actions: write a "pending" entry BEFORE executing, then
113
+ write a result entry AFTER with "success" or "failure" and detail.
114
+ 2. For **diagnostic** and **tier0_known** actions: write a single result entry
115
+ AFTER execution.
116
+ 3. Include optional fields when relevant: `waveIndex`, `laneNumber`, `taskId`, `durationMs`.
117
+ 4. Use the `bash` tool to append entries. Example:
118
+ `echo '{"ts":"...","action":"merge_retry","classification":"tier0_known","context":"merge timeout on wave 2","command":"git merge --no-ff task/lane-2","result":"success","detail":"merged with 0 conflicts","batchId":"..."}' >> {{actionsPath}}`
119
+
120
+ **Why this matters:** When you're taken over by another session or the operator
121
+ asks "what did you do?", the audit trail is the definitive record.
122
+
123
+ ## Operational Knowledge
124
+
125
+ **IMPORTANT:** Read `{{primerPath}}` for your complete operational runbook.
126
+ It contains:
127
+ - Architecture details and wave lifecycle
128
+ - Common failure patterns and recovery procedures
129
+ - Batch state editing guide (safe vs. dangerous edits)
130
+ - Git operations reference
131
+ - Communication guidelines
132
+
133
+ Read it now before doing anything else. It is your primary reference.
134
+
135
+ {{guardrailsSection}}
136
+
137
+ ## Available Orchestrator Tools
138
+
139
+ You can invoke these tools directly — no need to ask the operator or use slash commands:
140
+
141
+ - **orch_status()** — Check current batch status (phase, wave progress, task counts, elapsed time)
142
+ - **orch_pause()** — Pause the running batch (current tasks finish, no new tasks start)
143
+ - **orch_resume(force?)** — Resume a paused or interrupted batch. Use `force=true` for stuck batches.
144
+ - **orch_abort(hard?)** — Abort the running batch. Use `hard=true` for immediate kill.
145
+ - **orch_integrate(mode?, force?, branch?)** — Integrate completed batch into working branch.
146
+ Modes: `"fast-forward"` (default), `"merge"`, `"pr"`.
147
+
148
+ ### When to Use These Tools
149
+
150
+ Use tools **proactively** when the situation calls for it:
151
+ - Operator asks "how's it going?" → call `orch_status()` first, then summarize
152
+ - Batch paused due to a failure you diagnosed and fixed → call `orch_resume()`
153
+ - Batch completed successfully → offer to call `orch_integrate(mode="pr")` or the operator's preferred mode
154
+ - Batch is stuck or failing repeatedly → call `orch_status()` to diagnose, then `orch_abort()` if needed
155
+ - Need to investigate before more tasks launch → call `orch_pause()` first
156
+
157
+ These tools are preferred over reading batch-state.json directly because they handle
158
+ disk fallback, in-memory state, and all edge cases automatically.
159
+
160
+ ## Startup Checklist
161
+
162
+ Now that you've activated:
163
+ 1. Read the supervisor primer at `{{primerPath}}`
164
+ 2. Read `{{batchStatePath}}` for full batch metadata
165
+ 3. Read `{{eventsPath}}` for any events already emitted
166
+ 4. Report to the operator: batch status, wave progress, what you're monitoring