===== pi-crew package files =====
x/package/src/workflows/workflow-serializer.ts:15:	if (step.worktree !== undefined) lines.push(`worktree: ${step.worktree ? "true" : "false"}`);
x/package/src/workflows/workflow-config.ts:14:	worktree?: boolean;
x/package/src/workflows/discover-workflows.ts:14:const STEP_CONFIG_KEYS = new Set(["role", "dependsOn", "parallelGroup", "output", "reads", "model", "skills", "progress", "worktree", "verify", "task"]);
x/package/src/workflows/discover-workflows.ts:51:		worktree: config.worktree === "true" ? true : config.worktree === "false" ? false : undefined,
x/package/src/teams/team-config.ts:19:	workspaceMode?: "single" | "worktree";
x/package/src/teams/discover-teams.ts:84:			workspaceMode: frontmatter.workspaceMode === "worktree" ? "worktree" : "single",
x/package/src/config/defaults.ts:45:		worktreesSubdir: "worktrees",
x/package/src/config/config.ts:143:	worktree?: CrewWorktreeConfig;
x/package/src/config/config.ts:235:	if (base.worktree || override.worktree) {
x/package/src/config/config.ts:236:		merged.worktree = {
x/package/src/config/config.ts:237:			...(base.worktree ?? {}),
x/package/src/config/config.ts:238:			...withoutUndefined((override.worktree ?? {}) as Record<string, unknown>),
x/package/src/config/config.ts:438:	const worktree: CrewWorktreeConfig = {
x/package/src/config/config.ts:443:	return Object.values(worktree).some((entry) => entry !== undefined) ? worktree : undefined;
x/package/src/config/config.ts:592:		worktree: parseWorktreeConfig(obj.worktree),
x/package/src/schema/team-tool-schema.ts:25:		Type.Literal("worktrees"),
x/package/src/schema/team-tool-schema.ts:52:	async: Type.Optional(Type.Boolean({ description: "Run in background when execution support is enabled." })),
x/package/src/schema/team-tool-schema.ts:55:		Type.Literal("worktree"),
x/package/src/schema/team-tool-schema.ts:78:	action?: "run" | "plan" | "status" | "list" | "get" | "cancel" | "resume" | "create" | "update" | "delete" | "doctor" | "cleanup" | "events" | "artifacts" | "worktrees" | "forget" | "summary" | "prune" | "export" | "import" | "imports" | "help" | "validate" | "config" | "init" | "recommend" | "autonomy" | "api";
x/package/src/schema/team-tool-schema.ts:88:	workspaceMode?: "single" | "worktree";
x/package/src/schema/config-schema.ts:136:	worktree: Type.Optional(PiTeamsWorktreeConfigSchema),
x/package/src/worktree/worktree-manager.ts:1:import { execFileSync, spawnSync } from "node:child_process";
x/package/src/worktree/worktree-manager.ts:11:	worktreePath?: string;
x/package/src/worktree/worktree-manager.ts:44:function linkNodeModulesIfPresent(repoRoot: string, worktreePath: string): boolean {
x/package/src/worktree/worktree-manager.ts:46:	const target = path.join(worktreePath, "node_modules");
x/package/src/worktree/worktree-manager.ts:56:function normalizeSyntheticPath(worktreePath: string, rawPath: string): string {
x/package/src/worktree/worktree-manager.ts:57:	const resolved = path.resolve(worktreePath, rawPath);
x/package/src/worktree/worktree-manager.ts:58:	const relative = path.relative(worktreePath, resolved);
x/package/src/worktree/worktree-manager.ts:59:	if (!relative || relative === "." || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
x/package/src/worktree/worktree-manager.ts:63:function runSetupHook(manifest: TeamRunManifest, task: TeamTaskState, repoRoot: string, worktreePath: string, branch: string): string[] {
x/package/src/worktree/worktree-manager.ts:64:	const cfg = loadConfig(manifest.cwd).config.worktree;
x/package/src/worktree/worktree-manager.ts:67:	if (!fs.existsSync(hookPath) || fs.statSync(hookPath).isDirectory()) throw new Error(`worktree setup hook not found or not a file: ${hookPath}`);
x/package/src/worktree/worktree-manager.ts:69:	const result = spawnSync(nodeHook ? process.execPath : hookPath, nodeHook ? [hookPath] : [], {
x/package/src/worktree/worktree-manager.ts:70:		cwd: worktreePath,
x/package/src/worktree/worktree-manager.ts:72:		input: JSON.stringify({ version: 1, repoRoot, worktreePath, agentCwd: worktreePath, branch, runId: manifest.runId, taskId: task.id, agent: task.agent }),
x/package/src/worktree/worktree-manager.ts:76:	if (result.error) throw new Error(`worktree setup hook failed: ${result.error.message}`);
x/package/src/worktree/worktree-manager.ts:77:	if (result.status !== 0) throw new Error(`worktree setup hook failed with exit code ${result.status}: ${result.stderr || result.stdout || "no output"}`);
x/package/src/worktree/worktree-manager.ts:82:	return [...new Set(parsed.syntheticPaths.filter((entry): entry is string => typeof entry === "string").map((entry) => normalizeSyntheticPath(worktreePath, entry)))];
x/package/src/worktree/worktree-manager.ts:86:	if (manifest.workspaceMode !== "worktree") return { cwd: task.cwd };
x/package/src/worktree/worktree-manager.ts:90:	const worktreeRoot = path.join(projectCrewRoot(manifest.cwd), DEFAULT_PATHS.state.worktreesSubdir, manifest.runId);
x/package/src/worktree/worktree-manager.ts:91:	fs.mkdirSync(worktreeRoot, { recursive: true });
x/package/src/worktree/worktree-manager.ts:92:	const worktreePath = path.join(worktreeRoot, task.id);
x/package/src/worktree/worktree-manager.ts:94:	if (fs.existsSync(worktreePath)) {
x/package/src/worktree/worktree-manager.ts:95:		const currentBranch = git(worktreePath, ["rev-parse", "--abbrev-ref", "HEAD"]);
x/package/src/worktree/worktree-manager.ts:97:			throw new Error(`Existing worktree branch mismatch at ${worktreePath}: expected '${branch}', got '${currentBranch}'.`);
x/package/src/worktree/worktree-manager.ts:99:		return { cwd: worktreePath, worktreePath, branch, reused: true };
x/package/src/worktree/worktree-manager.ts:101:	git(repoRoot, ["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
x/package/src/worktree/worktree-manager.ts:102:	const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
x/package/src/worktree/worktree-manager.ts:103:	const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
x/package/src/worktree/worktree-manager.ts:104:	return { cwd: worktreePath, worktreePath, branch, reused: false, nodeModulesLinked, syntheticPaths };
x/package/src/worktree/worktree-manager.ts:107:export function captureWorktreeDiffStat(worktreePath: string): WorktreeDiffStat {
x/package/src/worktree/worktree-manager.ts:109:		const diffStat = git(worktreePath, ["diff", "--stat"]);
x/package/src/worktree/worktree-manager.ts:110:		const numstat = git(worktreePath, ["diff", "--numstat"]);
x/package/src/worktree/worktree-manager.ts:126:export function captureWorktreeDiff(worktreePath: string): string {
x/package/src/worktree/worktree-manager.ts:128:		return git(worktreePath, ["diff", "--stat"]) + "\n\n" + git(worktreePath, ["diff"]);
x/package/src/worktree/worktree-manager.ts:131:		return `Failed to capture worktree diff: ${message}`;
x/package/src/worktree/cleanup.ts:19:function isDirty(worktreePath: string): boolean {
x/package/src/worktree/cleanup.ts:21:		return git(worktreePath, ["status", "--porcelain"]).trim().length > 0;
x/package/src/worktree/cleanup.ts:27:function captureDiff(worktreePath: string): string {
x/package/src/worktree/cleanup.ts:29:		return [git(worktreePath, ["status", "--porcelain"]), "", git(worktreePath, ["diff", "--stat"]), "", git(worktreePath, ["diff"])].join("\n");
x/package/src/worktree/cleanup.ts:32:		return `Failed to capture cleanup diff for ${worktreePath}: ${message}`;
x/package/src/worktree/cleanup.ts:37:	const worktreeRoot = path.join(projectCrewRoot(manifest.cwd), DEFAULT_PATHS.state.worktreesSubdir, manifest.runId);
x/package/src/worktree/cleanup.ts:39:	if (!fs.existsSync(worktreeRoot)) return result;
x/package/src/worktree/cleanup.ts:41:	for (const entry of fs.readdirSync(worktreeRoot)) {
x/package/src/worktree/cleanup.ts:42:		const worktreePath = path.join(worktreeRoot, entry);
x/package/src/worktree/cleanup.ts:43:		if (!fs.statSync(worktreePath).isDirectory()) continue;
x/package/src/worktree/cleanup.ts:44:		const dirty = isDirty(worktreePath);
x/package/src/worktree/cleanup.ts:49:				content: captureDiff(worktreePath),
x/package/src/worktree/cleanup.ts:50:				producer: "worktree-cleanup",
x/package/src/worktree/cleanup.ts:53:			result.preserved.push({ path: worktreePath, reason: "dirty worktree preserved" });
x/package/src/worktree/cleanup.ts:57:			git(manifest.cwd, ["worktree", "remove", options.force ? "--force" : "", worktreePath].filter(Boolean));
x/package/src/worktree/cleanup.ts:58:			result.removed.push(worktreePath);
x/package/src/worktree/cleanup.ts:61:			result.preserved.push({ path: worktreePath, reason: message });
x/package/src/worktree/cleanup.ts:66:		if (fs.existsSync(worktreeRoot) && fs.readdirSync(worktreeRoot).length === 0) fs.rmSync(worktreeRoot, { recursive: true, force: true });
x/package/src/subagents/index.ts:1:export * from "./spawn.ts";
x/package/src/state/types.ts:47:	worktree?: string;
x/package/src/state/types.ts:81:	spawnedAt: string;
x/package/src/state/types.ts:91:	workspaceMode: "single" | "worktree";
x/package/src/state/types.ts:136:	phase: "started" | "child-spawned" | "child-stdout-final" | "artifact-written";
x/package/src/state/types.ts:157:	worktree?: TaskWorktreeState;

===== pi-messenger-swarm package files =====
x/package/router.ts:45:      throw new Error('No current or session channel set');
x/package/router.ts:218:    case 'spawn':
x/package/index.ts:5: * Uses file-based coordination with a harness server for action dispatch.
x/package/index.ts:36:import { stopAllSpawned } from './swarm/spawn.js';
x/package/index.ts:171:        content: `You are agent "${state.agentName}" in ${locationPart}. Your current channel is ${displayChannelLabel(state.currentChannel)}. Named channel ${displayChannelLabel('memory')} exists for durable cross-session notes. Use pi-messenger-swarm for all coordination. Examples: pi-messenger-swarm join | pi-messenger-swarm swarm | pi-messenger-swarm task create --title "..." | pi-messenger-swarm task claim task-1 | pi-messenger-swarm spawn --role Researcher "Analyze X" | pi-messenger-swarm send AgentName "hello" | pi-messenger-swarm feed --limit 20. See SKILL for full reference.`,
x/package/harness/server.ts:190:  // Resolve session channel if not yet set
x/package/harness/server.ts:404:      // Also patch the session channel's sessionId if it was created before
x/package/harness/cli.ts:30: *   pi-messenger-swarm spawn --role Researcher "Analyze the protocol" [--persona "..."] [--task-id task-1] [--agent-file path] [--objective "..."] [--context "..."]
x/package/harness/cli.ts:31: *   pi-messenger-swarm spawn list
x/package/harness/cli.ts:32: *   pi-messenger-swarm spawn history
x/package/harness/cli.ts:33: *   pi-messenger-swarm spawn stop <id>
x/package/harness/cli.ts:49:import { execSync, spawn as spawnChild } from 'node:child_process';
x/package/harness/cli.ts:187:  // Inherited channel hint from subagent spawn (PI_MESSENGER_CHANNEL is set
x/package/harness/cli.ts:188:  // by spawn.ts when it launches a child pi process — this is the only env var
x/package/harness/cli.ts:222:  const child = spawnChild(cmd, args, {
x/package/harness/cli.ts:225:    detached: true,
x/package/harness/cli.ts:358:  pi-messenger-swarm spawn --role Researcher "Analyze X" [--persona "..."] [--task-id <id>] [--name <name>] [--agent-file <path>] [--objective "..."] [--context "..."]
x/package/harness/cli.ts:359:  pi-messenger-swarm spawn list
x/package/harness/cli.ts:360:  pi-messenger-swarm spawn history
x/package/harness/cli.ts:361:  pi-messenger-swarm spawn stop <id>
x/package/harness/cli.ts:418:    const { spawn } = await import('node:child_process');
x/package/harness/cli.ts:419:    spawn('tail', ['-f', LOG], { stdio: 'inherit' });
x/package/harness/cli.ts:644:    case 'spawn': {
x/package/harness/cli.ts:647:        await postAction(buildAction({ action: 'spawn.list' }));
x/package/harness/cli.ts:649:        await postAction(buildAction({ action: 'spawn.history' }));
x/package/harness/cli.ts:653:          process.stderr.write('Error: spawn stop requires an id.\n');
x/package/harness/cli.ts:656:        await postAction(buildAction({ action: 'spawn.stop', id }));
x/package/harness/cli.ts:658:        // spawn --role Role "mission text" [--persona "..."] [--task-id task-1] [--name name]
x/package/harness/cli.ts:669:          process.stderr.write('Error: spawn requires mission text or --agent-file.\n');
x/package/harness/cli.ts:674:            action: 'spawn',
x/package/handlers/coordination/messaging.ts:3:import { findSpawnedAgentByName } from '../../swarm/spawn.js';
x/package/handlers/coordination/messaging.ts:48:  // Check if targeting a completed/failed/stopped spawned agent
x/package/handlers/coordination/messaging.ts:49:  let spawnWarning = '';
x/package/handlers/coordination/messaging.ts:52:    const spawnedAgent = findSpawnedAgentByName(cwd, sessionId, to);
x/package/handlers/coordination/messaging.ts:53:    if (spawnedAgent && spawnedAgent.status !== 'running') {
x/package/handlers/coordination/messaging.ts:55:        spawnedAgent.status === 'completed' ? '✅' : spawnedAgent.status === 'failed' ? '❌' : '🛑';
x/package/handlers/coordination/messaging.ts:56:      spawnWarning = `\n\n⚠ Warning: ${to} is a spawned agent that has already ${spawnedAgent.status} ${statusEmoji}. The message will be logged to the feed, but the agent process is no longer active.`;
x/package/handlers/coordination/messaging.ts:57:      if (spawnedAgent.status === 'completed') {
x/package/handlers/coordination/messaging.ts:58:        spawnWarning += `\n   If you need to continue the work, consider spawning a new agent.`;
x/package/handlers/coordination/messaging.ts:59:      } else if (spawnedAgent.status === 'failed') {
x/package/handlers/coordination/messaging.ts:60:        spawnWarning += `\n   The agent failed with errors. Review the task and consider respawning.`;
x/package/handlers/coordination/messaging.ts:83:  text += spawnWarning;
x/package/handlers/coordination/messaging.ts:89:    warning: spawnWarning ? 'target_agent_completed' : undefined,
x/package/extension/status.ts:9:import { getRunningSpawnCount } from '../swarm/spawn.js';
x/package/extension/status.ts:118:    const spawnStr = workerCount > 0 ? theme.fg('dim', ` 🔨${workerCount}`) : '';
x/package/extension/status.ts:122:      `msg: ${nameStr}${countStr}${unreadStr}${activityStr}${taskStr}${spawnStr}`
x/package/extension/shutdown.ts:24:    const { listSpawnedHistory } = await import('../swarm/spawn.js');
x/package/extension/shutdown.ts:25:    const spawnedAgents = listSpawnedHistory(cwd, sessionId);
x/package/extension/shutdown.ts:26:    const spawnedNames = new Set(spawnedAgents.map((s) => s.name));
x/package/extension/shutdown.ts:48:    // Unclaim tasks held by spawned agents
x/package/extension/shutdown.ts:49:    const spawnedClaimedTasks = allTasks.filter(
x/package/extension/shutdown.ts:50:      (t) => t.status === 'in_progress' && t.claimed_by && spawnedNames.has(t.claimed_by)
x/package/extension/shutdown.ts:52:    for (const task of spawnedClaimedTasks) {
x/package/extension/harness.ts:9:import { spawn as spawnChild, type ChildProcess } from 'node:child_process';
x/package/extension/harness.ts:100:      harnessProcess = spawnChild('npx', ['tsx', cliPath, '--start'], {
x/package/extension/harness.ts:103:        detached: true,
x/package/overlay/render-status.ts:173:    lines.push(theme.fg('dim', 'No spawned agents in this session.'));
x/package/overlay/render-status.ts:174:    lines.push(theme.fg('dim', 'spawn: pi-messenger-swarm spawn --role Researcher "..."'));
x/package/overlay/render-layout.ts:51:  spawned: SpawnedAgent[];
x/package/overlay/render-layout.ts:74:    spawned,
x/package/overlay/render-layout.ts:93:      viewState.mainView === 'swarm' ? Math.max(2, spawned.length) : Math.max(2, tasks.length);
x/package/overlay/render-layout.ts:103:        viewState.mainView === 'swarm' ? Math.max(2, spawned.length) : Math.max(2, tasks.length)
x/package/overlay/render-layout.ts:166:    mainLines = renderSwarmList(theme, spawned, sectionW, adjustedMainHeight, viewState);
x/package/overlay/render-detail.ts:138:  lines.push(theme.fg('dim', 'No swarm tasks yet — create one or spawn a specialist.'));
x/package/overlay/render-detail.ts:142:  lines.push(theme.fg('dim', 'spawn: pi-messenger-swarm spawn --role Researcher "Analyze issue"'));
x/package/overlay/input.ts:4:import { listSpawned, listSpawnedHistory } from '../swarm/spawn.js';
x/package/overlay/input.ts:249:  const spawned = listSpawnedHistory(cwd, getEffectiveSessionId(cwd, state));
x/package/overlay/input.ts:251:  const swarmAgent = spawned[viewState.selectedSwarmIndex];
x/package/overlay/input.ts:256:        navigateSwarm(viewState, 1, spawned.length);
x/package/overlay/input.ts:271:        navigateSwarm(viewState, -1, spawned.length);
x/package/overlay/input.ts:288:      navigateSwarm(viewState, -1, spawned.length);
x/package/overlay/input.ts:301:      navigateSwarm(viewState, 1, spawned.length);
x/package/overlay/input.ts:327:      viewState.selectedSwarmIndex = Math.max(0, spawned.length - 1);
x/package/overlay/input.ts:359:        navigateSwarm(viewState, -1, spawned.length);
x/package/overlay/input.ts:371:        navigateSwarm(viewState, 1, spawned.length);
x/package/overlay/component.ts:40:import { listSpawnedHistory } from '../swarm/spawn.js';
x/package/overlay/component.ts:336:    const spawned = listSpawnedHistory(this.cwd, sessionId);
x/package/overlay/component.ts:350:    if (spawned.length === 0) {
x/package/overlay/component.ts:358:        Math.min(this.viewState.selectedSwarmIndex, spawned.length - 1)
x/package/overlay/component.ts:363:    const selectedSwarmAgent = spawned[this.viewState.selectedSwarmIndex] ?? null;

===== taskplane package files =====
x/package/bin/taskplane.mjs:30:import { execSync, execFileSync, spawn } from "node:child_process";
x/package/bin/taskplane.mjs:817:	if (explicit.worktreePrefix) orchestratorCore.worktreePrefix = vars.worktree_prefix;
x/package/bin/taskplane.mjs:819:	if (explicit.spawnMode) orchestratorCore.spawnMode = vars.spawn_mode;
x/package/bin/taskplane.mjs:850:/** Auto-commit task files to git so they're available in orchestrator worktrees. */
x/package/bin/taskplane.mjs:878:		console.log(`  ${c.dim}(orchestrator worktrees require committed files)${c.reset}`);
x/package/bin/taskplane.mjs:1332:	// For workspace mode (prefix=".taskplane/"), scan .taskplane/.pi/ and .taskplane/.worktrees/
x/package/bin/taskplane.mjs:1333:	// For repo mode (no prefix), scan .pi/ and .worktrees/
x/package/bin/taskplane.mjs:1335:		? [`${prefix}.pi/`, `${prefix}.worktrees/`]
x/package/bin/taskplane.mjs:1336:		: [".pi/", ".worktrees/"];
x/package/bin/taskplane.mjs:1872:		vars.spawn_mode = "subprocess";
x/package/bin/taskplane.mjs:1981:		// Per spec: standard .pi/ patterns + .worktrees/ in config repo root
x/package/bin/taskplane.mjs:2082:	vars.spawn_mode = "subprocess";
x/package/bin/taskplane.mjs:2179:	// Auto-commit task files to git so they're available in worktrees
x/package/bin/taskplane.mjs:2201:		worktree_prefix: `${slug}-wt`,
x/package/bin/taskplane.mjs:2236:		worktree_prefix: `${slug}-wt`,
x/package/bin/taskplane.mjs:2947:			const scanDirs = [`${prefix}.pi/`, `${prefix}.worktrees/`];
x/package/bin/taskplane.mjs:3005:		const scanDirs = [".pi/", ".worktrees/"];
x/package/bin/taskplane.mjs:3115:	const child = spawn("node", [DASHBOARD_SERVER, ...serverArgs], {
x/package/bin/rpc-wrapper.mjs:30:import { spawn } from "node:child_process";
x/package/bin/rpc-wrapper.mjs:449: * @param {string|null} errorOverride - Override error message (e.g., spawn error)
x/package/bin/rpc-wrapper.mjs:515: * @param {object} proc - The spawned pi process (must have writable stdin)
x/package/bin/rpc-wrapper.mjs:752:// ── Build pi spawn args ──────────────────────────────────────────────
x/package/bin/rpc-wrapper.mjs:772:// ── System prompt: file-based passthrough to avoid command line limits ────
x/package/bin/rpc-wrapper.mjs:798:const proc = spawn("pi", piArgs, {
x/package/bin/rpc-wrapper.mjs:1014:// Fallback handler: spawn error (e.g., pi binary not found)
x/package/bin/rpc-wrapper.mjs:1016:	writeExitSummary(state, null, null, `spawn error: ${err.message}`, startTime);
x/package/bin/gitignore-patterns.mjs:29:	".worktrees/",
x/package/extensions/task-orchestrator.ts:2: * Task Orchestrator — Parallel task execution with git worktrees
x/package/extensions/reviewer-extension.ts:37:		// without error (fallback fresh-spawn mode).
x/package/extensions/taskplane/worktree.ts:3: * @module orch/worktree
x/package/extensions/taskplane/worktree.ts:33: * Resolve the base directory where worktrees are created, based on config.
x/package/extensions/taskplane/worktree.ts:35: * Two modes (from `worktree_location` config):
x/package/extensions/taskplane/worktree.ts:36: *   "sibling"      → resolve(repoRoot, "..")   — worktrees sit next to the repo
x/package/extensions/taskplane/worktree.ts:37: *   "subdirectory"  → resolve(repoRoot, ".worktrees") — worktrees inside the repo (gitignored)
x/package/extensions/taskplane/worktree.ts:39: * The returned path is the parent directory; individual worktree dirs are
x/package/extensions/taskplane/worktree.ts:43: * @param config   - Orchestrator config (reads `worktree_location`)
x/package/extensions/taskplane/worktree.ts:49:	const location = config.orchestrator.worktree_location;
x/package/extensions/taskplane/worktree.ts:54:	return resolve(repoRoot, ".worktrees");
x/package/extensions/taskplane/worktree.ts:63: * This is the directory that holds all lane worktrees and the merge
x/package/extensions/taskplane/worktree.ts:64: * worktree for a single batch.
x/package/extensions/taskplane/worktree.ts:76: * All worktrees for a single batch (lanes + merge) live inside this container.
x/package/extensions/taskplane/worktree.ts:79: * Uses `resolveWorktreeBasePath()` to respect `worktree_location` config
x/package/extensions/taskplane/worktree.ts:102: * Generate worktree path based on config's worktree_location setting.
x/package/extensions/taskplane/worktree.ts:106: *   Subdirectory mode: .worktrees/{opId}-{batchId}/lane-{N}
x/package/extensions/taskplane/worktree.ts:112: * preserving `worktree_location` semantics (sibling vs subdirectory).
x/package/extensions/taskplane/worktree.ts:144: * Generate the merge worktree path inside a batch container.
x/package/extensions/taskplane/worktree.ts:149: * path resolution (respects `worktree_location` setting). This ensures
x/package/extensions/taskplane/worktree.ts:150: * the merge worktree is co-located with lane worktrees in the same
x/package/extensions/taskplane/worktree.ts:188: * Used after per-worktree removals in `removeAllWorktrees()` and
x/package/extensions/taskplane/worktree.ts:190: * all worktrees inside it have been removed.
x/package/extensions/taskplane/worktree.ts:214: * Parse `git worktree list --porcelain` output into structured entries.
x/package/extensions/taskplane/worktree.ts:216: * Porcelain output format (one block per worktree, separated by blank lines):
x/package/extensions/taskplane/worktree.ts:217: *   worktree /absolute/path
x/package/extensions/taskplane/worktree.ts:220: *   [detached]
x/package/extensions/taskplane/worktree.ts:227:	branch: string | null; // null if detached HEAD
x/package/extensions/taskplane/worktree.ts:232:	const result = runGit(["worktree", "list", "--porcelain"], cwd);
x/package/extensions/taskplane/worktree.ts:248:			if (line.startsWith("worktree ")) {
x/package/extensions/taskplane/worktree.ts:249:				path = line.slice("worktree ".length).trim();
x/package/extensions/taskplane/worktree.ts:294: * Check if a given path is already registered as a git worktree.
x/package/extensions/taskplane/worktree.ts:295: * Uses `git worktree list --porcelain` for reliable detection.
x/package/extensions/taskplane/worktree.ts:313: * Create a new git worktree for a lane.
x/package/extensions/taskplane/worktree.ts:315: * Executes `git worktree add -b <branch> <path> <baseBranch>` from the
x/package/extensions/taskplane/worktree.ts:317: * and checks it out in the worktree directory.
x/package/extensions/taskplane/worktree.ts:321: * 2. Checks target path is not already a registered worktree
x/package/extensions/taskplane/worktree.ts:322: * 3. Checks target path is not a non-empty non-worktree directory
x/package/extensions/taskplane/worktree.ts:326: * - Correct branch is checked out in the worktree
x/package/extensions/taskplane/worktree.ts:337:	const worktreePath = generateWorktreePath(prefix, laneNumber, repoRoot, opId, config, batchId);
x/package/extensions/taskplane/worktree.ts:353:	// ── Pre-check 2: Check if path is already a registered worktree
x/package/extensions/taskplane/worktree.ts:354:	if (isRegisteredWorktree(worktreePath, repoRoot)) {
x/package/extensions/taskplane/worktree.ts:357:			`Path "${worktreePath}" is already registered as a git worktree. ` +
x/package/extensions/taskplane/worktree.ts:358:			`Remove it first: git worktree remove "${worktreePath}"`,
x/package/extensions/taskplane/worktree.ts:362:	// ── Pre-check 3: Check if path exists and is non-empty (non-worktree dir)
x/package/extensions/taskplane/worktree.ts:363:	if (existsSync(worktreePath)) {
x/package/extensions/taskplane/worktree.ts:365:			const entries = readdirSync(worktreePath);
x/package/extensions/taskplane/worktree.ts:369:					`Path "${worktreePath}" exists and is not empty. ` +
x/package/extensions/taskplane/worktree.ts:370:					`It is not a registered git worktree. Remove or rename it before creating a worktree here.`,
x/package/extensions/taskplane/worktree.ts:378:				`Path "${worktreePath}" exists but cannot be read as a directory.`,
x/package/extensions/taskplane/worktree.ts:392:			`This may indicate a stale worktree from a previous batch. ` +
x/package/extensions/taskplane/worktree.ts:400:	const containerDir = resolve(worktreePath, "..");
x/package/extensions/taskplane/worktree.ts:403:	// ── Create worktree ──────────────────────────────────────────

===== pi-subagents package files =====
x/package/worktree.ts:1:import { spawnSync } from "node:child_process";
x/package/worktree.ts:8:	worktrees: WorktreeInfo[];
x/package/worktree.ts:56:	worktreePath: string;
x/package/worktree.ts:84:	const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8" });
x/package/worktree.ts:108:		throw new Error("worktree isolation requires a clean git working tree. Commit or stash changes first.");
x/package/worktree.ts:144:	return `worktree isolation uses the shared cwd (${sharedCwd}); task ${conflict.index + 1} (${conflict.agent}) sets cwd to ${conflict.cwd}. Remove task-level cwd overrides or disable worktree.`;
x/package/worktree.ts:156:	return path.join(os.tmpdir(), `pi-worktree-${runId}-${index}`);
x/package/worktree.ts:162:		throw new Error("worktree isolation requires a git repository");
x/package/worktree.ts:173:	const worktreePath = buildWorktreePath(runId, index);
x/package/worktree.ts:174:	return cwdRelative ? path.join(worktreePath, cwdRelative) : worktreePath;
x/package/worktree.ts:177:function linkNodeModulesIfPresent(toplevel: string, worktreePath: string): boolean {
x/package/worktree.ts:179:	const nodeModulesLinkPath = path.join(worktreePath, "node_modules");
x/package/worktree.ts:193:		throw new Error("worktree setup hook timeout must be an integer greater than 0");
x/package/worktree.ts:205:		throw new Error("worktree setup hook path cannot be empty");
x/package/worktree.ts:215:		throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
x/package/worktree.ts:219:		throw new Error(`worktree setup hook not found: ${resolvedPath}`);
x/package/worktree.ts:222:		throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
x/package/worktree.ts:231:function normalizeSyntheticPath(worktreePath: string, rawPath: string): string {
x/package/worktree.ts:236:	const resolved = path.resolve(worktreePath, trimmed);
x/package/worktree.ts:237:	const relative = path.relative(worktreePath, resolved);
x/package/worktree.ts:239:		throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
x/package/worktree.ts:242:		throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
x/package/worktree.ts:247:function hasTrackedEntries(worktreePath: string, relativePath: string): boolean {
x/package/worktree.ts:248:	const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
x/package/worktree.ts:255:		throw new Error("worktree setup hook returned empty stdout; expected JSON object");
x/package/worktree.ts:262:		throw new Error(`worktree setup hook returned invalid JSON: ${message}`);
x/package/worktree.ts:265:		throw new Error("worktree setup hook stdout must be a JSON object");
x/package/worktree.ts:274:	const result = spawnSync(hook.hookPath, [], {
x/package/worktree.ts:275:		cwd: input.worktreePath,
x/package/worktree.ts:285:			throw new Error(`worktree setup hook timed out after ${hook.timeoutMs}ms`);
x/package/worktree.ts:287:		throw new Error(`worktree setup hook failed: ${result.error.message}`);
x/package/worktree.ts:292:		throw new Error(`worktree setup hook failed with exit code ${result.status}: ${details}`);
x/package/worktree.ts:298:		throw new Error("worktree setup hook output field 'syntheticPaths' must be an array of relative paths");
x/package/worktree.ts:304:			throw new Error("worktree setup hook output field 'syntheticPaths' must contain only strings");
x/package/worktree.ts:306:		const normalizedPath = normalizeSyntheticPath(input.worktreePath, candidate);
x/package/worktree.ts:307:		if (hasTrackedEntries(input.worktreePath, normalizedPath)) {
x/package/worktree.ts:308:			throw new Error(`worktree setup hook cannot mark tracked paths as synthetic: ${normalizedPath}`);
x/package/worktree.ts:325:	const worktreePath = buildWorktreePath(runId, index);
x/package/worktree.ts:326:	const add = runGit(toplevel, ["worktree", "add", worktreePath, "-b", branch, "HEAD"]);
x/package/worktree.ts:328:		const message = add.stderr.trim() || add.stdout.trim() || `failed to create worktree ${worktreePath}`;
x/package/worktree.ts:332:	const agentCwd = cwdRelative ? path.join(worktreePath, cwdRelative) : worktreePath;
x/package/worktree.ts:334:		const nodeModulesLinked = linkNodeModulesIfPresent(toplevel, worktreePath);
x/package/worktree.ts:341:				worktreePath,
x/package/worktree.ts:353:			path: worktreePath,
x/package/worktree.ts:361:		try { runGitChecked(toplevel, ["worktree", "remove", "--force", worktreePath]); } catch {
x/package/worktree.ts:371:function removeSyntheticPath(worktree: WorktreeInfo, syntheticPath: string): void {
x/package/worktree.ts:372:	const resolved = path.resolve(worktree.path, syntheticPath);
x/package/worktree.ts:373:	const relative = path.relative(worktree.path, resolved);
x/package/worktree.ts:398:function removeSyntheticPathsBeforeDiff(worktree: WorktreeInfo): void {
x/package/worktree.ts:399:	if (worktree.syntheticPaths.length === 0) return;
x/package/worktree.ts:401:	for (const syntheticPath of worktree.syntheticPaths) {
x/package/worktree.ts:404:		removeSyntheticPath(worktree, syntheticPath);
x/package/worktree.ts:443:	worktree: WorktreeInfo,
x/package/worktree.ts:447:	removeSyntheticPathsBeforeDiff(worktree);
x/package/worktree.ts:448:	runGitChecked(worktree.path, ["add", "-A"]);
x/package/worktree.ts:449:	const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
x/package/worktree.ts:450:	const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
x/package/worktree.ts:451:	const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
x/package/worktree.ts:455:		return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
x/package/worktree.ts:460:		index: worktree.index,
x/package/worktree.ts:462:		branch: worktree.branch,
x/package/worktree.ts:479:function cleanupSingleWorktree(repoCwd: string, worktree: WorktreeInfo): void {
x/package/worktree.ts:480:	try { runGitChecked(repoCwd, ["worktree", "remove", "--force", worktree.path]); } catch {
x/package/worktree.ts:483:	try { runGitChecked(repoCwd, ["branch", "-D", worktree.branch]); } catch {
x/package/worktree.ts:495:	const worktrees: WorktreeInfo[] = [];
x/package/worktree.ts:499:			worktrees.push(createSingleWorktree(
x/package/worktree.ts:512:			worktrees,
x/package/worktree.ts:520:		worktrees,
x/package/worktree.ts:534:	for (let index = 0; index < setup.worktrees.length; index++) {
x/package/worktree.ts:535:		const worktree = setup.worktrees[index]!;
x/package/worktree.ts:539:			diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
x/package/worktree.ts:543:			diffs.push(emptyDiff(index, agent, worktree.branch, patchPath));
x/package/worktree.ts:551:	for (let index = setup.worktrees.length - 1; index >= 0; index--) {
x/package/worktree.ts:552:		cleanupSingleWorktree(setup.cwd, setup.worktrees[index]!);
x/package/worktree.ts:554:	try { runGitChecked(setup.cwd, ["worktree", "prune"]); } catch {
x/package/types.ts:72:export type SubagentResultStatus = "completed" | "failed" | "paused" | "detached";
x/package/types.ts:110:	status: "pending" | "running" | "completed" | "failed" | "detached";
x/package/types.ts:154:	detached?: boolean;
x/package/types.ts:155:	detachedReason?: string;
x/package/types.ts:400:	worktreeSetupHook?: string;

===== @ifi/pi-extension-subagents package files =====
x/package/subagent-runner.ts:1:import { spawn, spawnSync } from "node:child_process";
x/package/subagent-runner.ts:8:import { getPiSpawnCommand } from "./pi-spawn.js";
x/package/subagent-runner.ts:108:		const spawnEnv = { ...process.env, ...env, ...getSubagentDepthEnv() };
x/package/subagent-runner.ts:109:		const spawnSpec = getPiSpawnCommand(args, piPackageRoot ? { piPackageRoot } : undefined);
x/package/subagent-runner.ts:110:		const child = spawn(spawnSpec.command, spawnSpec.args, { cwd, env: spawnEnv, stdio: ["ignore", "pipe", "pipe"] });
x/package/subagent-runner.ts:133:			resolve({ exitCode: 1, stderr: stderr || `Process spawn error: ${err.message}`, stdout });
x/package/subagent-runner.ts:173:		const auth = spawnSync("gh", ["auth", "status"], { encoding: "utf8" });
x/package/subagent-runner.ts:182:		const result = spawnSync("gh", ["gist", "create", htmlPath], { encoding: "utf8" });
x/package/schemas.ts:124:	async: Type.Optional(Type.Boolean({ description: "Run in background (default: false, or per config)" })),
x/package/render.ts:36: * causing background color bleed in the TUI. This implementation tracks active
x/package/render.ts:81:				// Re-apply all active styles before ellipsis to preserve background/colors
x/package/index.ts:510:					// User requested background execution
x/package/index.ts:720:					// User requested background execution
x/package/index.ts:1015:								pi.sendUserMessage(asyncResult.content[0]?.text || "(launched in background)");
x/package/execution.ts:5:import { spawn } from "node:child_process";
x/package/execution.ts:23:import { getPiSpawnCommand } from "./pi-spawn.js";
x/package/execution.ts:120:	// Pi's own skill discovery so the spawned process doesn't inject the full
x/package/execution.ts:195:	const spawnEnv = { ...process.env, ...getSubagentDepthEnv() };
x/package/execution.ts:198:		spawnEnv.MCP_DIRECT_TOOLS = mcpDirect.join(",");
x/package/execution.ts:200:		spawnEnv.MCP_DIRECT_TOOLS = "__none__";
x/package/execution.ts:205:		const spawnSpec = getPiSpawnCommand(args);
x/package/execution.ts:206:		const proc = spawn(spawnSpec.command, spawnSpec.args, {
x/package/execution.ts:208:			env: spawnEnv,
x/package/chain-execution.ts:215:		// User requested background execution - return early so caller can dispatch to async
x/package/chain-execution.ts:235:				content: [{ text: "Launching in background...", type: "text" }],
x/package/chain-clarify.ts:52:	/** User requested background/async execution */
x/package/chain-clarify.ts:100:	/** Run in background (async) mode */
x/package/chain-clarify.ts:591:		// 'b' to toggle background/async execution (all modes)
x/package/async-execution.ts:5:import { spawn } from "node:child_process";
x/package/async-execution.ts:18:import { resolvePiPackageRoot } from "./pi-spawn.js";
x/package/async-execution.ts:100:function spawnRunner(cfg: object, suffix: string, cwd: string): number | undefined {
x/package/async-execution.ts:110:	const proc = spawn("node", [jitiCliPath, runner, cfgPath], {
x/package/async-execution.ts:112:		detached: true,
x/package/async-execution.ts:208:	const pid = spawnRunner(
x/package/async-execution.ts:285:	const pid = spawnRunner(
x/package/README.md:199:Add `--bg` at the end of any slash command to run in the background:
x/package/README.md:314:- **Async Widget**: Lightweight TUI widget shows background run progress
x/package/README.md:325:All modes support background/async execution. For programmatic async, use `clarify: false, async: true`. For interactive async, use `clarify: true` and press `b` in the TUI to toggle background mode before running. Chains with parallel steps (`{ parallel: [...] }`) run concurrently with configurable `concurrency` and `failFast` options.
x/package/README.md:343:- `Enter` - Run (foreground) or launch in background if `b` is toggled on
x/package/README.md:350:- `b` - Toggle background/async execution (all modes) — shows `[b]g:ON` when enabled
x/package/README.md:411:{ agent: "scout", task: "...", skill: "tmux, safe-bash" }
x/package/README.md:486:// Async chain with parallel step (runs in background)
x/package/README.md:817:Subagents can themselves call the `subagent` tool, which risks unbounded recursive spawning (slow, expensive, hard to observe). A depth guard prevents this.
x/package/README.md:869:├── async-execution.ts            # Async/background execution support
x/package/README.md:877:├── subagent-runner.ts            # Async runner (detached process)
x/package/README.md:879:├── pi-spawn.ts                   # Cross-platform pi CLI spawning

===== @a5c-ai/babysitter-pi package files =====
x/package/bin/uninstall.cjs:6:const { spawnSync } = require('child_process');
x/package/bin/uninstall.cjs:32:  const result = spawnSync('pi', workspace ? ['remove', '-l', packageSpec] : ['remove', packageSpec], {
x/package/bin/install.cjs:6:const { spawnSync } = require('child_process');
x/package/bin/install.cjs:30:  const result = spawnSync(command, args, {
x/package/bin/cli.cjs:5:const { spawnSync } = require('child_process');
x/package/bin/cli.cjs:43:  const result = spawnSync(process.execPath, [scriptPath, ...args], {

===== pi-btw package files =====
x/package/extensions/btw.ts:1007:  background: "userMessageBg" | "toolPendingBg" | "customMessageBg",
x/package/extensions/btw.ts:1010:  return theme.bg(background, theme.fg(foreground, theme.bold(` ${label} `)));


**Evaluating tmux options**

I'm focusing on finding the best answer regarding tmux, which is crucial for maintaining session persistence. I noticed that most findings don't mention tmux explicitly, except for side-agents and a few specific fields. The user insists on tmux due to disconnection issues with the orchestrator. I need to be careful: detached child processes may survive if SSH disconnects. However, tmux is essential for explicit persistence, so I think side-agents might be the best match. I might also need to search package documentation for any mentions of tmux.


Updated #2 (pending → completed)

Find Pi extension search capability

Check whether local tools can search Pi extensions/packages, and if not, manually locate such a tool/command in installed Pi code/docs, then use it to search for relevant extensions.

Compare Pi orchestration extensions for tmux-resilient workflow

Inspect top candidate Pi packages for multi-agent orchestration with tmux/server persistence, then rank best fit for a single orchestrator whose children keep running after SSH disconnect.


Best match for your **tmux-required** constraint: **still `pi-side-agents`**.

Why:
- It is the only one in this set with **explicit tmux-first architecture**.
- Child agents run in **their own tmux windows + git worktrees**.
- If your SSH client drops, tmux on server stays alive, so workers keep going.
- It already exposes parent control tools: `agent-start`, `agent-check`, `agent-wait-any`, `agent-send`.

What I found on the others:

### 1. `pi-crew`
Strong contender on orchestration.
Pros:
- durable run state
- detached background runner
- optional worktrees
- explicit async/background model
- better “team/workflow” abstraction than `pi-side-agents`

But:
- I found **detached subprocess/background execution**, not tmux-managed workers.
- Good if “survive parent chat” matters.
- Not proven for your exact “one SSH-backed TUI dies, workers remain inspectable in tmux” requirement.

### 2. `taskplane`
Strong for planned batch execution.
Pros:
- serious worktree orchestration
- supervisor/worker/reviewer/merger roles
- persistent task packets
- dashboard
- likely better than `pi-side-agents` for structured task DAGs

But:
- again, **subprocess/worktree orchestration**, not tmux-first worker lifecycle.
- Better for batch engine. Worse for your explicit tmux persistence ask.

### 3. `pi-messenger-swarm`
Interesting but different.
Pros:
- detached spawned agents
- file-backed coordination
- no daemon required
- durable channels

But:
- more swarm/messaging mesh than tmux orchestration.
- Could survive disconnects.
- Not a tmux worker manager.

### 4. `pi-subagents` / `@ifi/pi-extension-subagents`
Good delegation layer.
Pros:
- async background runs
- worktree support
- recursion guard
- chains/parallel modes

But:
- uses **detached runners**, not tmux windows as primary execution surface.
- Great for subagent delegation inside a session.
- Not best fit for “I want child workers sitting in server tmux I can inspect later”.

## Bottom line

If **tmux is non-negotiable**, none of the alternatives beat `pi-side-agents` from what I found.

Best fit ranking for your exact goal:

1. **pi-side-agents** — best tmux persistence, direct fit
2. **pi-crew** — best non-tmux orchestrator candidate
3. **taskplane** — best structured batch/workflow engine
4. **pi-messenger-swarm** — best if you want durable mesh/chat/task channels
5. **pi-subagents / @ifi** — best lightweight delegation layer, but not tmux-first

## Real recommendation

For your target workflow, best architecture is likely:

- **top-level human-controlled Pi in tmux**
- uses **`pi-side-agents`** for project orchestrators
- project orchestrators may use:
  - `agent-start` again for tmux workers, or
  - `subagent`-style delegation inside their own session for short scoped jobs

So:
- **outer durability/persistence** via `pi-side-agents`
- **inner delegation ergonomics** via subagent tooling if needed

That gives you:
- SSH-loss resilience
- inspectable live workers in tmux
- hierarchical orchestration
- better control than pure subprocess swarms

If you want, next I can do one of these:
1. design the **best hybrid stack** for your workflow,
2. inspect whether `pi-crew` can be made tmux-backed with small changes,
3. compare `pi-side-agents` vs `pi-crew` specifically for **project orchestrator spawning workers**.


what is agent-start? didnt understand reco


`agent-start` is the **tool API** exposed by `pi-side-agents`.

Meaning:
- `/agent ...` = human-facing slash command
- `agent-start` = programmatic version an agent can call

It starts a new side agent:
- in a **new tmux window**
- in its own **git worktree**
- on its own **topic branch**

Related tools:
- `agent-start` → spawn child
- `agent-check` → inspect status/log tail
- `agent-wait-any` → wait until one child yields/finishes/fails
- `agent-send` → send follow-up prompt into child tmux pane

Why I recommended it:
- your requirement is **workers must survive SSH disconnect**
- `pi-side-agents` does that with tmux-backed children
- so your main orchestrator can spawn durable children with `agent-start`

Example shape:

- you run one main Pi in tmux
- main Pi calls `agent-start` for:
  - `project-a-orchestrator`
  - `project-b-orchestrator`
- each project orchestrator can also call `agent-start` again for:
  - `worker-fix-auth`
  - `worker-add-tests`

So recommendation was:
- use **`pi-side-agents` as the outer execution substrate**
- because it gives durable tmux-backed child agents
- then optionally use another subagent style inside if useful

Short version:
- **`agent-start` = “spawn a durable child Pi in tmux/worktree from inside an agent”**

If you want, I can draw the exact hierarchy for your intended workflow in 10 lines.


good but
1. pi TUI + tmux UX is bad. has some issues. not comfortable for human use
2. unless we can call agent-start with a session folder, project orchestrators will work awkwardly from outside project

the way i see it we can:
- make an rfc then pr on adding minimal support for our usecase to pi-side-agents (then either push for approval or fork)
- look for a client (web) we can host on the server and there open one session per project (not use a parent orchestrator for now)


**Considering options and contradictions**

I need to provide an answer with multiple options and maybe recommend both of them. I think it’s important to mention any contradictions that arise from the suggestions. It might also be a good idea to create some memory for the user later on if needed. I should consider asking a more focused question to clarify the decision. It seems I need to be direct in my approach!

Yes. Two real paths.

## Constraint recap

Your goal is not just “multi-agent”.
It is:

- durable server-side execution
- survives SSH drops
- human control from a **good UI**
- ideally one place to orchestrate across projects

And `pi-side-agents` currently misses that because:

1. **Pi TUI in tmux is weak for human supervision**
2. **project-root/session-root control looks too rigid**
   - if project orchestrators spawn from outside the project/session context, flow gets awkward

So your read is right.

---

## Path A — RFC + PR to `pi-side-agents`

Best if you want the underlying model to remain:

- parent orchestrator
- project orchestrators
- workers
- tmux durability

### Minimal features likely needed

1. **explicit child cwd / repo root**
   - spawn child in chosen project dir, not just current session repo

2. **explicit session dir or session namespace**
   - so each project orchestrator has its own durable session folder

3. **maybe explicit state root**
   - separate registry/runtime roots per project or per orchestration tree
   - avoids flat shared registry mess

4. **better non-tmux human control surface**
   - maybe not full replacement
   - just enough status/control via web/TUI overlay/API

5. **parent-child hierarchy metadata**
   - so nested orchestrators are visible as a tree, not flat list

### Why this path is good
- preserves the one real advantage: **tmux-backed durable workers**
- minimal change could unlock your exact workflow
- likely cleaner than rebuilding orchestration elsewhere

### Why this path is risky
- depends on upstream appetite
- if maintainer vision is “simple shallow side agents”, they may reject hierarchy/session-root expansion
- you may end up forking anyway

### My take
This is the **best technical fit** if you want the final system you described.

---

## Path B — use a web client on server, one project session each

This is the faster path.

Meaning:
- stop chasing one master orchestrator for now
- host a better client/UI on the server
- open one Pi session per project
- let each project run independently
- maybe later add orchestration between them

### Why this path is good
- solves your biggest current pain: **human UX**
- avoids tmux-heavy daily use
- lower design risk
- you can start using it now

### Why this path is limited
- no single top-level orchestrator yet
- cross-project coordination becomes manual
- durable workers may depend on how that web client manages processes/sessions

### What matters here
The client must:
- survive browser disconnects
- keep session/process alive server-side
- let you reopen existing sessions
- ideally expose multiple persistent tabs/sessions

If it is just a thin browser terminal to a live process, fine.
If it kills the session on disconnect, useless.

---

## Decision frame

If your priority is:

### “I want usable workflow this week”
Pick **Path B** first.

### “I want the right long-term architecture”
Pick **Path A**.

---

## Best practical strategy

Do both, sequenced:

### Phase 1
Find or host a **better server-side web client** for Pi sessions.
Use **one session per project**.
No top orchestrator yet.

### Phase 2
Draft a **small RFC** for `pi-side-agents` with only:
- `cwd`
- `sessionDir`
- optional `stateRoot`
- parent/child metadata

### Phase 3
If upstream says yes, PR it.
If not, fork.

This avoids blocking on upstream while still moving toward your real architecture.

---

## Important note

If you hate the tmux+Pi UX for human use, then building your whole workflow around a single human parent orchestrator **inside tmux** is already the wrong product shape.

So:
- **humans** should use web UI
- **workers** can still use tmux in background

That split is probably the right architecture.

---

## My recommendation

**Primary reco:**
- short term: **Path B**
- medium term: **Path A with minimal RFC**

Because:
- UX pain is your current blocker
- `pi-side-agents` is still the best worker substrate
- but not the best human control surface

---

## Suggested RFC scope

Keep it tiny:

1. `agent-start` accepts `cwd`
2. `agent-start` accepts `sessionDir`
3. `agent-start` accepts optional `stateRoot`
4. registry records `parentAgentId`
5. status views can group by parent

That is enough for:
- project-specific orchestrators
- nested trees
- per-project session isolation
- durable workers

Not enough to trigger a giant redesign.

---

If you want, next I can do one of these:

1. write the **RFC draft**
2. inspect `pi-side-agents` code and estimate how hard `cwd + sessionDir + parentAgentId` would be
3. search for the best **web client/UI** to host for Pi on this server


explore path b


**Exploring search options**

I think I need to search locally, maybe using the web, but I'm not completely sure. I wonder if I should tap into my memory skills for this task, although that might not be necessary. I could search npm for pi web clients and check local packages. Perhaps inspecting an existing opencode-web.log could help too. Still, I seem to be more focused on exploring path B generally, so I'll look into using npm search and local grep. Let's get started with the tools!

todo

Explore web client path for server-hosted Pi sessions

Find candidate web UIs/clients that can host persistent Pi sessions on the server, evaluate survival across browser disconnects, and recommend best path B options.


Created #3: Explore web client path for server-hosted Pi sessions (pending)

Find Pi extension search capability


bash

npm search --long --searchlimit=80 'pi web tui session terminal browser' 2>/dev/null | sed -n '1,180p'

npm search --long --searchlimit=80 keywords:pi-package 2>/dev/null | grep -Ei 'web|browser|dashboard|terminal|session|tui|overlay|ui|client' | sed -n '1,180p'

ls -1 /home/sebas/.npm-global/lib/node_modules 2>/dev/null | sed -n '1,120p' && printf '\n--- local grep ---\n' && grep -RIn "web\|browser\|dashboard\|session\|overlay\|terminal" /home/sebas/.npm-global/lib/node_modules/*/README* 2>/dev/null | head -200


@mariozechner/pi-tui
Terminal User Interface library with differential rendering for efficient text-based applications
Version 0.70.6 published 2026-04-28 by badlogic
Maintainers: badlogic mitsuhiko
Keywords: tui terminal ui text-editor differential-rendering typescript cli
https://npm.im/@mariozechner/pi-tui

@mariozechner/pi-coding-agent
Coding agent CLI with read, bash, edit, write tools and session management
Version 0.70.6 published 2026-04-28 by badlogic
Maintainers: badlogic mitsuhiko
Keywords: coding-agent ai llm cli tui agent
https://npm.im/@mariozechner/pi-coding-agent

openclaude-web
Local web UI for openclaude / Claude Code — claude.ai/code-style chat in your browser, runs on your phone or laptop. Spawns openclaude or claude as a subprocess and streams events over WebSocket.
Version 0.2.0 published 2026-04-28 by eizo__void
Maintainers: eizo__void
Keywords: openclaude claude-code claude web-ui chat-ui termux ai-agent local-ui
https://npm.im/openclaude-web

@oh-my-pi/pi-tui
Terminal User Interface library with differential rendering for efficient text-based applications
Version 14.5.6 published 2026-04-29 by can1357
Maintainers: can1357
Keywords: tui terminal ui text-editor differential-rendering typescript cli
https://npm.im/@oh-my-pi/pi-tui

pi-tau-mux-server
Standalone Tau server - aggregates multiple pi coding agent instances into one web UI with Tailscale support
Version 1.0.8 published 2026-03-26 by dwainm
Maintainers: dwainm
Keywords: pi pi-coding-agent tau mux aggregator web-ui tailscale
https://npm.im/pi-tau-mux-server

@f5xc-salesdemos/pi-tui
Terminal User Interface library with differential rendering for efficient text-based applications
Version 18.26.0 published 2026-04-29 by robinmordasiewicz
Maintainers: robinmordasiewicz
Keywords: tui terminal ui text-editor differential-rendering typescript cli
https://npm.im/@f5xc-salesdemos/pi-tui

@vincentkoc/qrcode-tui
Modern QR rendering helpers for Node.js CLIs and setup flows
Version 0.2.1 published 2026-04-13 by GitHub Actions
Maintainers: vincentkoc
Keywords: ascii cli png qr qrcode svg terminal tui utf8
https://npm.im/@vincentkoc/qrcode-tui

expo-auth-session
Expo module for browser-based authentication
Version 55.0.15 published 2026-04-21 by alanhughes
Maintainers: ide brentvatne evanbacon expoadmin exponent bycedric kudochien alanhughes tsapeta expo-bot philpl wschurman bbarthec
Keywords: react-native expo expo-auth-session auth oauth authentication auth-session
https://npm.im/expo-auth-session

acpx
Headless CLI client for the Agent Client Protocol (ACP) — talk to coding agents from the command line
Version 0.6.1 published 2026-04-25 by steipete
Maintainers: steipete osolmaz
Keywords: acp agent-client-protocol ai claude-code cli codex coding-agent
https://npm.im/acpx

pi-acp
ACP adapter for pi coding agent
Version 0.0.26 published 2026-04-18 by deepstereo
Maintainers: deepstereo
Keywords: acp agent-client-protocol pi-coding-agent adapter
https://npm.im/pi-acp

@kilocode/cli
The AI coding agent built for the terminal. Generate code from natural language, automate tasks, and run terminal commands -- powered by 500+ AI models.
Version 7.2.25 published 2026-04-27 by catrielmuller
Maintainers: marius-kilocode olelves helix_kilo scott-kilo johndotawesome storms42 alex-alecu-kilocode emilie_kilo catrielmuller rso
https://npm.im/@kilocode/cli

pi-markdown-preview
Rendered markdown + LaTeX preview for pi, with terminal, browser, and PDF output
Version 0.9.6 published 2026-03-29 by omacl
Maintainers: omacl
Keywords: pi-package
https://npm.im/pi-markdown-preview

pi-mermaid
🧜♀ Pi extension that renders Mermaid diagrams as ASCII in the TUI
Version 0.3.0 published 2026-02-23 by gurpartap
Maintainers: gurpartap
Keywords: pi pi-extension pi-package mermaid ascii tui
https://npm.im/pi-mermaid

@avadisabelle/ava-pi-tui
Terminal User Interface library with differential rendering for efficient text-based applications
Version 0.65.2 published 2026-04-27 by avadisabelle
Maintainers: avadisabelle
Keywords: tui terminal ui text-editor differential-rendering typescript cli
https://npm.im/@avadisabelle/ava-pi-tui

@bulletproof-sh/ctrl-daemon
WebSocket daemon for ctrl — watches Claude Code sessions and broadcasts agent state
Version 0.19.0 published 2026-04-02 by GitHub Actions
Maintainers: krashid
https://npm.im/@bulletproof-sh/ctrl-daemon

@noahsaso/pi-remote
Remote terminal access for pi via WebSocket and browser, with Tailscale integration
Version 0.3.1 published 2026-03-11 by noahsaso
Maintainers: noahsaso
Keywords: pi-package pi remote terminal pty websocket tailscale
https://npm.im/@noahsaso/pi-remote

mini-coder
A small, fast CLI coding agent
Version 0.5.14 published 2026-04-21 by xonecas
Maintainers: xonecas
https://npm.im/mini-coder

@lincoln504/pi-research
A set of web research tools (search, scrape, and others) and research agent system that uses them safely and efficiently.
Version 0.1.13 published 2026-04-20 by GitHub Actions
Maintainers: ldeen
Keywords: pi pi-package pi-extension research agent web-search scraping security stack-exchange
https://npm.im/@lincoln504/pi-research

mixpanel-browser
The official Mixpanel JavaScript browser client library
Version 2.78.0 published 2026-04-08 by jakub.grz
Maintainers: mixpanel-dev tdumitrescu mp_jthong rahul-mixpanel ketanmixpanel sonya-park-mp msiebert-mixpanel jakub.grz jaredcmixpanel
https://npm.im/mixpanel-browser

tty-table
Node cli table
Version 5.0.0 published 2025-11-09 by tecfu
Maintainers: tecfu
Keywords: table table in bash cli-table terminal table console table cli table console.table ascii table
https://npm.im/tty-table

@mariozechner/pi-ai
Unified LLM API with automatic model discovery and provider configuration
Version 0.70.6 published 2026-04-28 by badlogic
Maintainers: badlogic mitsuhiko
Keywords: ai llm openai anthropic gemini bedrock unified api
https://npm.im/@mariozechner/pi-ai

codex-session-tui
Prebuilt codex-session-tui binaries for macOS, Linux, and Windows.
Version 2.0.10 published 2026-03-20 by GitHub Actions
Maintainers: avikalpa
https://npm.im/codex-session-tui

tau-mirror
Web UI that mirrors your Pi terminal session in the browser
Version 1.0.7 published 2026-03-11 by deflating
Maintainers: deflating
Keywords: pi pi-package coding-agent web-ui mirror
https://npm.im/tau-mirror

appium-safari-driver
Appium driver for Safari browser
Version 4.1.12 published 2026-04-17 by GitHub Actions
Maintainers: jlipps nick.mokhnach kazucocoa imurchie dangraham
Keywords: appium safari
https://npm.im/appium-safari-driver

@stdlib/constants-float64-pi
π.
Version 0.2.3 published 2026-02-05 by stdlib-bot
Maintainers: stdlib-bot kgryte planeshifter rreusser
Keywords: stdlib stdmath constant const mathematics math pi ieee754 double dbl floating-point float64
https://npm.im/@stdlib/constants-float64-pi

whatsapp-pi
WhatsApp integration extension for Pi
Version 1.0.43 published 2026-04-29 by castelloes
Maintainers: castelloes
Keywords: pi pi-package pi-extension whatsapp baileys agent
https://npm.im/whatsapp-pi

@oh-my-pi/swarm-extension
Swarm orchestration extension for omp
Version 13.17.0 published 2026-03-30 by can1357


Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification
@ollama/pi-web-search
Web search and fetch tools for Pi agent - uses Ollama's web search and fetch APIs
Keywords: pi-package pi-extension web-search ollama
https://npm.im/@ollama/pi-web-search
pi-web-access
Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent
Keywords: pi-package pi pi-coding-agent extension web-search perplexity fetch scraping
https://npm.im/pi-web-access
Rendered markdown + LaTeX preview for pi, with terminal, browser, and PDF output
Keywords: pi-package pi-extension pi-coding-agent agent tui
@juicesharp/rpiv-ask-user-question
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
https://npm.im/@juicesharp/rpiv-ask-user-question
Persistent memory for pi — learns corrections, preferences, and patterns from sessions and injects them into future conversations.
Smart web_fetch with desktop-browser TLS impersonation and defuddle extraction.
Keywords: pi-package pi smart-fetch defuddle wreq-js content-extraction web-fetch tls-fingerprinting
@juicesharp/rpiv-todo
Pi extension: Claude-Code-parity todo tool + persistent overlay widget
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
https://npm.im/@juicesharp/rpiv-todo
@juicesharp/rpiv-pi
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
https://npm.im/@juicesharp/rpiv-pi
Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview
🧜♀ Pi extension that renders Mermaid diagrams as ASCII in the TUI
Keywords: pi pi-extension pi-package mermaid ascii tui
@juicesharp/rpiv-web-tools
Pi extension: web_search + web_fetch via the Brave Search API
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
Keywords: pi-package pi-extension rpiv web-search brave
https://npm.im/@juicesharp/rpiv-web-tools
@juicesharp/rpiv-btw
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
Keywords: pi-package pi-extension rpiv btw side-question overlay
https://npm.im/@juicesharp/rpiv-btw
@juicesharp/rpiv-advisor
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
https://npm.im/@juicesharp/rpiv-advisor
@tintinweb/pi-subagents
Version 0.6.3 published 2026-04-28 by tintinweb
Maintainers: tintinweb
https://npm.im/@tintinweb/pi-subagents
Test harness for pi extensions — in-process session testing, package install verification, and subprocess mocking
glimpseui
Native micro-UI for scripts and agents — cross-platform WebView windows with bidirectional JSON communication
Keywords: macos linux windows webview native ui webkit wkwebview webview2 gtk swift gui agent dialog prompt overlay pi-package
https://npm.im/glimpseui
Research-first CLI agent built on Pi and alphaXiv
@kaiserlich-dev/pi-session-search
Full-text search across pi sessions with FTS5 index and overlay UI
Keywords: pi-package pi-coding-agent search sessions fts5 sqlite
https://npm.im/@kaiserlich-dev/pi-session-search
Keywords: pi-package ai-agent multi-agent agent-teams agent-coordination autonomous-agents task-management team-collaboration agent-orchestration coding-assistant terminal tmux
Interactive ask_user tool for pi-coding-agent with searchable split-pane selection UI, multi-select, and freeform input
Run AI coding agents in pi TUI overlays with interactive, hands-free, and dispatch supervision
Keywords: pi-package pi pi-coding-agent extension interactive shell terminal tui subagent claude gemini codex
Pi Coding Agent extension with first-class MCP support — routes bash, read, grep, find, and ls through lean-ctx CLI, and exposes all 48 lean-ctx MCP tools (ctx_session, ctx_knowledge, ctx_semantic_search, ctx_impact, ctx_architecture, ctx_workflow, ctx_ga
@juicesharp/rpiv-args
Pi extension: skill-argument resolver — pre-emptively wraps /skill:<name> <args> via the input hook and performs opt-in $N/$ARGUMENTS substitution before Pi's built-in expansion
Version 1.0.6 published 2026-04-29 by juicesharp
Maintainers: juicesharp
https://npm.im/@juicesharp/rpiv-args
Version 0.1.2 published 2026-02-16 by tintinweb
Maintainers: tintinweb
Pi extension: multi-engine AI search (Perplexity, Bing Copilot, Google AI) via browser automation -- NO API KEYS needed. Extracts answers with sources, optional Gemini synthesis. Grounded AI answers from real browser interactions.
Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.
ChatGPT web-oracle extension for pi with isolated browser auth, async jobs, and project-context archives.
Full-featured subagent orchestration extension for pi, built on top of nicobailon/pi-subagents.
Compact tool call rendering, diff visualization, and output truncation extension for Pi coding agent. Hides, collapses, and truncates verbose tool output for cleaner TUI display.
Keywords: pi-package pi pi-extension pi-coding-agent pi-tui coding-agent tui tool-rendering tool-output diff-visualization output-truncation hide collapse truncate compact diff output-mode


@juicesharp
@malinamnam
@mariozechner
pi-btw
pi-session-switch
pi-side-agents
taskplane

--- local grep ---
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:5:`/btw` opens a real pi sub-session with coding-tool access, and it runs immediately even while the main agent is still busy.
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:7:![BTW overlay example](docs/btw-overlay.png)
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:12:- runs that side conversation as a real pi sub-session with `read` / `bash` / `edit` / `write` tool access
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:14:- supports `/btw:tangent` for a contextless side thread that does not inherit the current main-session conversation
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:16:- keeps the BTW overlay open while you switch focus back to the main editor with `Alt+/`
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:20:- optionally saves an individual BTW exchange as a visible session note with `--save`
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:69:- creates or reuses a real BTW sub-session instead of a one-off completion call
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:74:- with `--save`, also saves that single exchange as a visible session note
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:78:- `Alt+/` toggles focus between BTW and the main editor without closing the overlay
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:79:- `Ctrl+Alt+W` is a fallback focus toggle for terminals that do not deliver `Alt+/` as a usable shortcut
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:80:- `Esc` still dismisses BTW immediately while the overlay is focused
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:81:- BTW now opens top-centered so the main session remains visible underneath it
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:86:- starts a fresh thread that still inherits the current main-session context
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:93:- does not inherit the current main-session conversation
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:96:- with `--save`, also saves that single exchange as a visible session note
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:129:- changing `/btw:model` or `/btw:thinking` disposes the current BTW sub-session and applies the new settings on the next BTW prompt while preserving the hidden thread
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:133:### Real sub-session model
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:135:BTW is implemented as an actual pi sub-session with its own in-memory session state, transcript events, and tool surface.
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:137:- contextual `/btw` threads seed that sub-session from the current main-session branch while filtering out BTW-visible notes from the parent context
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:138:- `/btw:tangent` starts the same BTW UI in a contextless mode with no inherited main-session conversation
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:141:- the overlay transcript/status line is driven from sub-session events, so tool activity, streaming deltas, failures, and recovery are all visible without scraping rendered output
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:142:- handoff commands (`/btw:inject` and `/btw:summarize`) read from the BTW sub-session thread rather than maintaining a separate manual transcript model
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:146:Inside the BTW modal composer, slash handling is split at the BTW/session boundary:
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:149:- any other slash-prefixed input is routed through the BTW sub-session's normal `prompt()` path
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:150:- this means ordinary pi slash commands like `/help` are handled by the sub-session instead of being rejected by a modal-only fallback
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:151:- if the sub-session cannot handle a slash command, BTW surfaces the real sub-session failure through the transcript/status state instead of inventing an "unsupported slash input" warning
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:153:This keeps BTW-owned lifecycle commands explicit while giving the side conversation the same slash-command surface as the underlying sub-session.
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:159:BTW exchanges are persisted in the session as hidden custom entries so they:
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:164:- preserve the current BTW-only model and thinking overrides for that session history
/home/sebas/.npm-global/lib/node_modules/pi-btw/README.md:169:If you use `--save`, that one BTW exchange is also written as a visible custom message in the session transcript.
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:1:# Session Switch for Pi (`pi-session-switch`)
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:3:Switch between Pi sessions with `/switch-session`, or open the same picker after interactive startup with `pi --switch-session`, using the same layout and affordances as Pi's native `/resume` picker plus a live preview of the currently highlighted session beneath the picker.
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:5:![`/switch-session` demo](https://raw.githubusercontent.com/w-winter/dot314/main/assets/switch-session-demo.gif)
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:12:pi install npm:pi-session-switch
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:22:      "extensions": ["extensions/session-switch/index.ts"],
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:34:/switch-session
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:40:pi --switch-session
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:43:Opens the same picker after interactive startup, then relaunches Pi into the selected session.
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:45:This is an extension-only workaround for startup switching. Unlike native `pi --resume`, it does not provide missing-cwd recovery for sessions whose recorded cwd no longer exists, and it does not reuse Pi's normal in-process session-switch lifecycle or guarantee the same shutdown-hook cleanup semantics.
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:49:- `/switch-session` stays on Pi's native in-process session switch path
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:50:- `pi --switch-session` reuses the same picker UI, then relaunches Pi into the selected session
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:52:- Shows a live preview of the highlighted session below the picker
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:73:          args+=(--session "$2")
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:76:          args+=(--switch-session)
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:81:        args+=(--session "${1#--resume=}")
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:96:- `pi -r` / `pi --resume` to `pi --switch-session` (opens the picker)
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:97:- `pi -r <path>` / `pi --resume <path>` to `pi --session <path>` (opens that session directly)
/home/sebas/.npm-global/lib/node_modules/pi-session-switch/README.md:101:Derived from Damian Pedroza's [`pi-thread-switcher`](https://github.com/damianpdr/pi-thread-switcher).  This version has a few additions that I found helpful for readability: the session preview lives in a dedicated pane below the picker with syntax-highlighted Markdown rendering, and `Shift+PageUp` / `Shift+PageDown` are also offered for paging through the preview.  The picker itself uses Pi's native `SessionSelectorComponent`, matching the `/resume` interaction model.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:3:Multi-agent AI orchestration for coding with [pi](https://github.com/badlogic/pi-mono) — parallel task execution, mono- and poly-repo support, fresh-context worker loops, cross-model reviews, automated merges and a killer dashboard!
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:23:- A killer locally-run web-based dashboard so you can see everything that's going on
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:25:<img src="docs/images/orchrun-wave2of4-2lanes-withstatus.png" alt="image of taskplane dashboard" width="50%">
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:39:- **Web Dashboard** — Live browser-based monitoring via `taskplane dashboard`. SSE streaming, lane/task progress, reviewer activity, merge telemetry, batch history.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:102:### 3. Launch the dashboard (recommended)
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:104:In a separate terminal:
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:107:taskplane dashboard
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:110:Opens a live web dashboard at `http://localhost:8099` with real-time batch monitoring.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:118:Inside the pi session:
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:127:`/orch` with no arguments is the universal entry point — it detects your project state and activates the supervisor for guided interaction (onboarding, batch planning, health checks, or retrospective). The default scaffold includes two independent example tasks, so `/orch all` gives you an immediate orchestrator + dashboard experience.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:131:For a single task with full worktree isolation, dashboard, and reviews:
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:137:This uses the same orchestrator infrastructure as a full batch — isolated worktree, orch branch, supervisor, dashboard, inline reviews — but for just one task.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:141:When you're in pi, type /taskplane-settings. Near the top you'll see the 4 agents. By default, Taskplane installs with the agent models set to inherit whatever model your pi session is currently using. The best thing you can do is configure the reviewer agent to use a different model provider. If you're using Claude for coding, then consider using OpenAI for the reviews.
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:156:| `/orch-sessions` | List active worker sessions |
/home/sebas/.npm-global/lib/node_modules/taskplane/README.md:168:| `taskplane dashboard` | Launch the web dashboard |


**Inspecting candidate READMEs**

I need to inspect the READMEs of several candidates like pi-remote, tau-mirror, pi-tau-mux-server, pi-interactive-shell, and openclaude-web, among others. I’m looking for survival semantics here. To do this, I might utilize commands like `npm view readme/head` and perhaps use `grep` to filter the content efficiently. It feels like a bit of work, but finding the right information is essential!

for p in @noahsaso/pi-remote tau-mirror pi-tau-mux-server pi-interactive-shell openclaude-web; do echo '===== '"$p"' META ====='; npm view "$p" name version description repository.url homepage keywords 2>/dev/null; echo '----- README -----'; npm view "$p" readme 2>/dev/null | sed -n '1,180p'; echo; done

tmp=$(mktemp -d); cd "$tmp" || exit 1; for p in @noahsaso/pi-remote tau-mirror pi-tau-mux-server pi-interactive-shell openclaude-web; do echo '===== '"$p"' SIGNALS ====='; npm pack "$p" >/dev/null 2>&1 || { echo pack-failed; continue; }; tgz=$(ls -1 *.tgz | tail -1); mkdir x; tar -xzf "$tgz" -C x; grep -RIn 'tmux\|pty\|websocket\|disconnect\|resume\|persist\|session\|detached\|spawn\|browser\|reconnect\|tailscale\|terminal' x/package 2>/dev/null | head -120; rm -rf x *.tgz; echo; done